diff options
Diffstat (limited to 'drivers/gpu/drm/drm_connector.c')
-rw-r--r-- | drivers/gpu/drm/drm_connector.c | 108 |
1 files changed, 102 insertions, 6 deletions
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index ba9f36cef68c..9ae236036e32 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -152,6 +152,25 @@ static void drm_connector_free(struct kref *kref) connector->funcs->destroy(connector); } +void drm_connector_free_work_fn(struct work_struct *work) +{ + struct drm_connector *connector, *n; + struct drm_device *dev = + container_of(work, struct drm_device, mode_config.connector_free_work); + struct drm_mode_config *config = &dev->mode_config; + unsigned long flags; + struct llist_node *freed; + + spin_lock_irqsave(&config->connector_list_lock, flags); + freed = llist_del_all(&config->connector_free_list); + spin_unlock_irqrestore(&config->connector_list_lock, flags); + + llist_for_each_entry_safe(connector, n, freed, free_node) { + drm_mode_object_unregister(dev, &connector->base); + connector->funcs->destroy(connector); + } +} + /** * drm_connector_init - Init a preallocated connector * @dev: DRM device @@ -234,6 +253,10 @@ int drm_connector_init(struct drm_device *dev, config->link_status_property, 0); + drm_object_attach_property(&connector->base, + config->non_desktop_property, + 0); + if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { drm_object_attach_property(&connector->base, config->prop_crtc_id, 0); } @@ -525,6 +548,25 @@ void drm_connector_list_iter_begin(struct drm_device *dev, } EXPORT_SYMBOL(drm_connector_list_iter_begin); +/* + * Extra-safe connector put function that works in any context. Should only be + * used from the connector_iter functions, where we never really expect to + * actually release the connector when dropping our final reference. + */ +static void +__drm_connector_put_safe(struct drm_connector *conn) +{ + struct drm_mode_config *config = &conn->dev->mode_config; + + lockdep_assert_held(&config->connector_list_lock); + + if (!refcount_dec_and_test(&conn->base.refcount.refcount)) + return; + + llist_add(&conn->free_node, &config->connector_free_list); + schedule_work(&config->connector_free_work); +} + /** * drm_connector_list_iter_next - return next connector * @iter: connectr_list iterator @@ -554,10 +596,10 @@ drm_connector_list_iter_next(struct drm_connector_list_iter *iter) /* loop until it's not a zombie connector */ } while (!kref_get_unless_zero(&iter->conn->base.refcount)); - spin_unlock_irqrestore(&config->connector_list_lock, flags); if (old_conn) - drm_connector_put(old_conn); + __drm_connector_put_safe(old_conn); + spin_unlock_irqrestore(&config->connector_list_lock, flags); return iter->conn; } @@ -574,9 +616,15 @@ EXPORT_SYMBOL(drm_connector_list_iter_next); */ void drm_connector_list_iter_end(struct drm_connector_list_iter *iter) { + struct drm_mode_config *config = &iter->dev->mode_config; + unsigned long flags; + iter->dev = NULL; - if (iter->conn) - drm_connector_put(iter->conn); + if (iter->conn) { + spin_lock_irqsave(&config->connector_list_lock, flags); + __drm_connector_put_safe(iter->conn); + spin_unlock_irqrestore(&config->connector_list_lock, flags); + } lock_release(&connector_list_iter_dep_map, 0, _RET_IP_); } EXPORT_SYMBOL(drm_connector_list_iter_end); @@ -615,7 +663,6 @@ static const struct drm_prop_enum_list drm_link_status_enum_list[] = { { DRM_MODE_LINK_STATUS_GOOD, "Good" }, { DRM_MODE_LINK_STATUS_BAD, "Bad" }, }; -DRM_ENUM_NAME_FN(drm_get_link_status_name, drm_link_status_enum_list) /** * drm_display_info_set_bus_formats - set the supported bus formats @@ -720,6 +767,29 @@ DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name, * callback. For atomic drivers the remapping to the "ACTIVE" property is * implemented in the DRM core. This is the only standard connector * property that userspace can change. + * + * Note that this property cannot be set through the MODE_ATOMIC ioctl, + * userspace must use "ACTIVE" on the CRTC instead. + * + * WARNING: + * + * For userspace also running on legacy drivers the "DPMS" semantics are a + * lot more complicated. First, userspace cannot rely on the "DPMS" value + * returned by the GETCONNECTOR actually reflecting reality, because many + * drivers fail to update it. For atomic drivers this is taken care of in + * drm_atomic_helper_update_legacy_modeset_state(). + * + * The second issue is that the DPMS state is only well-defined when the + * connector is connected to a CRTC. In atomic the DRM core enforces that + * "ACTIVE" is off in such a case, no such checks exists for "DPMS". + * + * Finally, when enabling an output using the legacy SETCONFIG ioctl then + * "DPMS" is forced to ON. But see above, that might not be reflected in + * the software value on legacy drivers. + * + * Summarizing: Only set "DPMS" when the connector is known to be enabled, + * assume that a successful SETCONFIG call also sets "DPMS" to on, and + * never read back the value of "DPMS" because it can be incorrect. * PATH: * Connector path property to identify how this sink is physically * connected. Used by DP MST. This should be set by calling @@ -741,6 +811,10 @@ DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name, * value of link-status is "GOOD". If something fails during or after modeset, * the kernel driver may set this to "BAD" and issue a hotplug uevent. Drivers * should update this value using drm_mode_connector_set_link_status_property(). + * non_desktop: + * Indicates the output should be ignored for purposes of displaying a + * standard desktop environment or console. This is most likely because + * the output device is not rectilinear. * * Connectors also have one standardized atomic property: * @@ -789,6 +863,11 @@ int drm_connector_create_standard_properties(struct drm_device *dev) return -ENOMEM; dev->mode_config.link_status_property = prop; + prop = drm_property_create_bool(dev, DRM_MODE_PROP_IMMUTABLE, "non-desktop"); + if (!prop) + return -ENOMEM; + dev->mode_config.non_desktop_property = prop; + return 0; } @@ -1172,6 +1251,23 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector, if (edid) size = EDID_LENGTH * (1 + edid->extensions); + /* Set the display info, using edid if available, otherwise + * reseting the values to defaults. This duplicates the work + * done in drm_add_edid_modes, but that function is not + * consistently called before this one in all drivers and the + * computation is cheap enough that it seems better to + * duplicate it rather than attempt to ensure some arbitrary + * ordering of calls. + */ + if (edid) + drm_add_display_info(connector, edid); + else + drm_reset_display_info(connector); + + drm_object_property_set_value(&connector->base, + dev->mode_config.non_desktop_property, + connector->display_info.non_desktop); + ret = drm_property_replace_global_blob(dev, &connector->edid_blob_ptr, size, @@ -1288,7 +1384,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); - connector = drm_connector_lookup(dev, out_resp->connector_id); + connector = drm_connector_lookup(dev, file_priv, out_resp->connector_id); if (!connector) return -ENOENT; |