152217195SDaniel Vetter /* 252217195SDaniel Vetter * Copyright (c) 2016 Intel Corporation 352217195SDaniel Vetter * 452217195SDaniel Vetter * Permission to use, copy, modify, distribute, and sell this software and its 552217195SDaniel Vetter * documentation for any purpose is hereby granted without fee, provided that 652217195SDaniel Vetter * the above copyright notice appear in all copies and that both that copyright 752217195SDaniel Vetter * notice and this permission notice appear in supporting documentation, and 852217195SDaniel Vetter * that the name of the copyright holders not be used in advertising or 952217195SDaniel Vetter * publicity pertaining to distribution of the software without specific, 1052217195SDaniel Vetter * written prior permission. The copyright holders make no representations 1152217195SDaniel Vetter * about the suitability of this software for any purpose. It is provided "as 1252217195SDaniel Vetter * is" without express or implied warranty. 1352217195SDaniel Vetter * 1452217195SDaniel Vetter * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 1552217195SDaniel Vetter * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 1652217195SDaniel Vetter * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 1752217195SDaniel Vetter * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 1852217195SDaniel Vetter * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 1952217195SDaniel Vetter * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 2052217195SDaniel Vetter * OF THIS SOFTWARE. 2152217195SDaniel Vetter */ 2252217195SDaniel Vetter 2352217195SDaniel Vetter #include <drm/drmP.h> 2452217195SDaniel Vetter #include <drm/drm_connector.h> 2552217195SDaniel Vetter #include <drm/drm_edid.h> 269338203cSLaurent Pinchart #include <drm/drm_encoder.h> 2752217195SDaniel Vetter 2852217195SDaniel Vetter #include "drm_crtc_internal.h" 2952217195SDaniel Vetter #include "drm_internal.h" 3052217195SDaniel Vetter 31ae2a6da8SDaniel Vetter /** 32ae2a6da8SDaniel Vetter * DOC: overview 33ae2a6da8SDaniel Vetter * 34ae2a6da8SDaniel Vetter * In DRM connectors are the general abstraction for display sinks, and include 35ae2a6da8SDaniel Vetter * als fixed panels or anything else that can display pixels in some form. As 36ae2a6da8SDaniel Vetter * opposed to all other KMS objects representing hardware (like CRTC, encoder or 37ae2a6da8SDaniel Vetter * plane abstractions) connectors can be hotplugged and unplugged at runtime. 38ad093607SThierry Reding * Hence they are reference-counted using drm_connector_get() and 39ad093607SThierry Reding * drm_connector_put(). 40ae2a6da8SDaniel Vetter * 41d574528aSDaniel Vetter * KMS driver must create, initialize, register and attach at a &struct 42d574528aSDaniel Vetter * drm_connector for each such sink. The instance is created as other KMS 43aec97460SDaniel Vetter * objects and initialized by setting the following fields. The connector is 44aec97460SDaniel Vetter * initialized with a call to drm_connector_init() with a pointer to the 45aec97460SDaniel Vetter * &struct drm_connector_funcs and a connector type, and then exposed to 46aec97460SDaniel Vetter * userspace with a call to drm_connector_register(). 47ae2a6da8SDaniel Vetter * 48ae2a6da8SDaniel Vetter * Connectors must be attached to an encoder to be used. For devices that map 49ae2a6da8SDaniel Vetter * connectors to encoders 1:1, the connector should be attached at 50ae2a6da8SDaniel Vetter * initialization time with a call to drm_mode_connector_attach_encoder(). The 51d574528aSDaniel Vetter * driver must also set the &drm_connector.encoder field to point to the 52ae2a6da8SDaniel Vetter * attached encoder. 53ae2a6da8SDaniel Vetter * 54ae2a6da8SDaniel Vetter * For connectors which are not fixed (like built-in panels) the driver needs to 55ae2a6da8SDaniel Vetter * support hotplug notifications. The simplest way to do that is by using the 56ae2a6da8SDaniel Vetter * probe helpers, see drm_kms_helper_poll_init() for connectors which don't have 57ae2a6da8SDaniel Vetter * hardware support for hotplug interrupts. Connectors with hardware hotplug 58ae2a6da8SDaniel Vetter * support can instead use e.g. drm_helper_hpd_irq_event(). 59ae2a6da8SDaniel Vetter */ 60ae2a6da8SDaniel Vetter 6152217195SDaniel Vetter struct drm_conn_prop_enum_list { 6252217195SDaniel Vetter int type; 6352217195SDaniel Vetter const char *name; 6452217195SDaniel Vetter struct ida ida; 6552217195SDaniel Vetter }; 6652217195SDaniel Vetter 6752217195SDaniel Vetter /* 6852217195SDaniel Vetter * Connector and encoder types. 6952217195SDaniel Vetter */ 7052217195SDaniel Vetter static struct drm_conn_prop_enum_list drm_connector_enum_list[] = { 7152217195SDaniel Vetter { DRM_MODE_CONNECTOR_Unknown, "Unknown" }, 7252217195SDaniel Vetter { DRM_MODE_CONNECTOR_VGA, "VGA" }, 7352217195SDaniel Vetter { DRM_MODE_CONNECTOR_DVII, "DVI-I" }, 7452217195SDaniel Vetter { DRM_MODE_CONNECTOR_DVID, "DVI-D" }, 7552217195SDaniel Vetter { DRM_MODE_CONNECTOR_DVIA, "DVI-A" }, 7652217195SDaniel Vetter { DRM_MODE_CONNECTOR_Composite, "Composite" }, 7752217195SDaniel Vetter { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO" }, 7852217195SDaniel Vetter { DRM_MODE_CONNECTOR_LVDS, "LVDS" }, 7952217195SDaniel Vetter { DRM_MODE_CONNECTOR_Component, "Component" }, 8052217195SDaniel Vetter { DRM_MODE_CONNECTOR_9PinDIN, "DIN" }, 8152217195SDaniel Vetter { DRM_MODE_CONNECTOR_DisplayPort, "DP" }, 8252217195SDaniel Vetter { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" }, 8352217195SDaniel Vetter { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" }, 8452217195SDaniel Vetter { DRM_MODE_CONNECTOR_TV, "TV" }, 8552217195SDaniel Vetter { DRM_MODE_CONNECTOR_eDP, "eDP" }, 8652217195SDaniel Vetter { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" }, 8752217195SDaniel Vetter { DRM_MODE_CONNECTOR_DSI, "DSI" }, 8852217195SDaniel Vetter { DRM_MODE_CONNECTOR_DPI, "DPI" }, 8952217195SDaniel Vetter }; 9052217195SDaniel Vetter 9152217195SDaniel Vetter void drm_connector_ida_init(void) 9252217195SDaniel Vetter { 9352217195SDaniel Vetter int i; 9452217195SDaniel Vetter 9552217195SDaniel Vetter for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++) 9652217195SDaniel Vetter ida_init(&drm_connector_enum_list[i].ida); 9752217195SDaniel Vetter } 9852217195SDaniel Vetter 9952217195SDaniel Vetter void drm_connector_ida_destroy(void) 10052217195SDaniel Vetter { 10152217195SDaniel Vetter int i; 10252217195SDaniel Vetter 10352217195SDaniel Vetter for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++) 10452217195SDaniel Vetter ida_destroy(&drm_connector_enum_list[i].ida); 10552217195SDaniel Vetter } 10652217195SDaniel Vetter 10752217195SDaniel Vetter /** 10852217195SDaniel Vetter * drm_connector_get_cmdline_mode - reads the user's cmdline mode 10952217195SDaniel Vetter * @connector: connector to quwery 11052217195SDaniel Vetter * 111ae2a6da8SDaniel Vetter * The kernel supports per-connector configuration of its consoles through 11252217195SDaniel Vetter * use of the video= parameter. This function parses that option and 11352217195SDaniel Vetter * extracts the user's specified mode (or enable/disable status) for a 11452217195SDaniel Vetter * particular connector. This is typically only used during the early fbdev 11552217195SDaniel Vetter * setup. 11652217195SDaniel Vetter */ 11752217195SDaniel Vetter static void drm_connector_get_cmdline_mode(struct drm_connector *connector) 11852217195SDaniel Vetter { 11952217195SDaniel Vetter struct drm_cmdline_mode *mode = &connector->cmdline_mode; 12052217195SDaniel Vetter char *option = NULL; 12152217195SDaniel Vetter 12252217195SDaniel Vetter if (fb_get_options(connector->name, &option)) 12352217195SDaniel Vetter return; 12452217195SDaniel Vetter 12552217195SDaniel Vetter if (!drm_mode_parse_command_line_for_connector(option, 12652217195SDaniel Vetter connector, 12752217195SDaniel Vetter mode)) 12852217195SDaniel Vetter return; 12952217195SDaniel Vetter 13052217195SDaniel Vetter if (mode->force) { 1316140cf20SJani Nikula DRM_INFO("forcing %s connector %s\n", connector->name, 1326140cf20SJani Nikula drm_get_connector_force_name(mode->force)); 13352217195SDaniel Vetter connector->force = mode->force; 13452217195SDaniel Vetter } 13552217195SDaniel Vetter 13652217195SDaniel Vetter DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n", 13752217195SDaniel Vetter connector->name, 13852217195SDaniel Vetter mode->xres, mode->yres, 13952217195SDaniel Vetter mode->refresh_specified ? mode->refresh : 60, 14052217195SDaniel Vetter mode->rb ? " reduced blanking" : "", 14152217195SDaniel Vetter mode->margins ? " with margins" : "", 14252217195SDaniel Vetter mode->interlace ? " interlaced" : ""); 14352217195SDaniel Vetter } 14452217195SDaniel Vetter 14552217195SDaniel Vetter static void drm_connector_free(struct kref *kref) 14652217195SDaniel Vetter { 14752217195SDaniel Vetter struct drm_connector *connector = 14852217195SDaniel Vetter container_of(kref, struct drm_connector, base.refcount); 14952217195SDaniel Vetter struct drm_device *dev = connector->dev; 15052217195SDaniel Vetter 15152217195SDaniel Vetter drm_mode_object_unregister(dev, &connector->base); 15252217195SDaniel Vetter connector->funcs->destroy(connector); 15352217195SDaniel Vetter } 15452217195SDaniel Vetter 155*a703c550SDaniel Vetter static void drm_connector_free_work_fn(struct work_struct *work) 156*a703c550SDaniel Vetter { 157*a703c550SDaniel Vetter struct drm_connector *connector = 158*a703c550SDaniel Vetter container_of(work, struct drm_connector, free_work); 159*a703c550SDaniel Vetter struct drm_device *dev = connector->dev; 160*a703c550SDaniel Vetter 161*a703c550SDaniel Vetter drm_mode_object_unregister(dev, &connector->base); 162*a703c550SDaniel Vetter connector->funcs->destroy(connector); 163*a703c550SDaniel Vetter } 164*a703c550SDaniel Vetter 16552217195SDaniel Vetter /** 16652217195SDaniel Vetter * drm_connector_init - Init a preallocated connector 16752217195SDaniel Vetter * @dev: DRM device 16852217195SDaniel Vetter * @connector: the connector to init 16952217195SDaniel Vetter * @funcs: callbacks for this connector 17052217195SDaniel Vetter * @connector_type: user visible type of the connector 17152217195SDaniel Vetter * 17252217195SDaniel Vetter * Initialises a preallocated connector. Connectors should be 17352217195SDaniel Vetter * subclassed as part of driver connector objects. 17452217195SDaniel Vetter * 17552217195SDaniel Vetter * Returns: 17652217195SDaniel Vetter * Zero on success, error code on failure. 17752217195SDaniel Vetter */ 17852217195SDaniel Vetter int drm_connector_init(struct drm_device *dev, 17952217195SDaniel Vetter struct drm_connector *connector, 18052217195SDaniel Vetter const struct drm_connector_funcs *funcs, 18152217195SDaniel Vetter int connector_type) 18252217195SDaniel Vetter { 18352217195SDaniel Vetter struct drm_mode_config *config = &dev->mode_config; 18452217195SDaniel Vetter int ret; 18552217195SDaniel Vetter struct ida *connector_ida = 18652217195SDaniel Vetter &drm_connector_enum_list[connector_type].ida; 18752217195SDaniel Vetter 1882135ea7aSThierry Reding ret = __drm_mode_object_add(dev, &connector->base, 18952217195SDaniel Vetter DRM_MODE_OBJECT_CONNECTOR, 19052217195SDaniel Vetter false, drm_connector_free); 19152217195SDaniel Vetter if (ret) 192613051daSDaniel Vetter return ret; 19352217195SDaniel Vetter 194*a703c550SDaniel Vetter INIT_WORK(&connector->free_work, drm_connector_free_work_fn); 195*a703c550SDaniel Vetter 19652217195SDaniel Vetter connector->base.properties = &connector->properties; 19752217195SDaniel Vetter connector->dev = dev; 19852217195SDaniel Vetter connector->funcs = funcs; 19952217195SDaniel Vetter 20052217195SDaniel Vetter ret = ida_simple_get(&config->connector_ida, 0, 0, GFP_KERNEL); 20152217195SDaniel Vetter if (ret < 0) 20252217195SDaniel Vetter goto out_put; 20352217195SDaniel Vetter connector->index = ret; 20452217195SDaniel Vetter ret = 0; 20552217195SDaniel Vetter 20652217195SDaniel Vetter connector->connector_type = connector_type; 20752217195SDaniel Vetter connector->connector_type_id = 20852217195SDaniel Vetter ida_simple_get(connector_ida, 1, 0, GFP_KERNEL); 20952217195SDaniel Vetter if (connector->connector_type_id < 0) { 21052217195SDaniel Vetter ret = connector->connector_type_id; 21152217195SDaniel Vetter goto out_put_id; 21252217195SDaniel Vetter } 21352217195SDaniel Vetter connector->name = 21452217195SDaniel Vetter kasprintf(GFP_KERNEL, "%s-%d", 21552217195SDaniel Vetter drm_connector_enum_list[connector_type].name, 21652217195SDaniel Vetter connector->connector_type_id); 21752217195SDaniel Vetter if (!connector->name) { 21852217195SDaniel Vetter ret = -ENOMEM; 21952217195SDaniel Vetter goto out_put_type_id; 22052217195SDaniel Vetter } 22152217195SDaniel Vetter 22252217195SDaniel Vetter INIT_LIST_HEAD(&connector->probed_modes); 22352217195SDaniel Vetter INIT_LIST_HEAD(&connector->modes); 224e73ab00eSDaniel Vetter mutex_init(&connector->mutex); 22552217195SDaniel Vetter connector->edid_blob_ptr = NULL; 22652217195SDaniel Vetter connector->status = connector_status_unknown; 22752217195SDaniel Vetter 22852217195SDaniel Vetter drm_connector_get_cmdline_mode(connector); 22952217195SDaniel Vetter 23052217195SDaniel Vetter /* We should add connectors at the end to avoid upsetting the connector 23152217195SDaniel Vetter * index too much. */ 232613051daSDaniel Vetter spin_lock_irq(&config->connector_list_lock); 23352217195SDaniel Vetter list_add_tail(&connector->head, &config->connector_list); 23452217195SDaniel Vetter config->num_connector++; 235613051daSDaniel Vetter spin_unlock_irq(&config->connector_list_lock); 23652217195SDaniel Vetter 23752217195SDaniel Vetter if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL) 23852217195SDaniel Vetter drm_object_attach_property(&connector->base, 23952217195SDaniel Vetter config->edid_property, 24052217195SDaniel Vetter 0); 24152217195SDaniel Vetter 24252217195SDaniel Vetter drm_object_attach_property(&connector->base, 24352217195SDaniel Vetter config->dpms_property, 0); 24452217195SDaniel Vetter 24540ee6fbeSManasi Navare drm_object_attach_property(&connector->base, 24640ee6fbeSManasi Navare config->link_status_property, 24740ee6fbeSManasi Navare 0); 24840ee6fbeSManasi Navare 24966660d4cSDave Airlie drm_object_attach_property(&connector->base, 25066660d4cSDave Airlie config->non_desktop_property, 25166660d4cSDave Airlie 0); 25266660d4cSDave Airlie 25352217195SDaniel Vetter if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { 25452217195SDaniel Vetter drm_object_attach_property(&connector->base, config->prop_crtc_id, 0); 25552217195SDaniel Vetter } 25652217195SDaniel Vetter 25752217195SDaniel Vetter connector->debugfs_entry = NULL; 25852217195SDaniel Vetter out_put_type_id: 25952217195SDaniel Vetter if (ret) 260587680c1SChristophe JAILLET ida_simple_remove(connector_ida, connector->connector_type_id); 26152217195SDaniel Vetter out_put_id: 26252217195SDaniel Vetter if (ret) 263587680c1SChristophe JAILLET ida_simple_remove(&config->connector_ida, connector->index); 26452217195SDaniel Vetter out_put: 26552217195SDaniel Vetter if (ret) 26652217195SDaniel Vetter drm_mode_object_unregister(dev, &connector->base); 26752217195SDaniel Vetter 26852217195SDaniel Vetter return ret; 26952217195SDaniel Vetter } 27052217195SDaniel Vetter EXPORT_SYMBOL(drm_connector_init); 27152217195SDaniel Vetter 27252217195SDaniel Vetter /** 27352217195SDaniel Vetter * drm_mode_connector_attach_encoder - attach a connector to an encoder 27452217195SDaniel Vetter * @connector: connector to attach 27552217195SDaniel Vetter * @encoder: encoder to attach @connector to 27652217195SDaniel Vetter * 27752217195SDaniel Vetter * This function links up a connector to an encoder. Note that the routing 27852217195SDaniel Vetter * restrictions between encoders and crtcs are exposed to userspace through the 27952217195SDaniel Vetter * possible_clones and possible_crtcs bitmasks. 28052217195SDaniel Vetter * 28152217195SDaniel Vetter * Returns: 28252217195SDaniel Vetter * Zero on success, negative errno on failure. 28352217195SDaniel Vetter */ 28452217195SDaniel Vetter int drm_mode_connector_attach_encoder(struct drm_connector *connector, 28552217195SDaniel Vetter struct drm_encoder *encoder) 28652217195SDaniel Vetter { 28752217195SDaniel Vetter int i; 28852217195SDaniel Vetter 28952217195SDaniel Vetter /* 29052217195SDaniel Vetter * In the past, drivers have attempted to model the static association 29152217195SDaniel Vetter * of connector to encoder in simple connector/encoder devices using a 29252217195SDaniel Vetter * direct assignment of connector->encoder = encoder. This connection 29352217195SDaniel Vetter * is a logical one and the responsibility of the core, so drivers are 29452217195SDaniel Vetter * expected not to mess with this. 29552217195SDaniel Vetter * 29652217195SDaniel Vetter * Note that the error return should've been enough here, but a large 29752217195SDaniel Vetter * majority of drivers ignores the return value, so add in a big WARN 29852217195SDaniel Vetter * to get people's attention. 29952217195SDaniel Vetter */ 30052217195SDaniel Vetter if (WARN_ON(connector->encoder)) 30152217195SDaniel Vetter return -EINVAL; 30252217195SDaniel Vetter 30352217195SDaniel Vetter for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { 30452217195SDaniel Vetter if (connector->encoder_ids[i] == 0) { 30552217195SDaniel Vetter connector->encoder_ids[i] = encoder->base.id; 30652217195SDaniel Vetter return 0; 30752217195SDaniel Vetter } 30852217195SDaniel Vetter } 30952217195SDaniel Vetter return -ENOMEM; 31052217195SDaniel Vetter } 31152217195SDaniel Vetter EXPORT_SYMBOL(drm_mode_connector_attach_encoder); 31252217195SDaniel Vetter 31352217195SDaniel Vetter static void drm_mode_remove(struct drm_connector *connector, 31452217195SDaniel Vetter struct drm_display_mode *mode) 31552217195SDaniel Vetter { 31652217195SDaniel Vetter list_del(&mode->head); 31752217195SDaniel Vetter drm_mode_destroy(connector->dev, mode); 31852217195SDaniel Vetter } 31952217195SDaniel Vetter 32052217195SDaniel Vetter /** 32152217195SDaniel Vetter * drm_connector_cleanup - cleans up an initialised connector 32252217195SDaniel Vetter * @connector: connector to cleanup 32352217195SDaniel Vetter * 32452217195SDaniel Vetter * Cleans up the connector but doesn't free the object. 32552217195SDaniel Vetter */ 32652217195SDaniel Vetter void drm_connector_cleanup(struct drm_connector *connector) 32752217195SDaniel Vetter { 32852217195SDaniel Vetter struct drm_device *dev = connector->dev; 32952217195SDaniel Vetter struct drm_display_mode *mode, *t; 33052217195SDaniel Vetter 33152217195SDaniel Vetter /* The connector should have been removed from userspace long before 33252217195SDaniel Vetter * it is finally destroyed. 33352217195SDaniel Vetter */ 33452217195SDaniel Vetter if (WARN_ON(connector->registered)) 33552217195SDaniel Vetter drm_connector_unregister(connector); 33652217195SDaniel Vetter 33752217195SDaniel Vetter if (connector->tile_group) { 33852217195SDaniel Vetter drm_mode_put_tile_group(dev, connector->tile_group); 33952217195SDaniel Vetter connector->tile_group = NULL; 34052217195SDaniel Vetter } 34152217195SDaniel Vetter 34252217195SDaniel Vetter list_for_each_entry_safe(mode, t, &connector->probed_modes, head) 34352217195SDaniel Vetter drm_mode_remove(connector, mode); 34452217195SDaniel Vetter 34552217195SDaniel Vetter list_for_each_entry_safe(mode, t, &connector->modes, head) 34652217195SDaniel Vetter drm_mode_remove(connector, mode); 34752217195SDaniel Vetter 3489a47dba1SChristophe JAILLET ida_simple_remove(&drm_connector_enum_list[connector->connector_type].ida, 34952217195SDaniel Vetter connector->connector_type_id); 35052217195SDaniel Vetter 3519a47dba1SChristophe JAILLET ida_simple_remove(&dev->mode_config.connector_ida, 35252217195SDaniel Vetter connector->index); 35352217195SDaniel Vetter 35452217195SDaniel Vetter kfree(connector->display_info.bus_formats); 35552217195SDaniel Vetter drm_mode_object_unregister(dev, &connector->base); 35652217195SDaniel Vetter kfree(connector->name); 35752217195SDaniel Vetter connector->name = NULL; 358613051daSDaniel Vetter spin_lock_irq(&dev->mode_config.connector_list_lock); 35952217195SDaniel Vetter list_del(&connector->head); 36052217195SDaniel Vetter dev->mode_config.num_connector--; 361613051daSDaniel Vetter spin_unlock_irq(&dev->mode_config.connector_list_lock); 36252217195SDaniel Vetter 36352217195SDaniel Vetter WARN_ON(connector->state && !connector->funcs->atomic_destroy_state); 36452217195SDaniel Vetter if (connector->state && connector->funcs->atomic_destroy_state) 36552217195SDaniel Vetter connector->funcs->atomic_destroy_state(connector, 36652217195SDaniel Vetter connector->state); 36752217195SDaniel Vetter 368e73ab00eSDaniel Vetter mutex_destroy(&connector->mutex); 369e73ab00eSDaniel Vetter 37052217195SDaniel Vetter memset(connector, 0, sizeof(*connector)); 37152217195SDaniel Vetter } 37252217195SDaniel Vetter EXPORT_SYMBOL(drm_connector_cleanup); 37352217195SDaniel Vetter 37452217195SDaniel Vetter /** 37552217195SDaniel Vetter * drm_connector_register - register a connector 37652217195SDaniel Vetter * @connector: the connector to register 37752217195SDaniel Vetter * 37852217195SDaniel Vetter * Register userspace interfaces for a connector 37952217195SDaniel Vetter * 38052217195SDaniel Vetter * Returns: 38152217195SDaniel Vetter * Zero on success, error code on failure. 38252217195SDaniel Vetter */ 38352217195SDaniel Vetter int drm_connector_register(struct drm_connector *connector) 38452217195SDaniel Vetter { 385e73ab00eSDaniel Vetter int ret = 0; 38652217195SDaniel Vetter 387e6e7b48bSDaniel Vetter if (!connector->dev->registered) 388e6e7b48bSDaniel Vetter return 0; 389e6e7b48bSDaniel Vetter 390e73ab00eSDaniel Vetter mutex_lock(&connector->mutex); 39152217195SDaniel Vetter if (connector->registered) 392e73ab00eSDaniel Vetter goto unlock; 39352217195SDaniel Vetter 39452217195SDaniel Vetter ret = drm_sysfs_connector_add(connector); 39552217195SDaniel Vetter if (ret) 396e73ab00eSDaniel Vetter goto unlock; 39752217195SDaniel Vetter 39852217195SDaniel Vetter ret = drm_debugfs_connector_add(connector); 39952217195SDaniel Vetter if (ret) { 40052217195SDaniel Vetter goto err_sysfs; 40152217195SDaniel Vetter } 40252217195SDaniel Vetter 40352217195SDaniel Vetter if (connector->funcs->late_register) { 40452217195SDaniel Vetter ret = connector->funcs->late_register(connector); 40552217195SDaniel Vetter if (ret) 40652217195SDaniel Vetter goto err_debugfs; 40752217195SDaniel Vetter } 40852217195SDaniel Vetter 40952217195SDaniel Vetter drm_mode_object_register(connector->dev, &connector->base); 41052217195SDaniel Vetter 41152217195SDaniel Vetter connector->registered = true; 412e73ab00eSDaniel Vetter goto unlock; 41352217195SDaniel Vetter 41452217195SDaniel Vetter err_debugfs: 41552217195SDaniel Vetter drm_debugfs_connector_remove(connector); 41652217195SDaniel Vetter err_sysfs: 41752217195SDaniel Vetter drm_sysfs_connector_remove(connector); 418e73ab00eSDaniel Vetter unlock: 419e73ab00eSDaniel Vetter mutex_unlock(&connector->mutex); 42052217195SDaniel Vetter return ret; 42152217195SDaniel Vetter } 42252217195SDaniel Vetter EXPORT_SYMBOL(drm_connector_register); 42352217195SDaniel Vetter 42452217195SDaniel Vetter /** 42552217195SDaniel Vetter * drm_connector_unregister - unregister a connector 42652217195SDaniel Vetter * @connector: the connector to unregister 42752217195SDaniel Vetter * 42852217195SDaniel Vetter * Unregister userspace interfaces for a connector 42952217195SDaniel Vetter */ 43052217195SDaniel Vetter void drm_connector_unregister(struct drm_connector *connector) 43152217195SDaniel Vetter { 432e73ab00eSDaniel Vetter mutex_lock(&connector->mutex); 433e73ab00eSDaniel Vetter if (!connector->registered) { 434e73ab00eSDaniel Vetter mutex_unlock(&connector->mutex); 43552217195SDaniel Vetter return; 436e73ab00eSDaniel Vetter } 43752217195SDaniel Vetter 43852217195SDaniel Vetter if (connector->funcs->early_unregister) 43952217195SDaniel Vetter connector->funcs->early_unregister(connector); 44052217195SDaniel Vetter 44152217195SDaniel Vetter drm_sysfs_connector_remove(connector); 44252217195SDaniel Vetter drm_debugfs_connector_remove(connector); 44352217195SDaniel Vetter 44452217195SDaniel Vetter connector->registered = false; 445e73ab00eSDaniel Vetter mutex_unlock(&connector->mutex); 44652217195SDaniel Vetter } 44752217195SDaniel Vetter EXPORT_SYMBOL(drm_connector_unregister); 44852217195SDaniel Vetter 44952217195SDaniel Vetter void drm_connector_unregister_all(struct drm_device *dev) 45052217195SDaniel Vetter { 45152217195SDaniel Vetter struct drm_connector *connector; 452613051daSDaniel Vetter struct drm_connector_list_iter conn_iter; 45352217195SDaniel Vetter 454b982dab1SThierry Reding drm_connector_list_iter_begin(dev, &conn_iter); 455613051daSDaniel Vetter drm_for_each_connector_iter(connector, &conn_iter) 45652217195SDaniel Vetter drm_connector_unregister(connector); 457b982dab1SThierry Reding drm_connector_list_iter_end(&conn_iter); 45852217195SDaniel Vetter } 45952217195SDaniel Vetter 46052217195SDaniel Vetter int drm_connector_register_all(struct drm_device *dev) 46152217195SDaniel Vetter { 46252217195SDaniel Vetter struct drm_connector *connector; 463613051daSDaniel Vetter struct drm_connector_list_iter conn_iter; 464613051daSDaniel Vetter int ret = 0; 46552217195SDaniel Vetter 466b982dab1SThierry Reding drm_connector_list_iter_begin(dev, &conn_iter); 467613051daSDaniel Vetter drm_for_each_connector_iter(connector, &conn_iter) { 46852217195SDaniel Vetter ret = drm_connector_register(connector); 46952217195SDaniel Vetter if (ret) 470613051daSDaniel Vetter break; 47152217195SDaniel Vetter } 472b982dab1SThierry Reding drm_connector_list_iter_end(&conn_iter); 47352217195SDaniel Vetter 474613051daSDaniel Vetter if (ret) 47552217195SDaniel Vetter drm_connector_unregister_all(dev); 47652217195SDaniel Vetter return ret; 47752217195SDaniel Vetter } 47852217195SDaniel Vetter 47952217195SDaniel Vetter /** 48052217195SDaniel Vetter * drm_get_connector_status_name - return a string for connector status 48152217195SDaniel Vetter * @status: connector status to compute name of 48252217195SDaniel Vetter * 48352217195SDaniel Vetter * In contrast to the other drm_get_*_name functions this one here returns a 48452217195SDaniel Vetter * const pointer and hence is threadsafe. 48552217195SDaniel Vetter */ 48652217195SDaniel Vetter const char *drm_get_connector_status_name(enum drm_connector_status status) 48752217195SDaniel Vetter { 48852217195SDaniel Vetter if (status == connector_status_connected) 48952217195SDaniel Vetter return "connected"; 49052217195SDaniel Vetter else if (status == connector_status_disconnected) 49152217195SDaniel Vetter return "disconnected"; 49252217195SDaniel Vetter else 49352217195SDaniel Vetter return "unknown"; 49452217195SDaniel Vetter } 49552217195SDaniel Vetter EXPORT_SYMBOL(drm_get_connector_status_name); 49652217195SDaniel Vetter 4976140cf20SJani Nikula /** 4986140cf20SJani Nikula * drm_get_connector_force_name - return a string for connector force 4996140cf20SJani Nikula * @force: connector force to get name of 5006140cf20SJani Nikula * 5016140cf20SJani Nikula * Returns: const pointer to name. 5026140cf20SJani Nikula */ 5036140cf20SJani Nikula const char *drm_get_connector_force_name(enum drm_connector_force force) 5046140cf20SJani Nikula { 5056140cf20SJani Nikula switch (force) { 5066140cf20SJani Nikula case DRM_FORCE_UNSPECIFIED: 5076140cf20SJani Nikula return "unspecified"; 5086140cf20SJani Nikula case DRM_FORCE_OFF: 5096140cf20SJani Nikula return "off"; 5106140cf20SJani Nikula case DRM_FORCE_ON: 5116140cf20SJani Nikula return "on"; 5126140cf20SJani Nikula case DRM_FORCE_ON_DIGITAL: 5136140cf20SJani Nikula return "digital"; 5146140cf20SJani Nikula default: 5156140cf20SJani Nikula return "unknown"; 5166140cf20SJani Nikula } 5176140cf20SJani Nikula } 5186140cf20SJani Nikula 519613051daSDaniel Vetter #ifdef CONFIG_LOCKDEP 520613051daSDaniel Vetter static struct lockdep_map connector_list_iter_dep_map = { 521613051daSDaniel Vetter .name = "drm_connector_list_iter" 522613051daSDaniel Vetter }; 523613051daSDaniel Vetter #endif 524613051daSDaniel Vetter 525613051daSDaniel Vetter /** 526b982dab1SThierry Reding * drm_connector_list_iter_begin - initialize a connector_list iterator 527613051daSDaniel Vetter * @dev: DRM device 528613051daSDaniel Vetter * @iter: connector_list iterator 529613051daSDaniel Vetter * 530d574528aSDaniel Vetter * Sets @iter up to walk the &drm_mode_config.connector_list of @dev. @iter 531b982dab1SThierry Reding * must always be cleaned up again by calling drm_connector_list_iter_end(). 532613051daSDaniel Vetter * Iteration itself happens using drm_connector_list_iter_next() or 533613051daSDaniel Vetter * drm_for_each_connector_iter(). 534613051daSDaniel Vetter */ 535b982dab1SThierry Reding void drm_connector_list_iter_begin(struct drm_device *dev, 536613051daSDaniel Vetter struct drm_connector_list_iter *iter) 537613051daSDaniel Vetter { 538613051daSDaniel Vetter iter->dev = dev; 539613051daSDaniel Vetter iter->conn = NULL; 540613051daSDaniel Vetter lock_acquire_shared_recursive(&connector_list_iter_dep_map, 0, 1, NULL, _RET_IP_); 541613051daSDaniel Vetter } 542b982dab1SThierry Reding EXPORT_SYMBOL(drm_connector_list_iter_begin); 543613051daSDaniel Vetter 544*a703c550SDaniel Vetter /* 545*a703c550SDaniel Vetter * Extra-safe connector put function that works in any context. Should only be 546*a703c550SDaniel Vetter * used from the connector_iter functions, where we never really expect to 547*a703c550SDaniel Vetter * actually release the connector when dropping our final reference. 548*a703c550SDaniel Vetter */ 549*a703c550SDaniel Vetter static void 550*a703c550SDaniel Vetter drm_connector_put_safe(struct drm_connector *conn) 551*a703c550SDaniel Vetter { 552*a703c550SDaniel Vetter if (refcount_dec_and_test(&conn->base.refcount.refcount)) 553*a703c550SDaniel Vetter schedule_work(&conn->free_work); 554*a703c550SDaniel Vetter } 555*a703c550SDaniel Vetter 556613051daSDaniel Vetter /** 557613051daSDaniel Vetter * drm_connector_list_iter_next - return next connector 558613051daSDaniel Vetter * @iter: connectr_list iterator 559613051daSDaniel Vetter * 560613051daSDaniel Vetter * Returns the next connector for @iter, or NULL when the list walk has 561613051daSDaniel Vetter * completed. 562613051daSDaniel Vetter */ 563613051daSDaniel Vetter struct drm_connector * 564613051daSDaniel Vetter drm_connector_list_iter_next(struct drm_connector_list_iter *iter) 565613051daSDaniel Vetter { 566613051daSDaniel Vetter struct drm_connector *old_conn = iter->conn; 567613051daSDaniel Vetter struct drm_mode_config *config = &iter->dev->mode_config; 568613051daSDaniel Vetter struct list_head *lhead; 569613051daSDaniel Vetter unsigned long flags; 570613051daSDaniel Vetter 571613051daSDaniel Vetter spin_lock_irqsave(&config->connector_list_lock, flags); 572613051daSDaniel Vetter lhead = old_conn ? &old_conn->head : &config->connector_list; 573613051daSDaniel Vetter 574613051daSDaniel Vetter do { 575613051daSDaniel Vetter if (lhead->next == &config->connector_list) { 576613051daSDaniel Vetter iter->conn = NULL; 577613051daSDaniel Vetter break; 578613051daSDaniel Vetter } 579613051daSDaniel Vetter 580613051daSDaniel Vetter lhead = lhead->next; 581613051daSDaniel Vetter iter->conn = list_entry(lhead, struct drm_connector, head); 582613051daSDaniel Vetter 583613051daSDaniel Vetter /* loop until it's not a zombie connector */ 584613051daSDaniel Vetter } while (!kref_get_unless_zero(&iter->conn->base.refcount)); 585613051daSDaniel Vetter spin_unlock_irqrestore(&config->connector_list_lock, flags); 586613051daSDaniel Vetter 587613051daSDaniel Vetter if (old_conn) 588*a703c550SDaniel Vetter drm_connector_put_safe(old_conn); 589613051daSDaniel Vetter 590613051daSDaniel Vetter return iter->conn; 591613051daSDaniel Vetter } 592613051daSDaniel Vetter EXPORT_SYMBOL(drm_connector_list_iter_next); 593613051daSDaniel Vetter 594613051daSDaniel Vetter /** 595b982dab1SThierry Reding * drm_connector_list_iter_end - tear down a connector_list iterator 596613051daSDaniel Vetter * @iter: connector_list iterator 597613051daSDaniel Vetter * 598613051daSDaniel Vetter * Tears down @iter and releases any resources (like &drm_connector references) 599613051daSDaniel Vetter * acquired while walking the list. This must always be called, both when the 600613051daSDaniel Vetter * iteration completes fully or when it was aborted without walking the entire 601613051daSDaniel Vetter * list. 602613051daSDaniel Vetter */ 603b982dab1SThierry Reding void drm_connector_list_iter_end(struct drm_connector_list_iter *iter) 604613051daSDaniel Vetter { 605613051daSDaniel Vetter iter->dev = NULL; 606613051daSDaniel Vetter if (iter->conn) 607*a703c550SDaniel Vetter drm_connector_put_safe(iter->conn); 608613051daSDaniel Vetter lock_release(&connector_list_iter_dep_map, 0, _RET_IP_); 609613051daSDaniel Vetter } 610b982dab1SThierry Reding EXPORT_SYMBOL(drm_connector_list_iter_end); 611613051daSDaniel Vetter 61252217195SDaniel Vetter static const struct drm_prop_enum_list drm_subpixel_enum_list[] = { 61352217195SDaniel Vetter { SubPixelUnknown, "Unknown" }, 61452217195SDaniel Vetter { SubPixelHorizontalRGB, "Horizontal RGB" }, 61552217195SDaniel Vetter { SubPixelHorizontalBGR, "Horizontal BGR" }, 61652217195SDaniel Vetter { SubPixelVerticalRGB, "Vertical RGB" }, 61752217195SDaniel Vetter { SubPixelVerticalBGR, "Vertical BGR" }, 61852217195SDaniel Vetter { SubPixelNone, "None" }, 61952217195SDaniel Vetter }; 62052217195SDaniel Vetter 62152217195SDaniel Vetter /** 62252217195SDaniel Vetter * drm_get_subpixel_order_name - return a string for a given subpixel enum 62352217195SDaniel Vetter * @order: enum of subpixel_order 62452217195SDaniel Vetter * 62552217195SDaniel Vetter * Note you could abuse this and return something out of bounds, but that 62652217195SDaniel Vetter * would be a caller error. No unscrubbed user data should make it here. 62752217195SDaniel Vetter */ 62852217195SDaniel Vetter const char *drm_get_subpixel_order_name(enum subpixel_order order) 62952217195SDaniel Vetter { 63052217195SDaniel Vetter return drm_subpixel_enum_list[order].name; 63152217195SDaniel Vetter } 63252217195SDaniel Vetter EXPORT_SYMBOL(drm_get_subpixel_order_name); 63352217195SDaniel Vetter 63452217195SDaniel Vetter static const struct drm_prop_enum_list drm_dpms_enum_list[] = { 63552217195SDaniel Vetter { DRM_MODE_DPMS_ON, "On" }, 63652217195SDaniel Vetter { DRM_MODE_DPMS_STANDBY, "Standby" }, 63752217195SDaniel Vetter { DRM_MODE_DPMS_SUSPEND, "Suspend" }, 63852217195SDaniel Vetter { DRM_MODE_DPMS_OFF, "Off" } 63952217195SDaniel Vetter }; 64052217195SDaniel Vetter DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list) 64152217195SDaniel Vetter 64240ee6fbeSManasi Navare static const struct drm_prop_enum_list drm_link_status_enum_list[] = { 64340ee6fbeSManasi Navare { DRM_MODE_LINK_STATUS_GOOD, "Good" }, 64440ee6fbeSManasi Navare { DRM_MODE_LINK_STATUS_BAD, "Bad" }, 64540ee6fbeSManasi Navare }; 64640ee6fbeSManasi Navare 647b3c6c8bfSDaniel Vetter /** 648b3c6c8bfSDaniel Vetter * drm_display_info_set_bus_formats - set the supported bus formats 649b3c6c8bfSDaniel Vetter * @info: display info to store bus formats in 650b3c6c8bfSDaniel Vetter * @formats: array containing the supported bus formats 651b3c6c8bfSDaniel Vetter * @num_formats: the number of entries in the fmts array 652b3c6c8bfSDaniel Vetter * 653b3c6c8bfSDaniel Vetter * Store the supported bus formats in display info structure. 654b3c6c8bfSDaniel Vetter * See MEDIA_BUS_FMT_* definitions in include/uapi/linux/media-bus-format.h for 655b3c6c8bfSDaniel Vetter * a full list of available formats. 656b3c6c8bfSDaniel Vetter */ 657b3c6c8bfSDaniel Vetter int drm_display_info_set_bus_formats(struct drm_display_info *info, 658b3c6c8bfSDaniel Vetter const u32 *formats, 659b3c6c8bfSDaniel Vetter unsigned int num_formats) 660b3c6c8bfSDaniel Vetter { 661b3c6c8bfSDaniel Vetter u32 *fmts = NULL; 662b3c6c8bfSDaniel Vetter 663b3c6c8bfSDaniel Vetter if (!formats && num_formats) 664b3c6c8bfSDaniel Vetter return -EINVAL; 665b3c6c8bfSDaniel Vetter 666b3c6c8bfSDaniel Vetter if (formats && num_formats) { 667b3c6c8bfSDaniel Vetter fmts = kmemdup(formats, sizeof(*formats) * num_formats, 668b3c6c8bfSDaniel Vetter GFP_KERNEL); 669b3c6c8bfSDaniel Vetter if (!fmts) 670b3c6c8bfSDaniel Vetter return -ENOMEM; 671b3c6c8bfSDaniel Vetter } 672b3c6c8bfSDaniel Vetter 673b3c6c8bfSDaniel Vetter kfree(info->bus_formats); 674b3c6c8bfSDaniel Vetter info->bus_formats = fmts; 675b3c6c8bfSDaniel Vetter info->num_bus_formats = num_formats; 676b3c6c8bfSDaniel Vetter 677b3c6c8bfSDaniel Vetter return 0; 678b3c6c8bfSDaniel Vetter } 679b3c6c8bfSDaniel Vetter EXPORT_SYMBOL(drm_display_info_set_bus_formats); 680b3c6c8bfSDaniel Vetter 68152217195SDaniel Vetter /* Optional connector properties. */ 68252217195SDaniel Vetter static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] = { 68352217195SDaniel Vetter { DRM_MODE_SCALE_NONE, "None" }, 68452217195SDaniel Vetter { DRM_MODE_SCALE_FULLSCREEN, "Full" }, 68552217195SDaniel Vetter { DRM_MODE_SCALE_CENTER, "Center" }, 68652217195SDaniel Vetter { DRM_MODE_SCALE_ASPECT, "Full aspect" }, 68752217195SDaniel Vetter }; 68852217195SDaniel Vetter 68952217195SDaniel Vetter static const struct drm_prop_enum_list drm_aspect_ratio_enum_list[] = { 69052217195SDaniel Vetter { DRM_MODE_PICTURE_ASPECT_NONE, "Automatic" }, 69152217195SDaniel Vetter { DRM_MODE_PICTURE_ASPECT_4_3, "4:3" }, 69252217195SDaniel Vetter { DRM_MODE_PICTURE_ASPECT_16_9, "16:9" }, 69352217195SDaniel Vetter }; 69452217195SDaniel Vetter 69552217195SDaniel Vetter static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = { 69652217195SDaniel Vetter { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ 69752217195SDaniel Vetter { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ 69852217195SDaniel Vetter { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ 69952217195SDaniel Vetter }; 70052217195SDaniel Vetter DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list) 70152217195SDaniel Vetter 70252217195SDaniel Vetter static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = { 70352217195SDaniel Vetter { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ 70452217195SDaniel Vetter { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ 70552217195SDaniel Vetter { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ 70652217195SDaniel Vetter }; 70752217195SDaniel Vetter DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name, 70852217195SDaniel Vetter drm_dvi_i_subconnector_enum_list) 70952217195SDaniel Vetter 71052217195SDaniel Vetter static const struct drm_prop_enum_list drm_tv_select_enum_list[] = { 71152217195SDaniel Vetter { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ 71252217195SDaniel Vetter { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ 71352217195SDaniel Vetter { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ 71452217195SDaniel Vetter { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ 71552217195SDaniel Vetter { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ 71652217195SDaniel Vetter }; 71752217195SDaniel Vetter DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list) 71852217195SDaniel Vetter 71952217195SDaniel Vetter static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = { 72052217195SDaniel Vetter { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ 72152217195SDaniel Vetter { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ 72252217195SDaniel Vetter { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ 72352217195SDaniel Vetter { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ 72452217195SDaniel Vetter { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ 72552217195SDaniel Vetter }; 72652217195SDaniel Vetter DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name, 72752217195SDaniel Vetter drm_tv_subconnector_enum_list) 72852217195SDaniel Vetter 7294ada6f22SDaniel Vetter /** 7304ada6f22SDaniel Vetter * DOC: standard connector properties 7314ada6f22SDaniel Vetter * 7324ada6f22SDaniel Vetter * DRM connectors have a few standardized properties: 7334ada6f22SDaniel Vetter * 7344ada6f22SDaniel Vetter * EDID: 7354ada6f22SDaniel Vetter * Blob property which contains the current EDID read from the sink. This 7364ada6f22SDaniel Vetter * is useful to parse sink identification information like vendor, model 7374ada6f22SDaniel Vetter * and serial. Drivers should update this property by calling 7384ada6f22SDaniel Vetter * drm_mode_connector_update_edid_property(), usually after having parsed 7394ada6f22SDaniel Vetter * the EDID using drm_add_edid_modes(). Userspace cannot change this 7404ada6f22SDaniel Vetter * property. 7414ada6f22SDaniel Vetter * DPMS: 7424ada6f22SDaniel Vetter * Legacy property for setting the power state of the connector. For atomic 7434ada6f22SDaniel Vetter * drivers this is only provided for backwards compatibility with existing 7444ada6f22SDaniel Vetter * drivers, it remaps to controlling the "ACTIVE" property on the CRTC the 7454ada6f22SDaniel Vetter * connector is linked to. Drivers should never set this property directly, 746d574528aSDaniel Vetter * it is handled by the DRM core by calling the &drm_connector_funcs.dpms 747144a7999SDaniel Vetter * callback. For atomic drivers the remapping to the "ACTIVE" property is 748144a7999SDaniel Vetter * implemented in the DRM core. This is the only standard connector 749144a7999SDaniel Vetter * property that userspace can change. 750d0d1aee5SDaniel Vetter * 751d0d1aee5SDaniel Vetter * Note that this property cannot be set through the MODE_ATOMIC ioctl, 752d0d1aee5SDaniel Vetter * userspace must use "ACTIVE" on the CRTC instead. 753d0d1aee5SDaniel Vetter * 754d0d1aee5SDaniel Vetter * WARNING: 755d0d1aee5SDaniel Vetter * 756d0d1aee5SDaniel Vetter * For userspace also running on legacy drivers the "DPMS" semantics are a 757d0d1aee5SDaniel Vetter * lot more complicated. First, userspace cannot rely on the "DPMS" value 758d0d1aee5SDaniel Vetter * returned by the GETCONNECTOR actually reflecting reality, because many 759d0d1aee5SDaniel Vetter * drivers fail to update it. For atomic drivers this is taken care of in 760d0d1aee5SDaniel Vetter * drm_atomic_helper_update_legacy_modeset_state(). 761d0d1aee5SDaniel Vetter * 762d0d1aee5SDaniel Vetter * The second issue is that the DPMS state is only well-defined when the 763d0d1aee5SDaniel Vetter * connector is connected to a CRTC. In atomic the DRM core enforces that 764d0d1aee5SDaniel Vetter * "ACTIVE" is off in such a case, no such checks exists for "DPMS". 765d0d1aee5SDaniel Vetter * 766d0d1aee5SDaniel Vetter * Finally, when enabling an output using the legacy SETCONFIG ioctl then 767d0d1aee5SDaniel Vetter * "DPMS" is forced to ON. But see above, that might not be reflected in 768d0d1aee5SDaniel Vetter * the software value on legacy drivers. 769d0d1aee5SDaniel Vetter * 770d0d1aee5SDaniel Vetter * Summarizing: Only set "DPMS" when the connector is known to be enabled, 771d0d1aee5SDaniel Vetter * assume that a successful SETCONFIG call also sets "DPMS" to on, and 772d0d1aee5SDaniel Vetter * never read back the value of "DPMS" because it can be incorrect. 7734ada6f22SDaniel Vetter * PATH: 7744ada6f22SDaniel Vetter * Connector path property to identify how this sink is physically 7754ada6f22SDaniel Vetter * connected. Used by DP MST. This should be set by calling 7764ada6f22SDaniel Vetter * drm_mode_connector_set_path_property(), in the case of DP MST with the 7774ada6f22SDaniel Vetter * path property the MST manager created. Userspace cannot change this 7784ada6f22SDaniel Vetter * property. 7794ada6f22SDaniel Vetter * TILE: 7804ada6f22SDaniel Vetter * Connector tile group property to indicate how a set of DRM connector 7814ada6f22SDaniel Vetter * compose together into one logical screen. This is used by both high-res 7824ada6f22SDaniel Vetter * external screens (often only using a single cable, but exposing multiple 7834ada6f22SDaniel Vetter * DP MST sinks), or high-res integrated panels (like dual-link DSI) which 7844ada6f22SDaniel Vetter * are not gen-locked. Note that for tiled panels which are genlocked, like 7854ada6f22SDaniel Vetter * dual-link LVDS or dual-link DSI, the driver should try to not expose the 7864ada6f22SDaniel Vetter * tiling and virtualize both &drm_crtc and &drm_plane if needed. Drivers 7874ada6f22SDaniel Vetter * should update this value using drm_mode_connector_set_tile_property(). 7884ada6f22SDaniel Vetter * Userspace cannot change this property. 78940ee6fbeSManasi Navare * link-status: 79040ee6fbeSManasi Navare * Connector link-status property to indicate the status of link. The default 79140ee6fbeSManasi Navare * value of link-status is "GOOD". If something fails during or after modeset, 79240ee6fbeSManasi Navare * the kernel driver may set this to "BAD" and issue a hotplug uevent. Drivers 79340ee6fbeSManasi Navare * should update this value using drm_mode_connector_set_link_status_property(). 79466660d4cSDave Airlie * non_desktop: 79566660d4cSDave Airlie * Indicates the output should be ignored for purposes of displaying a 79666660d4cSDave Airlie * standard desktop environment or console. This is most likely because 79766660d4cSDave Airlie * the output device is not rectilinear. 7984ada6f22SDaniel Vetter * 7994ada6f22SDaniel Vetter * Connectors also have one standardized atomic property: 8004ada6f22SDaniel Vetter * 8014ada6f22SDaniel Vetter * CRTC_ID: 8024ada6f22SDaniel Vetter * Mode object ID of the &drm_crtc this connector should be connected to. 8034ada6f22SDaniel Vetter */ 8044ada6f22SDaniel Vetter 80552217195SDaniel Vetter int drm_connector_create_standard_properties(struct drm_device *dev) 80652217195SDaniel Vetter { 80752217195SDaniel Vetter struct drm_property *prop; 80852217195SDaniel Vetter 80952217195SDaniel Vetter prop = drm_property_create(dev, DRM_MODE_PROP_BLOB | 81052217195SDaniel Vetter DRM_MODE_PROP_IMMUTABLE, 81152217195SDaniel Vetter "EDID", 0); 81252217195SDaniel Vetter if (!prop) 81352217195SDaniel Vetter return -ENOMEM; 81452217195SDaniel Vetter dev->mode_config.edid_property = prop; 81552217195SDaniel Vetter 81652217195SDaniel Vetter prop = drm_property_create_enum(dev, 0, 81752217195SDaniel Vetter "DPMS", drm_dpms_enum_list, 81852217195SDaniel Vetter ARRAY_SIZE(drm_dpms_enum_list)); 81952217195SDaniel Vetter if (!prop) 82052217195SDaniel Vetter return -ENOMEM; 82152217195SDaniel Vetter dev->mode_config.dpms_property = prop; 82252217195SDaniel Vetter 82352217195SDaniel Vetter prop = drm_property_create(dev, 82452217195SDaniel Vetter DRM_MODE_PROP_BLOB | 82552217195SDaniel Vetter DRM_MODE_PROP_IMMUTABLE, 82652217195SDaniel Vetter "PATH", 0); 82752217195SDaniel Vetter if (!prop) 82852217195SDaniel Vetter return -ENOMEM; 82952217195SDaniel Vetter dev->mode_config.path_property = prop; 83052217195SDaniel Vetter 83152217195SDaniel Vetter prop = drm_property_create(dev, 83252217195SDaniel Vetter DRM_MODE_PROP_BLOB | 83352217195SDaniel Vetter DRM_MODE_PROP_IMMUTABLE, 83452217195SDaniel Vetter "TILE", 0); 83552217195SDaniel Vetter if (!prop) 83652217195SDaniel Vetter return -ENOMEM; 83752217195SDaniel Vetter dev->mode_config.tile_property = prop; 83852217195SDaniel Vetter 83940ee6fbeSManasi Navare prop = drm_property_create_enum(dev, 0, "link-status", 84040ee6fbeSManasi Navare drm_link_status_enum_list, 84140ee6fbeSManasi Navare ARRAY_SIZE(drm_link_status_enum_list)); 84240ee6fbeSManasi Navare if (!prop) 84340ee6fbeSManasi Navare return -ENOMEM; 84440ee6fbeSManasi Navare dev->mode_config.link_status_property = prop; 84540ee6fbeSManasi Navare 84666660d4cSDave Airlie prop = drm_property_create_bool(dev, DRM_MODE_PROP_IMMUTABLE, "non-desktop"); 84766660d4cSDave Airlie if (!prop) 84866660d4cSDave Airlie return -ENOMEM; 84966660d4cSDave Airlie dev->mode_config.non_desktop_property = prop; 85066660d4cSDave Airlie 85152217195SDaniel Vetter return 0; 85252217195SDaniel Vetter } 85352217195SDaniel Vetter 85452217195SDaniel Vetter /** 85552217195SDaniel Vetter * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties 85652217195SDaniel Vetter * @dev: DRM device 85752217195SDaniel Vetter * 85852217195SDaniel Vetter * Called by a driver the first time a DVI-I connector is made. 85952217195SDaniel Vetter */ 86052217195SDaniel Vetter int drm_mode_create_dvi_i_properties(struct drm_device *dev) 86152217195SDaniel Vetter { 86252217195SDaniel Vetter struct drm_property *dvi_i_selector; 86352217195SDaniel Vetter struct drm_property *dvi_i_subconnector; 86452217195SDaniel Vetter 86552217195SDaniel Vetter if (dev->mode_config.dvi_i_select_subconnector_property) 86652217195SDaniel Vetter return 0; 86752217195SDaniel Vetter 86852217195SDaniel Vetter dvi_i_selector = 86952217195SDaniel Vetter drm_property_create_enum(dev, 0, 87052217195SDaniel Vetter "select subconnector", 87152217195SDaniel Vetter drm_dvi_i_select_enum_list, 87252217195SDaniel Vetter ARRAY_SIZE(drm_dvi_i_select_enum_list)); 87352217195SDaniel Vetter dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector; 87452217195SDaniel Vetter 87552217195SDaniel Vetter dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, 87652217195SDaniel Vetter "subconnector", 87752217195SDaniel Vetter drm_dvi_i_subconnector_enum_list, 87852217195SDaniel Vetter ARRAY_SIZE(drm_dvi_i_subconnector_enum_list)); 87952217195SDaniel Vetter dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector; 88052217195SDaniel Vetter 88152217195SDaniel Vetter return 0; 88252217195SDaniel Vetter } 88352217195SDaniel Vetter EXPORT_SYMBOL(drm_mode_create_dvi_i_properties); 88452217195SDaniel Vetter 88552217195SDaniel Vetter /** 88652217195SDaniel Vetter * drm_create_tv_properties - create TV specific connector properties 88752217195SDaniel Vetter * @dev: DRM device 88852217195SDaniel Vetter * @num_modes: number of different TV formats (modes) supported 88952217195SDaniel Vetter * @modes: array of pointers to strings containing name of each format 89052217195SDaniel Vetter * 89152217195SDaniel Vetter * Called by a driver's TV initialization routine, this function creates 89252217195SDaniel Vetter * the TV specific connector properties for a given device. Caller is 89352217195SDaniel Vetter * responsible for allocating a list of format names and passing them to 89452217195SDaniel Vetter * this routine. 89552217195SDaniel Vetter */ 89652217195SDaniel Vetter int drm_mode_create_tv_properties(struct drm_device *dev, 89752217195SDaniel Vetter unsigned int num_modes, 89852217195SDaniel Vetter const char * const modes[]) 89952217195SDaniel Vetter { 90052217195SDaniel Vetter struct drm_property *tv_selector; 90152217195SDaniel Vetter struct drm_property *tv_subconnector; 90252217195SDaniel Vetter unsigned int i; 90352217195SDaniel Vetter 90452217195SDaniel Vetter if (dev->mode_config.tv_select_subconnector_property) 90552217195SDaniel Vetter return 0; 90652217195SDaniel Vetter 90752217195SDaniel Vetter /* 90852217195SDaniel Vetter * Basic connector properties 90952217195SDaniel Vetter */ 91052217195SDaniel Vetter tv_selector = drm_property_create_enum(dev, 0, 91152217195SDaniel Vetter "select subconnector", 91252217195SDaniel Vetter drm_tv_select_enum_list, 91352217195SDaniel Vetter ARRAY_SIZE(drm_tv_select_enum_list)); 91452217195SDaniel Vetter if (!tv_selector) 91552217195SDaniel Vetter goto nomem; 91652217195SDaniel Vetter 91752217195SDaniel Vetter dev->mode_config.tv_select_subconnector_property = tv_selector; 91852217195SDaniel Vetter 91952217195SDaniel Vetter tv_subconnector = 92052217195SDaniel Vetter drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, 92152217195SDaniel Vetter "subconnector", 92252217195SDaniel Vetter drm_tv_subconnector_enum_list, 92352217195SDaniel Vetter ARRAY_SIZE(drm_tv_subconnector_enum_list)); 92452217195SDaniel Vetter if (!tv_subconnector) 92552217195SDaniel Vetter goto nomem; 92652217195SDaniel Vetter dev->mode_config.tv_subconnector_property = tv_subconnector; 92752217195SDaniel Vetter 92852217195SDaniel Vetter /* 92952217195SDaniel Vetter * Other, TV specific properties: margins & TV modes. 93052217195SDaniel Vetter */ 93152217195SDaniel Vetter dev->mode_config.tv_left_margin_property = 93252217195SDaniel Vetter drm_property_create_range(dev, 0, "left margin", 0, 100); 93352217195SDaniel Vetter if (!dev->mode_config.tv_left_margin_property) 93452217195SDaniel Vetter goto nomem; 93552217195SDaniel Vetter 93652217195SDaniel Vetter dev->mode_config.tv_right_margin_property = 93752217195SDaniel Vetter drm_property_create_range(dev, 0, "right margin", 0, 100); 93852217195SDaniel Vetter if (!dev->mode_config.tv_right_margin_property) 93952217195SDaniel Vetter goto nomem; 94052217195SDaniel Vetter 94152217195SDaniel Vetter dev->mode_config.tv_top_margin_property = 94252217195SDaniel Vetter drm_property_create_range(dev, 0, "top margin", 0, 100); 94352217195SDaniel Vetter if (!dev->mode_config.tv_top_margin_property) 94452217195SDaniel Vetter goto nomem; 94552217195SDaniel Vetter 94652217195SDaniel Vetter dev->mode_config.tv_bottom_margin_property = 94752217195SDaniel Vetter drm_property_create_range(dev, 0, "bottom margin", 0, 100); 94852217195SDaniel Vetter if (!dev->mode_config.tv_bottom_margin_property) 94952217195SDaniel Vetter goto nomem; 95052217195SDaniel Vetter 95152217195SDaniel Vetter dev->mode_config.tv_mode_property = 95252217195SDaniel Vetter drm_property_create(dev, DRM_MODE_PROP_ENUM, 95352217195SDaniel Vetter "mode", num_modes); 95452217195SDaniel Vetter if (!dev->mode_config.tv_mode_property) 95552217195SDaniel Vetter goto nomem; 95652217195SDaniel Vetter 95752217195SDaniel Vetter for (i = 0; i < num_modes; i++) 95852217195SDaniel Vetter drm_property_add_enum(dev->mode_config.tv_mode_property, i, 95952217195SDaniel Vetter i, modes[i]); 96052217195SDaniel Vetter 96152217195SDaniel Vetter dev->mode_config.tv_brightness_property = 96252217195SDaniel Vetter drm_property_create_range(dev, 0, "brightness", 0, 100); 96352217195SDaniel Vetter if (!dev->mode_config.tv_brightness_property) 96452217195SDaniel Vetter goto nomem; 96552217195SDaniel Vetter 96652217195SDaniel Vetter dev->mode_config.tv_contrast_property = 96752217195SDaniel Vetter drm_property_create_range(dev, 0, "contrast", 0, 100); 96852217195SDaniel Vetter if (!dev->mode_config.tv_contrast_property) 96952217195SDaniel Vetter goto nomem; 97052217195SDaniel Vetter 97152217195SDaniel Vetter dev->mode_config.tv_flicker_reduction_property = 97252217195SDaniel Vetter drm_property_create_range(dev, 0, "flicker reduction", 0, 100); 97352217195SDaniel Vetter if (!dev->mode_config.tv_flicker_reduction_property) 97452217195SDaniel Vetter goto nomem; 97552217195SDaniel Vetter 97652217195SDaniel Vetter dev->mode_config.tv_overscan_property = 97752217195SDaniel Vetter drm_property_create_range(dev, 0, "overscan", 0, 100); 97852217195SDaniel Vetter if (!dev->mode_config.tv_overscan_property) 97952217195SDaniel Vetter goto nomem; 98052217195SDaniel Vetter 98152217195SDaniel Vetter dev->mode_config.tv_saturation_property = 98252217195SDaniel Vetter drm_property_create_range(dev, 0, "saturation", 0, 100); 98352217195SDaniel Vetter if (!dev->mode_config.tv_saturation_property) 98452217195SDaniel Vetter goto nomem; 98552217195SDaniel Vetter 98652217195SDaniel Vetter dev->mode_config.tv_hue_property = 98752217195SDaniel Vetter drm_property_create_range(dev, 0, "hue", 0, 100); 98852217195SDaniel Vetter if (!dev->mode_config.tv_hue_property) 98952217195SDaniel Vetter goto nomem; 99052217195SDaniel Vetter 99152217195SDaniel Vetter return 0; 99252217195SDaniel Vetter nomem: 99352217195SDaniel Vetter return -ENOMEM; 99452217195SDaniel Vetter } 99552217195SDaniel Vetter EXPORT_SYMBOL(drm_mode_create_tv_properties); 99652217195SDaniel Vetter 99752217195SDaniel Vetter /** 99852217195SDaniel Vetter * drm_mode_create_scaling_mode_property - create scaling mode property 99952217195SDaniel Vetter * @dev: DRM device 100052217195SDaniel Vetter * 100152217195SDaniel Vetter * Called by a driver the first time it's needed, must be attached to desired 100252217195SDaniel Vetter * connectors. 10038f6e1e22SMaarten Lankhorst * 10048f6e1e22SMaarten Lankhorst * Atomic drivers should use drm_connector_attach_scaling_mode_property() 10058f6e1e22SMaarten Lankhorst * instead to correctly assign &drm_connector_state.picture_aspect_ratio 10068f6e1e22SMaarten Lankhorst * in the atomic state. 100752217195SDaniel Vetter */ 100852217195SDaniel Vetter int drm_mode_create_scaling_mode_property(struct drm_device *dev) 100952217195SDaniel Vetter { 101052217195SDaniel Vetter struct drm_property *scaling_mode; 101152217195SDaniel Vetter 101252217195SDaniel Vetter if (dev->mode_config.scaling_mode_property) 101352217195SDaniel Vetter return 0; 101452217195SDaniel Vetter 101552217195SDaniel Vetter scaling_mode = 101652217195SDaniel Vetter drm_property_create_enum(dev, 0, "scaling mode", 101752217195SDaniel Vetter drm_scaling_mode_enum_list, 101852217195SDaniel Vetter ARRAY_SIZE(drm_scaling_mode_enum_list)); 101952217195SDaniel Vetter 102052217195SDaniel Vetter dev->mode_config.scaling_mode_property = scaling_mode; 102152217195SDaniel Vetter 102252217195SDaniel Vetter return 0; 102352217195SDaniel Vetter } 102452217195SDaniel Vetter EXPORT_SYMBOL(drm_mode_create_scaling_mode_property); 102552217195SDaniel Vetter 102652217195SDaniel Vetter /** 10278f6e1e22SMaarten Lankhorst * drm_connector_attach_scaling_mode_property - attach atomic scaling mode property 10288f6e1e22SMaarten Lankhorst * @connector: connector to attach scaling mode property on. 10298f6e1e22SMaarten Lankhorst * @scaling_mode_mask: or'ed mask of BIT(%DRM_MODE_SCALE_\*). 10308f6e1e22SMaarten Lankhorst * 10318f6e1e22SMaarten Lankhorst * This is used to add support for scaling mode to atomic drivers. 10328f6e1e22SMaarten Lankhorst * The scaling mode will be set to &drm_connector_state.picture_aspect_ratio 10338f6e1e22SMaarten Lankhorst * and can be used from &drm_connector_helper_funcs->atomic_check for validation. 10348f6e1e22SMaarten Lankhorst * 10358f6e1e22SMaarten Lankhorst * This is the atomic version of drm_mode_create_scaling_mode_property(). 10368f6e1e22SMaarten Lankhorst * 10378f6e1e22SMaarten Lankhorst * Returns: 10388f6e1e22SMaarten Lankhorst * Zero on success, negative errno on failure. 10398f6e1e22SMaarten Lankhorst */ 10408f6e1e22SMaarten Lankhorst int drm_connector_attach_scaling_mode_property(struct drm_connector *connector, 10418f6e1e22SMaarten Lankhorst u32 scaling_mode_mask) 10428f6e1e22SMaarten Lankhorst { 10438f6e1e22SMaarten Lankhorst struct drm_device *dev = connector->dev; 10448f6e1e22SMaarten Lankhorst struct drm_property *scaling_mode_property; 10458f6e1e22SMaarten Lankhorst int i, j = 0; 10468f6e1e22SMaarten Lankhorst const unsigned valid_scaling_mode_mask = 10478f6e1e22SMaarten Lankhorst (1U << ARRAY_SIZE(drm_scaling_mode_enum_list)) - 1; 10488f6e1e22SMaarten Lankhorst 10498f6e1e22SMaarten Lankhorst if (WARN_ON(hweight32(scaling_mode_mask) < 2 || 10508f6e1e22SMaarten Lankhorst scaling_mode_mask & ~valid_scaling_mode_mask)) 10518f6e1e22SMaarten Lankhorst return -EINVAL; 10528f6e1e22SMaarten Lankhorst 10538f6e1e22SMaarten Lankhorst scaling_mode_property = 10548f6e1e22SMaarten Lankhorst drm_property_create(dev, DRM_MODE_PROP_ENUM, "scaling mode", 10558f6e1e22SMaarten Lankhorst hweight32(scaling_mode_mask)); 10568f6e1e22SMaarten Lankhorst 10578f6e1e22SMaarten Lankhorst if (!scaling_mode_property) 10588f6e1e22SMaarten Lankhorst return -ENOMEM; 10598f6e1e22SMaarten Lankhorst 10608f6e1e22SMaarten Lankhorst for (i = 0; i < ARRAY_SIZE(drm_scaling_mode_enum_list); i++) { 10618f6e1e22SMaarten Lankhorst int ret; 10628f6e1e22SMaarten Lankhorst 10638f6e1e22SMaarten Lankhorst if (!(BIT(i) & scaling_mode_mask)) 10648f6e1e22SMaarten Lankhorst continue; 10658f6e1e22SMaarten Lankhorst 10668f6e1e22SMaarten Lankhorst ret = drm_property_add_enum(scaling_mode_property, j++, 10678f6e1e22SMaarten Lankhorst drm_scaling_mode_enum_list[i].type, 10688f6e1e22SMaarten Lankhorst drm_scaling_mode_enum_list[i].name); 10698f6e1e22SMaarten Lankhorst 10708f6e1e22SMaarten Lankhorst if (ret) { 10718f6e1e22SMaarten Lankhorst drm_property_destroy(dev, scaling_mode_property); 10728f6e1e22SMaarten Lankhorst 10738f6e1e22SMaarten Lankhorst return ret; 10748f6e1e22SMaarten Lankhorst } 10758f6e1e22SMaarten Lankhorst } 10768f6e1e22SMaarten Lankhorst 10778f6e1e22SMaarten Lankhorst drm_object_attach_property(&connector->base, 10788f6e1e22SMaarten Lankhorst scaling_mode_property, 0); 10798f6e1e22SMaarten Lankhorst 10808f6e1e22SMaarten Lankhorst connector->scaling_mode_property = scaling_mode_property; 10818f6e1e22SMaarten Lankhorst 10828f6e1e22SMaarten Lankhorst return 0; 10838f6e1e22SMaarten Lankhorst } 10848f6e1e22SMaarten Lankhorst EXPORT_SYMBOL(drm_connector_attach_scaling_mode_property); 10858f6e1e22SMaarten Lankhorst 10868f6e1e22SMaarten Lankhorst /** 108752217195SDaniel Vetter * drm_mode_create_aspect_ratio_property - create aspect ratio property 108852217195SDaniel Vetter * @dev: DRM device 108952217195SDaniel Vetter * 109052217195SDaniel Vetter * Called by a driver the first time it's needed, must be attached to desired 109152217195SDaniel Vetter * connectors. 109252217195SDaniel Vetter * 109352217195SDaniel Vetter * Returns: 109452217195SDaniel Vetter * Zero on success, negative errno on failure. 109552217195SDaniel Vetter */ 109652217195SDaniel Vetter int drm_mode_create_aspect_ratio_property(struct drm_device *dev) 109752217195SDaniel Vetter { 109852217195SDaniel Vetter if (dev->mode_config.aspect_ratio_property) 109952217195SDaniel Vetter return 0; 110052217195SDaniel Vetter 110152217195SDaniel Vetter dev->mode_config.aspect_ratio_property = 110252217195SDaniel Vetter drm_property_create_enum(dev, 0, "aspect ratio", 110352217195SDaniel Vetter drm_aspect_ratio_enum_list, 110452217195SDaniel Vetter ARRAY_SIZE(drm_aspect_ratio_enum_list)); 110552217195SDaniel Vetter 110652217195SDaniel Vetter if (dev->mode_config.aspect_ratio_property == NULL) 110752217195SDaniel Vetter return -ENOMEM; 110852217195SDaniel Vetter 110952217195SDaniel Vetter return 0; 111052217195SDaniel Vetter } 111152217195SDaniel Vetter EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property); 111252217195SDaniel Vetter 111352217195SDaniel Vetter /** 111452217195SDaniel Vetter * drm_mode_create_suggested_offset_properties - create suggests offset properties 111552217195SDaniel Vetter * @dev: DRM device 111652217195SDaniel Vetter * 111752217195SDaniel Vetter * Create the the suggested x/y offset property for connectors. 111852217195SDaniel Vetter */ 111952217195SDaniel Vetter int drm_mode_create_suggested_offset_properties(struct drm_device *dev) 112052217195SDaniel Vetter { 112152217195SDaniel Vetter if (dev->mode_config.suggested_x_property && dev->mode_config.suggested_y_property) 112252217195SDaniel Vetter return 0; 112352217195SDaniel Vetter 112452217195SDaniel Vetter dev->mode_config.suggested_x_property = 112552217195SDaniel Vetter drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested X", 0, 0xffffffff); 112652217195SDaniel Vetter 112752217195SDaniel Vetter dev->mode_config.suggested_y_property = 112852217195SDaniel Vetter drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested Y", 0, 0xffffffff); 112952217195SDaniel Vetter 113052217195SDaniel Vetter if (dev->mode_config.suggested_x_property == NULL || 113152217195SDaniel Vetter dev->mode_config.suggested_y_property == NULL) 113252217195SDaniel Vetter return -ENOMEM; 113352217195SDaniel Vetter return 0; 113452217195SDaniel Vetter } 113552217195SDaniel Vetter EXPORT_SYMBOL(drm_mode_create_suggested_offset_properties); 113652217195SDaniel Vetter 113752217195SDaniel Vetter /** 113852217195SDaniel Vetter * drm_mode_connector_set_path_property - set tile property on connector 113952217195SDaniel Vetter * @connector: connector to set property on. 114052217195SDaniel Vetter * @path: path to use for property; must not be NULL. 114152217195SDaniel Vetter * 114252217195SDaniel Vetter * This creates a property to expose to userspace to specify a 114352217195SDaniel Vetter * connector path. This is mainly used for DisplayPort MST where 114452217195SDaniel Vetter * connectors have a topology and we want to allow userspace to give 114552217195SDaniel Vetter * them more meaningful names. 114652217195SDaniel Vetter * 114752217195SDaniel Vetter * Returns: 114852217195SDaniel Vetter * Zero on success, negative errno on failure. 114952217195SDaniel Vetter */ 115052217195SDaniel Vetter int drm_mode_connector_set_path_property(struct drm_connector *connector, 115152217195SDaniel Vetter const char *path) 115252217195SDaniel Vetter { 115352217195SDaniel Vetter struct drm_device *dev = connector->dev; 115452217195SDaniel Vetter int ret; 115552217195SDaniel Vetter 115652217195SDaniel Vetter ret = drm_property_replace_global_blob(dev, 115752217195SDaniel Vetter &connector->path_blob_ptr, 115852217195SDaniel Vetter strlen(path) + 1, 115952217195SDaniel Vetter path, 116052217195SDaniel Vetter &connector->base, 116152217195SDaniel Vetter dev->mode_config.path_property); 116252217195SDaniel Vetter return ret; 116352217195SDaniel Vetter } 116452217195SDaniel Vetter EXPORT_SYMBOL(drm_mode_connector_set_path_property); 116552217195SDaniel Vetter 116652217195SDaniel Vetter /** 116752217195SDaniel Vetter * drm_mode_connector_set_tile_property - set tile property on connector 116852217195SDaniel Vetter * @connector: connector to set property on. 116952217195SDaniel Vetter * 117052217195SDaniel Vetter * This looks up the tile information for a connector, and creates a 117152217195SDaniel Vetter * property for userspace to parse if it exists. The property is of 117252217195SDaniel Vetter * the form of 8 integers using ':' as a separator. 117352217195SDaniel Vetter * 117452217195SDaniel Vetter * Returns: 117552217195SDaniel Vetter * Zero on success, errno on failure. 117652217195SDaniel Vetter */ 117752217195SDaniel Vetter int drm_mode_connector_set_tile_property(struct drm_connector *connector) 117852217195SDaniel Vetter { 117952217195SDaniel Vetter struct drm_device *dev = connector->dev; 118052217195SDaniel Vetter char tile[256]; 118152217195SDaniel Vetter int ret; 118252217195SDaniel Vetter 118352217195SDaniel Vetter if (!connector->has_tile) { 118452217195SDaniel Vetter ret = drm_property_replace_global_blob(dev, 118552217195SDaniel Vetter &connector->tile_blob_ptr, 118652217195SDaniel Vetter 0, 118752217195SDaniel Vetter NULL, 118852217195SDaniel Vetter &connector->base, 118952217195SDaniel Vetter dev->mode_config.tile_property); 119052217195SDaniel Vetter return ret; 119152217195SDaniel Vetter } 119252217195SDaniel Vetter 119352217195SDaniel Vetter snprintf(tile, 256, "%d:%d:%d:%d:%d:%d:%d:%d", 119452217195SDaniel Vetter connector->tile_group->id, connector->tile_is_single_monitor, 119552217195SDaniel Vetter connector->num_h_tile, connector->num_v_tile, 119652217195SDaniel Vetter connector->tile_h_loc, connector->tile_v_loc, 119752217195SDaniel Vetter connector->tile_h_size, connector->tile_v_size); 119852217195SDaniel Vetter 119952217195SDaniel Vetter ret = drm_property_replace_global_blob(dev, 120052217195SDaniel Vetter &connector->tile_blob_ptr, 120152217195SDaniel Vetter strlen(tile) + 1, 120252217195SDaniel Vetter tile, 120352217195SDaniel Vetter &connector->base, 120452217195SDaniel Vetter dev->mode_config.tile_property); 120552217195SDaniel Vetter return ret; 120652217195SDaniel Vetter } 120752217195SDaniel Vetter EXPORT_SYMBOL(drm_mode_connector_set_tile_property); 120852217195SDaniel Vetter 120952217195SDaniel Vetter /** 121052217195SDaniel Vetter * drm_mode_connector_update_edid_property - update the edid property of a connector 121152217195SDaniel Vetter * @connector: drm connector 121252217195SDaniel Vetter * @edid: new value of the edid property 121352217195SDaniel Vetter * 121452217195SDaniel Vetter * This function creates a new blob modeset object and assigns its id to the 121552217195SDaniel Vetter * connector's edid property. 121652217195SDaniel Vetter * 121752217195SDaniel Vetter * Returns: 121852217195SDaniel Vetter * Zero on success, negative errno on failure. 121952217195SDaniel Vetter */ 122052217195SDaniel Vetter int drm_mode_connector_update_edid_property(struct drm_connector *connector, 122152217195SDaniel Vetter const struct edid *edid) 122252217195SDaniel Vetter { 122352217195SDaniel Vetter struct drm_device *dev = connector->dev; 122452217195SDaniel Vetter size_t size = 0; 122552217195SDaniel Vetter int ret; 122652217195SDaniel Vetter 122752217195SDaniel Vetter /* ignore requests to set edid when overridden */ 122852217195SDaniel Vetter if (connector->override_edid) 122952217195SDaniel Vetter return 0; 123052217195SDaniel Vetter 123152217195SDaniel Vetter if (edid) 123252217195SDaniel Vetter size = EDID_LENGTH * (1 + edid->extensions); 123352217195SDaniel Vetter 123466660d4cSDave Airlie drm_object_property_set_value(&connector->base, 123566660d4cSDave Airlie dev->mode_config.non_desktop_property, 123666660d4cSDave Airlie connector->display_info.non_desktop); 123766660d4cSDave Airlie 123852217195SDaniel Vetter ret = drm_property_replace_global_blob(dev, 123952217195SDaniel Vetter &connector->edid_blob_ptr, 124052217195SDaniel Vetter size, 124152217195SDaniel Vetter edid, 124252217195SDaniel Vetter &connector->base, 124352217195SDaniel Vetter dev->mode_config.edid_property); 124452217195SDaniel Vetter return ret; 124552217195SDaniel Vetter } 124652217195SDaniel Vetter EXPORT_SYMBOL(drm_mode_connector_update_edid_property); 124752217195SDaniel Vetter 124840ee6fbeSManasi Navare /** 124940ee6fbeSManasi Navare * drm_mode_connector_set_link_status_property - Set link status property of a connector 125040ee6fbeSManasi Navare * @connector: drm connector 125140ee6fbeSManasi Navare * @link_status: new value of link status property (0: Good, 1: Bad) 125240ee6fbeSManasi Navare * 125340ee6fbeSManasi Navare * In usual working scenario, this link status property will always be set to 125440ee6fbeSManasi Navare * "GOOD". If something fails during or after a mode set, the kernel driver 125540ee6fbeSManasi Navare * may set this link status property to "BAD". The caller then needs to send a 125640ee6fbeSManasi Navare * hotplug uevent for userspace to re-check the valid modes through 125740ee6fbeSManasi Navare * GET_CONNECTOR_IOCTL and retry modeset. 125840ee6fbeSManasi Navare * 125940ee6fbeSManasi Navare * Note: Drivers cannot rely on userspace to support this property and 126040ee6fbeSManasi Navare * issue a modeset. As such, they may choose to handle issues (like 126140ee6fbeSManasi Navare * re-training a link) without userspace's intervention. 126240ee6fbeSManasi Navare * 126340ee6fbeSManasi Navare * The reason for adding this property is to handle link training failures, but 126440ee6fbeSManasi Navare * it is not limited to DP or link training. For example, if we implement 126540ee6fbeSManasi Navare * asynchronous setcrtc, this property can be used to report any failures in that. 126640ee6fbeSManasi Navare */ 126740ee6fbeSManasi Navare void drm_mode_connector_set_link_status_property(struct drm_connector *connector, 126840ee6fbeSManasi Navare uint64_t link_status) 126940ee6fbeSManasi Navare { 127040ee6fbeSManasi Navare struct drm_device *dev = connector->dev; 127140ee6fbeSManasi Navare 127240ee6fbeSManasi Navare drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); 127340ee6fbeSManasi Navare connector->state->link_status = link_status; 127440ee6fbeSManasi Navare drm_modeset_unlock(&dev->mode_config.connection_mutex); 127540ee6fbeSManasi Navare } 127640ee6fbeSManasi Navare EXPORT_SYMBOL(drm_mode_connector_set_link_status_property); 127740ee6fbeSManasi Navare 127852217195SDaniel Vetter int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj, 127952217195SDaniel Vetter struct drm_property *property, 128052217195SDaniel Vetter uint64_t value) 128152217195SDaniel Vetter { 128252217195SDaniel Vetter int ret = -EINVAL; 128352217195SDaniel Vetter struct drm_connector *connector = obj_to_connector(obj); 128452217195SDaniel Vetter 128552217195SDaniel Vetter /* Do DPMS ourselves */ 128652217195SDaniel Vetter if (property == connector->dev->mode_config.dpms_property) { 128752217195SDaniel Vetter ret = (*connector->funcs->dpms)(connector, (int)value); 128852217195SDaniel Vetter } else if (connector->funcs->set_property) 128952217195SDaniel Vetter ret = connector->funcs->set_property(connector, property, value); 129052217195SDaniel Vetter 1291144a7999SDaniel Vetter if (!ret) 129252217195SDaniel Vetter drm_object_property_set_value(&connector->base, property, value); 129352217195SDaniel Vetter return ret; 129452217195SDaniel Vetter } 129552217195SDaniel Vetter 129652217195SDaniel Vetter int drm_mode_connector_property_set_ioctl(struct drm_device *dev, 129752217195SDaniel Vetter void *data, struct drm_file *file_priv) 129852217195SDaniel Vetter { 129952217195SDaniel Vetter struct drm_mode_connector_set_property *conn_set_prop = data; 130052217195SDaniel Vetter struct drm_mode_obj_set_property obj_set_prop = { 130152217195SDaniel Vetter .value = conn_set_prop->value, 130252217195SDaniel Vetter .prop_id = conn_set_prop->prop_id, 130352217195SDaniel Vetter .obj_id = conn_set_prop->connector_id, 130452217195SDaniel Vetter .obj_type = DRM_MODE_OBJECT_CONNECTOR 130552217195SDaniel Vetter }; 130652217195SDaniel Vetter 130752217195SDaniel Vetter /* It does all the locking and checking we need */ 130852217195SDaniel Vetter return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv); 130952217195SDaniel Vetter } 131052217195SDaniel Vetter 131152217195SDaniel Vetter static struct drm_encoder *drm_connector_get_encoder(struct drm_connector *connector) 131252217195SDaniel Vetter { 131352217195SDaniel Vetter /* For atomic drivers only state objects are synchronously updated and 131452217195SDaniel Vetter * protected by modeset locks, so check those first. */ 131552217195SDaniel Vetter if (connector->state) 131652217195SDaniel Vetter return connector->state->best_encoder; 131752217195SDaniel Vetter return connector->encoder; 131852217195SDaniel Vetter } 131952217195SDaniel Vetter 132052217195SDaniel Vetter static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode, 132152217195SDaniel Vetter const struct drm_file *file_priv) 132252217195SDaniel Vetter { 132352217195SDaniel Vetter /* 132452217195SDaniel Vetter * If user-space hasn't configured the driver to expose the stereo 3D 132552217195SDaniel Vetter * modes, don't expose them. 132652217195SDaniel Vetter */ 132752217195SDaniel Vetter if (!file_priv->stereo_allowed && drm_mode_is_stereo(mode)) 132852217195SDaniel Vetter return false; 132952217195SDaniel Vetter 133052217195SDaniel Vetter return true; 133152217195SDaniel Vetter } 133252217195SDaniel Vetter 133352217195SDaniel Vetter int drm_mode_getconnector(struct drm_device *dev, void *data, 133452217195SDaniel Vetter struct drm_file *file_priv) 133552217195SDaniel Vetter { 133652217195SDaniel Vetter struct drm_mode_get_connector *out_resp = data; 133752217195SDaniel Vetter struct drm_connector *connector; 133852217195SDaniel Vetter struct drm_encoder *encoder; 133952217195SDaniel Vetter struct drm_display_mode *mode; 134052217195SDaniel Vetter int mode_count = 0; 134152217195SDaniel Vetter int encoders_count = 0; 134252217195SDaniel Vetter int ret = 0; 134352217195SDaniel Vetter int copied = 0; 134452217195SDaniel Vetter int i; 134552217195SDaniel Vetter struct drm_mode_modeinfo u_mode; 134652217195SDaniel Vetter struct drm_mode_modeinfo __user *mode_ptr; 134752217195SDaniel Vetter uint32_t __user *encoder_ptr; 134852217195SDaniel Vetter 134952217195SDaniel Vetter if (!drm_core_check_feature(dev, DRIVER_MODESET)) 135052217195SDaniel Vetter return -EINVAL; 135152217195SDaniel Vetter 135252217195SDaniel Vetter memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); 135352217195SDaniel Vetter 1354418da172SKeith Packard connector = drm_connector_lookup(dev, file_priv, out_resp->connector_id); 135591eefc05SDaniel Vetter if (!connector) 135691eefc05SDaniel Vetter return -ENOENT; 135752217195SDaniel Vetter 135891eefc05SDaniel Vetter for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) 135991eefc05SDaniel Vetter if (connector->encoder_ids[i] != 0) 136091eefc05SDaniel Vetter encoders_count++; 136191eefc05SDaniel Vetter 136291eefc05SDaniel Vetter if ((out_resp->count_encoders >= encoders_count) && encoders_count) { 136391eefc05SDaniel Vetter copied = 0; 136491eefc05SDaniel Vetter encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr); 136591eefc05SDaniel Vetter for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { 136691eefc05SDaniel Vetter if (connector->encoder_ids[i] != 0) { 136791eefc05SDaniel Vetter if (put_user(connector->encoder_ids[i], 136891eefc05SDaniel Vetter encoder_ptr + copied)) { 136991eefc05SDaniel Vetter ret = -EFAULT; 1370e94ac351SDaniel Vetter goto out; 137191eefc05SDaniel Vetter } 137291eefc05SDaniel Vetter copied++; 137391eefc05SDaniel Vetter } 137491eefc05SDaniel Vetter } 137591eefc05SDaniel Vetter } 137691eefc05SDaniel Vetter out_resp->count_encoders = encoders_count; 137791eefc05SDaniel Vetter 137891eefc05SDaniel Vetter out_resp->connector_id = connector->base.id; 137991eefc05SDaniel Vetter out_resp->connector_type = connector->connector_type; 138091eefc05SDaniel Vetter out_resp->connector_type_id = connector->connector_type_id; 138191eefc05SDaniel Vetter 138291eefc05SDaniel Vetter mutex_lock(&dev->mode_config.mutex); 138391eefc05SDaniel Vetter if (out_resp->count_modes == 0) { 138491eefc05SDaniel Vetter connector->funcs->fill_modes(connector, 138591eefc05SDaniel Vetter dev->mode_config.max_width, 138691eefc05SDaniel Vetter dev->mode_config.max_height); 138791eefc05SDaniel Vetter } 138891eefc05SDaniel Vetter 138991eefc05SDaniel Vetter out_resp->mm_width = connector->display_info.width_mm; 139091eefc05SDaniel Vetter out_resp->mm_height = connector->display_info.height_mm; 139191eefc05SDaniel Vetter out_resp->subpixel = connector->display_info.subpixel_order; 139291eefc05SDaniel Vetter out_resp->connection = connector->status; 139391eefc05SDaniel Vetter 139491eefc05SDaniel Vetter /* delayed so we get modes regardless of pre-fill_modes state */ 139591eefc05SDaniel Vetter list_for_each_entry(mode, &connector->modes, head) 139691eefc05SDaniel Vetter if (drm_mode_expose_to_userspace(mode, file_priv)) 139791eefc05SDaniel Vetter mode_count++; 139891eefc05SDaniel Vetter 139952217195SDaniel Vetter /* 140052217195SDaniel Vetter * This ioctl is called twice, once to determine how much space is 140152217195SDaniel Vetter * needed, and the 2nd time to fill it. 140252217195SDaniel Vetter */ 140352217195SDaniel Vetter if ((out_resp->count_modes >= mode_count) && mode_count) { 140452217195SDaniel Vetter copied = 0; 140552217195SDaniel Vetter mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr; 140652217195SDaniel Vetter list_for_each_entry(mode, &connector->modes, head) { 140752217195SDaniel Vetter if (!drm_mode_expose_to_userspace(mode, file_priv)) 140852217195SDaniel Vetter continue; 140952217195SDaniel Vetter 141052217195SDaniel Vetter drm_mode_convert_to_umode(&u_mode, mode); 141152217195SDaniel Vetter if (copy_to_user(mode_ptr + copied, 141252217195SDaniel Vetter &u_mode, sizeof(u_mode))) { 141352217195SDaniel Vetter ret = -EFAULT; 1414e94ac351SDaniel Vetter mutex_unlock(&dev->mode_config.mutex); 1415e94ac351SDaniel Vetter 141652217195SDaniel Vetter goto out; 141752217195SDaniel Vetter } 141852217195SDaniel Vetter copied++; 141952217195SDaniel Vetter } 142052217195SDaniel Vetter } 142152217195SDaniel Vetter out_resp->count_modes = mode_count; 142252217195SDaniel Vetter mutex_unlock(&dev->mode_config.mutex); 1423e94ac351SDaniel Vetter 1424e94ac351SDaniel Vetter drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); 1425e94ac351SDaniel Vetter encoder = drm_connector_get_encoder(connector); 1426e94ac351SDaniel Vetter if (encoder) 1427e94ac351SDaniel Vetter out_resp->encoder_id = encoder->base.id; 1428e94ac351SDaniel Vetter else 1429e94ac351SDaniel Vetter out_resp->encoder_id = 0; 1430e94ac351SDaniel Vetter 1431e94ac351SDaniel Vetter /* Only grab properties after probing, to make sure EDID and other 1432e94ac351SDaniel Vetter * properties reflect the latest status. */ 1433e94ac351SDaniel Vetter ret = drm_mode_object_get_properties(&connector->base, file_priv->atomic, 1434e94ac351SDaniel Vetter (uint32_t __user *)(unsigned long)(out_resp->props_ptr), 1435e94ac351SDaniel Vetter (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr), 1436e94ac351SDaniel Vetter &out_resp->count_props); 1437e94ac351SDaniel Vetter drm_modeset_unlock(&dev->mode_config.connection_mutex); 1438e94ac351SDaniel Vetter 1439e94ac351SDaniel Vetter out: 1440ad093607SThierry Reding drm_connector_put(connector); 144152217195SDaniel Vetter 144252217195SDaniel Vetter return ret; 144352217195SDaniel Vetter } 144452217195SDaniel Vetter 14459498c19bSDaniel Vetter 14469498c19bSDaniel Vetter /** 14479498c19bSDaniel Vetter * DOC: Tile group 14489498c19bSDaniel Vetter * 14499498c19bSDaniel Vetter * Tile groups are used to represent tiled monitors with a unique integer 14509498c19bSDaniel Vetter * identifier. Tiled monitors using DisplayID v1.3 have a unique 8-byte handle, 14519498c19bSDaniel Vetter * we store this in a tile group, so we have a common identifier for all tiles 14529498c19bSDaniel Vetter * in a monitor group. The property is called "TILE". Drivers can manage tile 14539498c19bSDaniel Vetter * groups using drm_mode_create_tile_group(), drm_mode_put_tile_group() and 14549498c19bSDaniel Vetter * drm_mode_get_tile_group(). But this is only needed for internal panels where 14559498c19bSDaniel Vetter * the tile group information is exposed through a non-standard way. 14569498c19bSDaniel Vetter */ 14579498c19bSDaniel Vetter 14589498c19bSDaniel Vetter static void drm_tile_group_free(struct kref *kref) 14599498c19bSDaniel Vetter { 14609498c19bSDaniel Vetter struct drm_tile_group *tg = container_of(kref, struct drm_tile_group, refcount); 14619498c19bSDaniel Vetter struct drm_device *dev = tg->dev; 14629498c19bSDaniel Vetter mutex_lock(&dev->mode_config.idr_mutex); 14639498c19bSDaniel Vetter idr_remove(&dev->mode_config.tile_idr, tg->id); 14649498c19bSDaniel Vetter mutex_unlock(&dev->mode_config.idr_mutex); 14659498c19bSDaniel Vetter kfree(tg); 14669498c19bSDaniel Vetter } 14679498c19bSDaniel Vetter 14689498c19bSDaniel Vetter /** 14699498c19bSDaniel Vetter * drm_mode_put_tile_group - drop a reference to a tile group. 14709498c19bSDaniel Vetter * @dev: DRM device 14719498c19bSDaniel Vetter * @tg: tile group to drop reference to. 14729498c19bSDaniel Vetter * 14739498c19bSDaniel Vetter * drop reference to tile group and free if 0. 14749498c19bSDaniel Vetter */ 14759498c19bSDaniel Vetter void drm_mode_put_tile_group(struct drm_device *dev, 14769498c19bSDaniel Vetter struct drm_tile_group *tg) 14779498c19bSDaniel Vetter { 14789498c19bSDaniel Vetter kref_put(&tg->refcount, drm_tile_group_free); 14799498c19bSDaniel Vetter } 14809498c19bSDaniel Vetter EXPORT_SYMBOL(drm_mode_put_tile_group); 14819498c19bSDaniel Vetter 14829498c19bSDaniel Vetter /** 14839498c19bSDaniel Vetter * drm_mode_get_tile_group - get a reference to an existing tile group 14849498c19bSDaniel Vetter * @dev: DRM device 14859498c19bSDaniel Vetter * @topology: 8-bytes unique per monitor. 14869498c19bSDaniel Vetter * 14879498c19bSDaniel Vetter * Use the unique bytes to get a reference to an existing tile group. 14889498c19bSDaniel Vetter * 14899498c19bSDaniel Vetter * RETURNS: 14909498c19bSDaniel Vetter * tile group or NULL if not found. 14919498c19bSDaniel Vetter */ 14929498c19bSDaniel Vetter struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev, 14939498c19bSDaniel Vetter char topology[8]) 14949498c19bSDaniel Vetter { 14959498c19bSDaniel Vetter struct drm_tile_group *tg; 14969498c19bSDaniel Vetter int id; 14979498c19bSDaniel Vetter mutex_lock(&dev->mode_config.idr_mutex); 14989498c19bSDaniel Vetter idr_for_each_entry(&dev->mode_config.tile_idr, tg, id) { 14999498c19bSDaniel Vetter if (!memcmp(tg->group_data, topology, 8)) { 15009498c19bSDaniel Vetter if (!kref_get_unless_zero(&tg->refcount)) 15019498c19bSDaniel Vetter tg = NULL; 15029498c19bSDaniel Vetter mutex_unlock(&dev->mode_config.idr_mutex); 15039498c19bSDaniel Vetter return tg; 15049498c19bSDaniel Vetter } 15059498c19bSDaniel Vetter } 15069498c19bSDaniel Vetter mutex_unlock(&dev->mode_config.idr_mutex); 15079498c19bSDaniel Vetter return NULL; 15089498c19bSDaniel Vetter } 15099498c19bSDaniel Vetter EXPORT_SYMBOL(drm_mode_get_tile_group); 15109498c19bSDaniel Vetter 15119498c19bSDaniel Vetter /** 15129498c19bSDaniel Vetter * drm_mode_create_tile_group - create a tile group from a displayid description 15139498c19bSDaniel Vetter * @dev: DRM device 15149498c19bSDaniel Vetter * @topology: 8-bytes unique per monitor. 15159498c19bSDaniel Vetter * 15169498c19bSDaniel Vetter * Create a tile group for the unique monitor, and get a unique 15179498c19bSDaniel Vetter * identifier for the tile group. 15189498c19bSDaniel Vetter * 15199498c19bSDaniel Vetter * RETURNS: 15209498c19bSDaniel Vetter * new tile group or error. 15219498c19bSDaniel Vetter */ 15229498c19bSDaniel Vetter struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev, 15239498c19bSDaniel Vetter char topology[8]) 15249498c19bSDaniel Vetter { 15259498c19bSDaniel Vetter struct drm_tile_group *tg; 15269498c19bSDaniel Vetter int ret; 15279498c19bSDaniel Vetter 15289498c19bSDaniel Vetter tg = kzalloc(sizeof(*tg), GFP_KERNEL); 15299498c19bSDaniel Vetter if (!tg) 15309498c19bSDaniel Vetter return ERR_PTR(-ENOMEM); 15319498c19bSDaniel Vetter 15329498c19bSDaniel Vetter kref_init(&tg->refcount); 15339498c19bSDaniel Vetter memcpy(tg->group_data, topology, 8); 15349498c19bSDaniel Vetter tg->dev = dev; 15359498c19bSDaniel Vetter 15369498c19bSDaniel Vetter mutex_lock(&dev->mode_config.idr_mutex); 15379498c19bSDaniel Vetter ret = idr_alloc(&dev->mode_config.tile_idr, tg, 1, 0, GFP_KERNEL); 15389498c19bSDaniel Vetter if (ret >= 0) { 15399498c19bSDaniel Vetter tg->id = ret; 15409498c19bSDaniel Vetter } else { 15419498c19bSDaniel Vetter kfree(tg); 15429498c19bSDaniel Vetter tg = ERR_PTR(ret); 15439498c19bSDaniel Vetter } 15449498c19bSDaniel Vetter 15459498c19bSDaniel Vetter mutex_unlock(&dev->mode_config.idr_mutex); 15469498c19bSDaniel Vetter return tg; 15479498c19bSDaniel Vetter } 15489498c19bSDaniel Vetter EXPORT_SYMBOL(drm_mode_create_tile_group); 1549