diff options
Diffstat (limited to 'drivers/gpu/drm/drm_crtc.c')
| -rw-r--r-- | drivers/gpu/drm/drm_crtc.c | 1467 |
1 files changed, 1107 insertions, 360 deletions
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index bff2fa941f6..fe94cc10cd3 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -37,35 +37,78 @@ #include <drm/drm_crtc.h> #include <drm/drm_edid.h> #include <drm/drm_fourcc.h> +#include <drm/drm_modeset_lock.h> + +#include "drm_crtc_internal.h" /** * drm_modeset_lock_all - take all modeset locks * @dev: drm device * * This function takes all modeset locks, suitable where a more fine-grained - * scheme isn't (yet) implemented. + * scheme isn't (yet) implemented. Locks must be dropped with + * drm_modeset_unlock_all. */ void drm_modeset_lock_all(struct drm_device *dev) { - struct drm_crtc *crtc; + struct drm_mode_config *config = &dev->mode_config; + struct drm_modeset_acquire_ctx *ctx; + int ret; - mutex_lock(&dev->mode_config.mutex); + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (WARN_ON(!ctx)) + return; - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex); + mutex_lock(&config->mutex); + + drm_modeset_acquire_init(ctx, 0); + +retry: + ret = drm_modeset_lock(&config->connection_mutex, ctx); + if (ret) + goto fail; + ret = drm_modeset_lock_all_crtcs(dev, ctx); + if (ret) + goto fail; + + WARN_ON(config->acquire_ctx); + + /* now we hold the locks, so now that it is safe, stash the + * ctx for drm_modeset_unlock_all(): + */ + config->acquire_ctx = ctx; + + drm_warn_on_modeset_not_all_locked(dev); + + return; + +fail: + if (ret == -EDEADLK) { + drm_modeset_backoff(ctx); + goto retry; + } } EXPORT_SYMBOL(drm_modeset_lock_all); /** * drm_modeset_unlock_all - drop all modeset locks * @dev: device + * + * This function drop all modeset locks taken by drm_modeset_lock_all. */ void drm_modeset_unlock_all(struct drm_device *dev) { - struct drm_crtc *crtc; + struct drm_mode_config *config = &dev->mode_config; + struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx; - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - mutex_unlock(&crtc->mutex); + if (WARN_ON(!ctx)) + return; + + config->acquire_ctx = NULL; + drm_modeset_drop_locks(ctx); + drm_modeset_acquire_fini(ctx); + + kfree(ctx); mutex_unlock(&dev->mode_config.mutex); } @@ -74,6 +117,8 @@ EXPORT_SYMBOL(drm_modeset_unlock_all); /** * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked * @dev: device + * + * Useful as a debug assert. */ void drm_warn_on_modeset_not_all_locked(struct drm_device *dev) { @@ -84,8 +129,9 @@ void drm_warn_on_modeset_not_all_locked(struct drm_device *dev) return; list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - WARN_ON(!mutex_is_locked(&crtc->mutex)); + WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); + WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); } EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked); @@ -114,6 +160,13 @@ static const struct drm_prop_enum_list drm_dpms_enum_list[] = DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list) +static const struct drm_prop_enum_list drm_plane_type_enum_list[] = +{ + { DRM_PLANE_TYPE_OVERLAY, "Overlay" }, + { DRM_PLANE_TYPE_PRIMARY, "Primary" }, + { DRM_PLANE_TYPE_CURSOR, "Cursor" }, +}; + /* * Optional properties */ @@ -202,6 +255,7 @@ static struct drm_conn_prop_enum_list drm_connector_enum_list[] = { DRM_MODE_CONNECTOR_TV, "TV" }, { DRM_MODE_CONNECTOR_eDP, "eDP" }, { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" }, + { DRM_MODE_CONNECTOR_DSI, "DSI" }, }; static const struct drm_prop_enum_list drm_encoder_enum_list[] = @@ -211,6 +265,18 @@ static const struct drm_prop_enum_list drm_encoder_enum_list[] = { DRM_MODE_ENCODER_LVDS, "LVDS" }, { DRM_MODE_ENCODER_TVDAC, "TV" }, { DRM_MODE_ENCODER_VIRTUAL, "Virtual" }, + { DRM_MODE_ENCODER_DSI, "DSI" }, + { DRM_MODE_ENCODER_DPMST, "DP MST" }, +}; + +static const struct drm_prop_enum_list drm_subpixel_enum_list[] = +{ + { SubPixelUnknown, "Unknown" }, + { SubPixelHorizontalRGB, "Horizontal RGB" }, + { SubPixelHorizontalBGR, "Horizontal BGR" }, + { SubPixelVerticalRGB, "Vertical RGB" }, + { SubPixelVerticalBGR, "Vertical BGR" }, + { SubPixelNone, "None" }, }; void drm_connector_ida_init(void) @@ -229,28 +295,13 @@ void drm_connector_ida_destroy(void) ida_destroy(&drm_connector_enum_list[i].ida); } -const char *drm_get_encoder_name(const struct drm_encoder *encoder) -{ - static char buf[32]; - - snprintf(buf, 32, "%s-%d", - drm_encoder_enum_list[encoder->encoder_type].name, - encoder->base.id); - return buf; -} -EXPORT_SYMBOL(drm_get_encoder_name); - -const char *drm_get_connector_name(const struct drm_connector *connector) -{ - static char buf[32]; - - snprintf(buf, 32, "%s-%d", - drm_connector_enum_list[connector->connector_type].name, - connector->connector_type_id); - return buf; -} -EXPORT_SYMBOL(drm_get_connector_name); - +/** + * drm_get_connector_status_name - return a string for connector status + * @status: connector status to compute name of + * + * In contrast to the other drm_get_*_name functions this one here returns a + * const pointer and hence is threadsafe. + */ const char *drm_get_connector_status_name(enum drm_connector_status status) { if (status == connector_status_connected) @@ -262,11 +313,33 @@ const char *drm_get_connector_status_name(enum drm_connector_status status) } EXPORT_SYMBOL(drm_get_connector_status_name); +/** + * drm_get_subpixel_order_name - return a string for a given subpixel enum + * @order: enum of subpixel_order + * + * Note you could abuse this and return something out of bounds, but that + * would be a caller error. No unscrubbed user data should make it here. + */ +const char *drm_get_subpixel_order_name(enum subpixel_order order) +{ + return drm_subpixel_enum_list[order].name; +} +EXPORT_SYMBOL(drm_get_subpixel_order_name); + static char printable_char(int c) { return isascii(c) && isprint(c) ? c : '?'; } +/** + * drm_get_format_name - return a string for drm fourcc format + * @format: format to compute name of + * + * Note that the buffer used by this function is globally shared and owned by + * the function itself. + * + * FIXME: This isn't really multithreading safe. + */ const char *drm_get_format_name(uint32_t format) { static char buf[32]; @@ -291,14 +364,16 @@ EXPORT_SYMBOL(drm_get_format_name); * @obj_type: object type * * Create a unique identifier based on @ptr in @dev's identifier space. Used - * for tracking modes, CRTCs and connectors. + * for tracking modes, CRTCs and connectors. Note that despite the _get postfix + * modeset identifiers are _not_ reference counted. Hence don't use this for + * reference counted modeset objects like framebuffers. * - * RETURNS: + * Returns: * New unique (relative to other objects in @dev) integer identifier for the * object. */ -static int drm_mode_object_get(struct drm_device *dev, - struct drm_mode_object *obj, uint32_t obj_type) +int drm_mode_object_get(struct drm_device *dev, + struct drm_mode_object *obj, uint32_t obj_type) { int ret; @@ -322,16 +397,33 @@ static int drm_mode_object_get(struct drm_device *dev, * @dev: DRM device * @object: object to free * - * Free @id from @dev's unique identifier pool. + * Free @id from @dev's unique identifier pool. Note that despite the _get + * postfix modeset identifiers are _not_ reference counted. Hence don't use this + * for reference counted modeset objects like framebuffers. */ -static void drm_mode_object_put(struct drm_device *dev, - struct drm_mode_object *object) +void drm_mode_object_put(struct drm_device *dev, + struct drm_mode_object *object) { mutex_lock(&dev->mode_config.idr_mutex); idr_remove(&dev->mode_config.crtc_idr, object->id); mutex_unlock(&dev->mode_config.idr_mutex); } +static struct drm_mode_object *_object_find(struct drm_device *dev, + uint32_t id, uint32_t type) +{ + struct drm_mode_object *obj = NULL; + + mutex_lock(&dev->mode_config.idr_mutex); + obj = idr_find(&dev->mode_config.crtc_idr, id); + if (!obj || (type != DRM_MODE_OBJECT_ANY && obj->type != type) || + (obj->id != id)) + obj = NULL; + mutex_unlock(&dev->mode_config.idr_mutex); + + return obj; +} + /** * drm_mode_object_find - look up a drm object with static lifetime * @dev: drm device @@ -339,7 +431,9 @@ static void drm_mode_object_put(struct drm_device *dev, * @type: type of the mode object * * Note that framebuffers cannot be looked up with this functions - since those - * are reference counted, they need special treatment. + * are reference counted, they need special treatment. Even with + * DRM_MODE_OBJECT_ANY (although that will simply return NULL + * rather than WARN_ON()). */ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type) @@ -349,13 +443,10 @@ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, /* Framebuffers are reference counted and need their own lookup * function.*/ WARN_ON(type == DRM_MODE_OBJECT_FB); - - mutex_lock(&dev->mode_config.idr_mutex); - obj = idr_find(&dev->mode_config.crtc_idr, id); - if (!obj || (obj->type != type) || (obj->id != id)) + obj = _object_find(dev, id, type); + /* don't leak out unref'd fb's */ + if (obj && (obj->type == DRM_MODE_OBJECT_FB)) obj = NULL; - mutex_unlock(&dev->mode_config.idr_mutex); - return obj; } EXPORT_SYMBOL(drm_mode_object_find); @@ -375,7 +466,7 @@ EXPORT_SYMBOL(drm_mode_object_find); * since all the fb attributes are invariant over its lifetime, no further * locking but only correct reference counting is required. * - * RETURNS: + * Returns: * Zero on success, error code on failure. */ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, @@ -436,7 +527,7 @@ static struct drm_framebuffer *__drm_framebuffer_lookup(struct drm_device *dev, * * If successful, this grabs an additional reference to the framebuffer - * callers need to make sure to eventually unreference the returned framebuffer - * again. + * again, using @drm_framebuffer_unreference. */ struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, uint32_t id) @@ -461,7 +552,7 @@ EXPORT_SYMBOL(drm_framebuffer_lookup); */ void drm_framebuffer_unreference(struct drm_framebuffer *fb) { - DRM_DEBUG("FB ID: %d\n", fb->base.id); + DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount)); kref_put(&fb->refcount, drm_framebuffer_free); } EXPORT_SYMBOL(drm_framebuffer_unreference); @@ -469,10 +560,12 @@ EXPORT_SYMBOL(drm_framebuffer_unreference); /** * drm_framebuffer_reference - incr the fb refcnt * @fb: framebuffer + * + * This functions increments the fb's refcount. */ void drm_framebuffer_reference(struct drm_framebuffer *fb) { - DRM_DEBUG("FB ID: %d\n", fb->base.id); + DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount)); kref_get(&fb->refcount); } EXPORT_SYMBOL(drm_framebuffer_reference); @@ -484,7 +577,7 @@ static void drm_framebuffer_free_bug(struct kref *kref) static void __drm_framebuffer_unreference(struct drm_framebuffer *fb) { - DRM_DEBUG("FB ID: %d\n", fb->base.id); + DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount)); kref_put(&fb->refcount, drm_framebuffer_free_bug); } @@ -525,8 +618,9 @@ EXPORT_SYMBOL(drm_framebuffer_unregister_private); * drm_framebuffer_cleanup - remove a framebuffer object * @fb: framebuffer to remove * - * Cleanup references to a user-created framebuffer. This function is intended - * to be used from the drivers ->destroy callback. + * Cleanup framebuffer. This function is intended to be used from the drivers + * ->destroy callback. It can also be used to clean up driver private + * framebuffers embedded into a larger structure. * * Note that this function does not remove the fb from active usuage - if it is * still used anywhere, hilarity can ensue since userspace could call getfb on @@ -589,7 +683,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) drm_modeset_lock_all(dev); /* remove from any CRTC */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - if (crtc->fb == fb) { + if (crtc->primary->fb == fb) { /* should turn off the crtc */ memset(&set, 0, sizeof(struct drm_mode_set)); set.crtc = crtc; @@ -611,20 +705,28 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) } EXPORT_SYMBOL(drm_framebuffer_remove); +DEFINE_WW_CLASS(crtc_ww_class); + /** - * drm_crtc_init - Initialise a new CRTC object + * drm_crtc_init_with_planes - Initialise a new CRTC object with + * specified primary and cursor planes. * @dev: DRM device * @crtc: CRTC object to init + * @primary: Primary plane for CRTC + * @cursor: Cursor plane for CRTC * @funcs: callbacks for the new CRTC * * Inits a new object created as base part of a driver crtc object. * - * RETURNS: + * Returns: * Zero on success, error code on failure. */ -int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, - const struct drm_crtc_funcs *funcs) +int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, + struct drm_plane *primary, + void *cursor, + const struct drm_crtc_funcs *funcs) { + struct drm_mode_config *config = &dev->mode_config; int ret; crtc->dev = dev; @@ -632,8 +734,9 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, crtc->invert_dimensions = false; drm_modeset_lock_all(dev); - mutex_init(&crtc->mutex); - mutex_lock_nest_lock(&crtc->mutex, &dev->mode_config.mutex); + drm_modeset_lock_init(&crtc->mutex); + /* dropped by _unlock_all(): */ + drm_modeset_lock(&crtc->mutex, config->acquire_ctx); ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); if (ret) @@ -641,15 +744,19 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, crtc->base.properties = &crtc->properties; - list_add_tail(&crtc->head, &dev->mode_config.crtc_list); - dev->mode_config.num_crtc++; + list_add_tail(&crtc->head, &config->crtc_list); + config->num_crtc++; + + crtc->primary = primary; + if (primary) + primary->possible_crtcs = 1 << drm_crtc_index(crtc); out: drm_modeset_unlock_all(dev); return ret; } -EXPORT_SYMBOL(drm_crtc_init); +EXPORT_SYMBOL(drm_crtc_init_with_planes); /** * drm_crtc_cleanup - Clean up the core crtc usage @@ -666,6 +773,8 @@ void drm_crtc_cleanup(struct drm_crtc *crtc) kfree(crtc->gamma_store); crtc->gamma_store = NULL; + drm_modeset_lock_fini(&crtc->mutex); + drm_mode_object_put(dev, &crtc->base); list_del(&crtc->head); dev->mode_config.num_crtc--; @@ -673,18 +782,27 @@ void drm_crtc_cleanup(struct drm_crtc *crtc) EXPORT_SYMBOL(drm_crtc_cleanup); /** - * drm_mode_probed_add - add a mode to a connector's probed mode list - * @connector: connector the new mode - * @mode: mode data + * drm_crtc_index - find the index of a registered CRTC + * @crtc: CRTC to find index for * - * Add @mode to @connector's mode list for later use. + * Given a registered CRTC, return the index of that CRTC within a DRM + * device's list of CRTCs. */ -void drm_mode_probed_add(struct drm_connector *connector, - struct drm_display_mode *mode) +unsigned int drm_crtc_index(struct drm_crtc *crtc) { - list_add_tail(&mode->head, &connector->probed_modes); + unsigned int index = 0; + struct drm_crtc *tmp; + + list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) { + if (tmp == crtc) + return index; + + index++; + } + + BUG(); } -EXPORT_SYMBOL(drm_mode_probed_add); +EXPORT_SYMBOL(drm_crtc_index); /* * drm_mode_remove - remove and free a mode @@ -710,7 +828,7 @@ static void drm_mode_remove(struct drm_connector *connector, * Initialises a preallocated connector. Connectors should be * subclassed as part of driver connector objects. * - * RETURNS: + * Returns: * Zero on success, error code on failure. */ int drm_connector_init(struct drm_device *dev, @@ -726,7 +844,7 @@ int drm_connector_init(struct drm_device *dev, ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR); if (ret) - goto out; + goto out_unlock; connector->base.properties = &connector->properties; connector->dev = dev; @@ -736,9 +854,17 @@ int drm_connector_init(struct drm_device *dev, ida_simple_get(connector_ida, 1, 0, GFP_KERNEL); if (connector->connector_type_id < 0) { ret = connector->connector_type_id; - drm_mode_object_put(dev, &connector->base); - goto out; + goto out_put; } + connector->name = + kasprintf(GFP_KERNEL, "%s-%d", + drm_connector_enum_list[connector_type].name, + connector->connector_type_id); + if (!connector->name) { + ret = -ENOMEM; + goto out_put; + } + INIT_LIST_HEAD(&connector->probed_modes); INIT_LIST_HEAD(&connector->modes); connector->edid_blob_ptr = NULL; @@ -755,7 +881,11 @@ int drm_connector_init(struct drm_device *dev, drm_object_attach_property(&connector->base, dev->mode_config.dpms_property, 0); - out: +out_put: + if (ret) + drm_mode_object_put(dev, &connector->base); + +out_unlock: drm_modeset_unlock_all(dev); return ret; @@ -783,11 +913,21 @@ void drm_connector_cleanup(struct drm_connector *connector) connector->connector_type_id); drm_mode_object_put(dev, &connector->base); + kfree(connector->name); + connector->name = NULL; list_del(&connector->head); dev->mode_config.num_connector--; } EXPORT_SYMBOL(drm_connector_cleanup); +/** + * drm_connector_unplug_all - unregister connector userspace interfaces + * @dev: drm device + * + * This function unregisters all connector userspace interfaces in sysfs. Should + * be call when the device is disconnected, e.g. from an usb driver's + * ->disconnect callback. + */ void drm_connector_unplug_all(struct drm_device *dev) { struct drm_connector *connector; @@ -799,6 +939,18 @@ void drm_connector_unplug_all(struct drm_device *dev) } EXPORT_SYMBOL(drm_connector_unplug_all); +/** + * drm_bridge_init - initialize a drm transcoder/bridge + * @dev: drm device + * @bridge: transcoder/bridge to set up + * @funcs: bridge function table + * + * Initialises a preallocated bridge. Bridges should be + * subclassed as part of driver connector objects. + * + * Returns: + * Zero on success, error code on failure. + */ int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge, const struct drm_bridge_funcs *funcs) { @@ -822,6 +974,12 @@ int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge, } EXPORT_SYMBOL(drm_bridge_init); +/** + * drm_bridge_cleanup - cleans up an initialised bridge + * @bridge: bridge to cleanup + * + * Cleans up the bridge but doesn't free the object. + */ void drm_bridge_cleanup(struct drm_bridge *bridge) { struct drm_device *dev = bridge->dev; @@ -834,6 +992,19 @@ void drm_bridge_cleanup(struct drm_bridge *bridge) } EXPORT_SYMBOL(drm_bridge_cleanup); +/** + * drm_encoder_init - Init a preallocated encoder + * @dev: drm device + * @encoder: the encoder to init + * @funcs: callbacks for this encoder + * @encoder_type: user visible type of the encoder + * + * Initialises a preallocated encoder. Encoder should be + * subclassed as part of driver encoder objects. + * + * Returns: + * Zero on success, error code on failure. + */ int drm_encoder_init(struct drm_device *dev, struct drm_encoder *encoder, const struct drm_encoder_funcs *funcs, @@ -845,27 +1016,46 @@ int drm_encoder_init(struct drm_device *dev, ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); if (ret) - goto out; + goto out_unlock; encoder->dev = dev; encoder->encoder_type = encoder_type; encoder->funcs = funcs; + encoder->name = kasprintf(GFP_KERNEL, "%s-%d", + drm_encoder_enum_list[encoder_type].name, + encoder->base.id); + if (!encoder->name) { + ret = -ENOMEM; + goto out_put; + } list_add_tail(&encoder->head, &dev->mode_config.encoder_list); dev->mode_config.num_encoder++; - out: +out_put: + if (ret) + drm_mode_object_put(dev, &encoder->base); + +out_unlock: drm_modeset_unlock_all(dev); return ret; } EXPORT_SYMBOL(drm_encoder_init); +/** + * drm_encoder_cleanup - cleans up an initialised encoder + * @encoder: encoder to cleanup + * + * Cleans up the encoder but doesn't free the object. + */ void drm_encoder_cleanup(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; drm_modeset_lock_all(dev); drm_mode_object_put(dev, &encoder->base); + kfree(encoder->name); + encoder->name = NULL; list_del(&encoder->head); dev->mode_config.num_encoder--; drm_modeset_unlock_all(dev); @@ -873,25 +1063,25 @@ void drm_encoder_cleanup(struct drm_encoder *encoder) EXPORT_SYMBOL(drm_encoder_cleanup); /** - * drm_plane_init - Initialise a new plane object + * drm_universal_plane_init - Initialize a new universal plane object * @dev: DRM device * @plane: plane object to init * @possible_crtcs: bitmask of possible CRTCs * @funcs: callbacks for the new plane * @formats: array of supported formats (%DRM_FORMAT_*) * @format_count: number of elements in @formats - * @priv: plane is private (hidden from userspace)? + * @type: type of plane (overlay, primary, cursor) * - * Inits a new object created as base part of a driver plane object. + * Initializes a plane object of type @type. * - * RETURNS: + * Returns: * Zero on success, error code on failure. */ -int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, - unsigned long possible_crtcs, - const struct drm_plane_funcs *funcs, - const uint32_t *formats, uint32_t format_count, - bool priv) +int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, + unsigned long possible_crtcs, + const struct drm_plane_funcs *funcs, + const uint32_t *formats, uint32_t format_count, + enum drm_plane_type type) { int ret; @@ -916,23 +1106,53 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, memcpy(plane->format_types, formats, format_count * sizeof(uint32_t)); plane->format_count = format_count; plane->possible_crtcs = possible_crtcs; + plane->type = type; - /* private planes are not exposed to userspace, but depending on - * display hardware, might be convenient to allow sharing programming - * for the scanout engine with the crtc implementation. - */ - if (!priv) { - list_add_tail(&plane->head, &dev->mode_config.plane_list); - dev->mode_config.num_plane++; - } else { - INIT_LIST_HEAD(&plane->head); - } + list_add_tail(&plane->head, &dev->mode_config.plane_list); + dev->mode_config.num_total_plane++; + if (plane->type == DRM_PLANE_TYPE_OVERLAY) + dev->mode_config.num_overlay_plane++; + + drm_object_attach_property(&plane->base, + dev->mode_config.plane_type_property, + plane->type); out: drm_modeset_unlock_all(dev); return ret; } +EXPORT_SYMBOL(drm_universal_plane_init); + +/** + * drm_plane_init - Initialize a legacy plane + * @dev: DRM device + * @plane: plane object to init + * @possible_crtcs: bitmask of possible CRTCs + * @funcs: callbacks for the new plane + * @formats: array of supported formats (%DRM_FORMAT_*) + * @format_count: number of elements in @formats + * @is_primary: plane type (primary vs overlay) + * + * Legacy API to initialize a DRM plane. + * + * New drivers should call drm_universal_plane_init() instead. + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, + unsigned long possible_crtcs, + const struct drm_plane_funcs *funcs, + const uint32_t *formats, uint32_t format_count, + bool is_primary) +{ + enum drm_plane_type type; + + type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; + return drm_universal_plane_init(dev, plane, possible_crtcs, funcs, + formats, format_count, type); +} EXPORT_SYMBOL(drm_plane_init); /** @@ -950,11 +1170,13 @@ void drm_plane_cleanup(struct drm_plane *plane) drm_modeset_lock_all(dev); kfree(plane->format_types); drm_mode_object_put(dev, &plane->base); - /* if not added to a list, it must be a private plane */ - if (!list_empty(&plane->head)) { - list_del(&plane->head); - dev->mode_config.num_plane--; - } + + BUG_ON(list_empty(&plane->head)); + + list_del(&plane->head); + dev->mode_config.num_total_plane--; + if (plane->type == DRM_PLANE_TYPE_OVERLAY) + dev->mode_config.num_overlay_plane--; drm_modeset_unlock_all(dev); } EXPORT_SYMBOL(drm_plane_cleanup); @@ -970,65 +1192,24 @@ EXPORT_SYMBOL(drm_plane_cleanup); */ void drm_plane_force_disable(struct drm_plane *plane) { + struct drm_framebuffer *old_fb = plane->fb; int ret; - if (!plane->fb) + if (!old_fb) return; ret = plane->funcs->disable_plane(plane); - if (ret) + if (ret) { DRM_ERROR("failed to disable plane with busy fb\n"); + return; + } /* disconnect the plane from the fb and crtc: */ - __drm_framebuffer_unreference(plane->fb); + __drm_framebuffer_unreference(old_fb); plane->fb = NULL; plane->crtc = NULL; } EXPORT_SYMBOL(drm_plane_force_disable); -/** - * drm_mode_create - create a new display mode - * @dev: DRM device - * - * Create a new drm_display_mode, give it an ID, and return it. - * - * RETURNS: - * Pointer to new mode on success, NULL on error. - */ -struct drm_display_mode *drm_mode_create(struct drm_device *dev) -{ - struct drm_display_mode *nmode; - - nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); - if (!nmode) - return NULL; - - if (drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE)) { - kfree(nmode); - return NULL; - } - - return nmode; -} -EXPORT_SYMBOL(drm_mode_create); - -/** - * drm_mode_destroy - remove a mode - * @dev: DRM device - * @mode: mode to remove - * - * Free @mode's unique identifier, then free it. - */ -void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) -{ - if (!mode) - return; - - drm_mode_object_put(dev, &mode->base); - - kfree(mode); -} -EXPORT_SYMBOL(drm_mode_destroy); - static int drm_mode_create_standard_connector_properties(struct drm_device *dev) { struct drm_property *edid; @@ -1050,6 +1231,21 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev) return 0; } +static int drm_mode_create_standard_plane_properties(struct drm_device *dev) +{ + struct drm_property *type; + + /* + * Standard properties (apply to all planes) + */ + type = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, + "type", drm_plane_type_enum_list, + ARRAY_SIZE(drm_plane_type_enum_list)); + dev->mode_config.plane_type_property = type; + + return 0; +} + /** * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties * @dev: DRM device @@ -1232,6 +1428,16 @@ static int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *gr return 0; } +void drm_mode_group_destroy(struct drm_mode_group *group) +{ + kfree(group->id_list); + group->id_list = NULL; +} + +/* + * NOTE: Driver's shouldn't ever call drm_mode_group_init_legacy_group - it is + * the drm core's responsibility to set up mode control groups. + */ int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group) { @@ -1301,14 +1507,14 @@ static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, } /** - * drm_crtc_convert_to_umode - convert a modeinfo into a drm_display_mode + * drm_crtc_convert_umode - convert a modeinfo into a drm_display_mode * @out: drm_display_mode to return to the user * @in: drm_mode_modeinfo to use * * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to * the caller. * - * RETURNS: + * Returns: * Zero on success, errno on failure. */ static int drm_crtc_convert_umode(struct drm_display_mode *out, @@ -1317,6 +1523,9 @@ static int drm_crtc_convert_umode(struct drm_display_mode *out, if (in->clock > INT_MAX || in->vrefresh > INT_MAX) return -ERANGE; + if ((in->flags & DRM_MODE_FLAG_3D_MASK) > DRM_MODE_FLAG_3D_MAX) + return -EINVAL; + out->clock = in->clock; out->hdisplay = in->hdisplay; out->hsync_start = in->hsync_start; @@ -1348,7 +1557,7 @@ static int drm_crtc_convert_umode(struct drm_display_mode *out, * * Called by the user via ioctl. * - * RETURNS: + * Returns: * Zero on success, errno on failure. */ int drm_mode_getresources(struct drm_device *dev, void *data, @@ -1401,9 +1610,9 @@ int drm_mode_getresources(struct drm_device *dev, void *data, mutex_unlock(&file_priv->fbs_lock); drm_modeset_lock_all(dev); - mode_group = &file_priv->master->minor->mode_group; - if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { + if (!drm_is_primary_client(file_priv)) { + mode_group = NULL; list_for_each(lh, &dev->mode_config.crtc_list) crtc_count++; @@ -1414,6 +1623,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data, encoder_count++; } else { + mode_group = &file_priv->master->minor->mode_group; crtc_count = mode_group->num_crtcs; connector_count = mode_group->num_connectors; encoder_count = mode_group->num_encoders; @@ -1428,7 +1638,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data, if (card_res->count_crtcs >= crtc_count) { copied = 0; crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr; - if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { + if (!mode_group) { list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); @@ -1455,12 +1665,12 @@ int drm_mode_getresources(struct drm_device *dev, void *data, if (card_res->count_encoders >= encoder_count) { copied = 0; encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr; - if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { + if (!mode_group) { list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { DRM_DEBUG_KMS("[ENCODER:%d:%s]\n", encoder->base.id, - drm_get_encoder_name(encoder)); + encoder->name); if (put_user(encoder->base.id, encoder_id + copied)) { ret = -EFAULT; @@ -1486,13 +1696,13 @@ int drm_mode_getresources(struct drm_device *dev, void *data, if (card_res->count_connectors >= connector_count) { copied = 0; connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr; - if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { + if (!mode_group) { list_for_each_entry(connector, &dev->mode_config.connector_list, head) { DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, - drm_get_connector_name(connector)); + connector->name); if (put_user(connector->base.id, connector_id + copied)) { ret = -EFAULT; @@ -1533,7 +1743,7 @@ out: * * Called by the user via ioctl. * - * RETURNS: + * Returns: * Zero on success, errno on failure. */ int drm_mode_getcrtc(struct drm_device *dev, @@ -1541,7 +1751,6 @@ int drm_mode_getcrtc(struct drm_device *dev, { struct drm_mode_crtc *crtc_resp = data; struct drm_crtc *crtc; - struct drm_mode_object *obj; int ret = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) @@ -1549,19 +1758,17 @@ int drm_mode_getcrtc(struct drm_device *dev, drm_modeset_lock_all(dev); - obj = drm_mode_object_find(dev, crtc_resp->crtc_id, - DRM_MODE_OBJECT_CRTC); - if (!obj) { - ret = -EINVAL; + crtc = drm_crtc_find(dev, crtc_resp->crtc_id); + if (!crtc) { + ret = -ENOENT; goto out; } - crtc = obj_to_crtc(obj); crtc_resp->x = crtc->x; crtc_resp->y = crtc->y; crtc_resp->gamma_size = crtc->gamma_size; - if (crtc->fb) - crtc_resp->fb_id = crtc->fb->base.id; + if (crtc->primary->fb) + crtc_resp->fb_id = crtc->primary->fb->base.id; else crtc_resp->fb_id = 0; @@ -1579,6 +1786,19 @@ out: return ret; } +static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode, + const struct drm_file *file_priv) +{ + /* + * If user-space hasn't configured the driver to expose the stereo 3D + * modes, don't expose them. + */ + if (!file_priv->stereo_allowed && drm_mode_is_stereo(mode)) + return false; + + return true; +} + /** * drm_mode_getconnector - get connector configuration * @dev: drm device for the ioctl @@ -1589,14 +1809,13 @@ out: * * Called by the user via ioctl. * - * RETURNS: + * Returns: * Zero on success, errno on failure. */ int drm_mode_getconnector(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_get_connector *out_resp = data; - struct drm_mode_object *obj; struct drm_connector *connector; struct drm_display_mode *mode; int mode_count = 0; @@ -1620,13 +1839,11 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, mutex_lock(&dev->mode_config.mutex); - obj = drm_mode_object_find(dev, out_resp->connector_id, - DRM_MODE_OBJECT_CONNECTOR); - if (!obj) { - ret = -EINVAL; + connector = drm_connector_find(dev, out_resp->connector_id); + if (!connector) { + ret = -ENOENT; goto out; } - connector = obj_to_connector(obj); props_count = connector->properties.count; @@ -1644,7 +1861,8 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, /* delayed so we get modes regardless of pre-fill_modes state */ list_for_each_entry(mode, &connector->modes, head) - mode_count++; + if (drm_mode_expose_to_userspace(mode, file_priv)) + mode_count++; out_resp->connector_id = connector->base.id; out_resp->connector_type = connector->connector_type; @@ -1653,10 +1871,12 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, out_resp->mm_height = connector->display_info.height_mm; out_resp->subpixel = connector->display_info.subpixel_order; out_resp->connection = connector->status; + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); if (connector->encoder) out_resp->encoder_id = connector->encoder->base.id; else out_resp->encoder_id = 0; + drm_modeset_unlock(&dev->mode_config.connection_mutex); /* * This ioctl is called twice, once to determine how much space is @@ -1666,6 +1886,9 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, copied = 0; mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr; list_for_each_entry(mode, &connector->modes, head) { + if (!drm_mode_expose_to_userspace(mode, file_priv)) + continue; + drm_crtc_convert_to_umode(&u_mode, mode); if (copy_to_user(mode_ptr + copied, &u_mode, sizeof(u_mode))) { @@ -1720,11 +1943,23 @@ out: return ret; } +/** + * drm_mode_getencoder - get encoder configuration + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + * Construct a encoder configuration structure to return to the user. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_getencoder(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_get_encoder *enc_resp = data; - struct drm_mode_object *obj; struct drm_encoder *encoder; int ret = 0; @@ -1732,13 +1967,11 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, return -EINVAL; drm_modeset_lock_all(dev); - obj = drm_mode_object_find(dev, enc_resp->encoder_id, - DRM_MODE_OBJECT_ENCODER); - if (!obj) { - ret = -EINVAL; + encoder = drm_encoder_find(dev, enc_resp->encoder_id); + if (!encoder) { + ret = -ENOENT; goto out; } - encoder = obj_to_encoder(obj); if (encoder->crtc) enc_resp->crtc_id = encoder->crtc->base.id; @@ -1755,21 +1988,27 @@ out: } /** - * drm_mode_getplane_res - get plane info + * drm_mode_getplane_res - enumerate all plane resources * @dev: DRM device * @data: ioctl data * @file_priv: DRM file info * - * Return an plane count and set of IDs. + * Construct a list of plane ids to return to the user. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. */ int drm_mode_getplane_res(struct drm_device *dev, void *data, - struct drm_file *file_priv) + struct drm_file *file_priv) { struct drm_mode_get_plane_res *plane_resp = data; struct drm_mode_config *config; struct drm_plane *plane; uint32_t __user *plane_ptr; int copied = 0, ret = 0; + unsigned num_planes; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; @@ -1777,15 +2016,28 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data, drm_modeset_lock_all(dev); config = &dev->mode_config; + if (file_priv->universal_planes) + num_planes = config->num_total_plane; + else + num_planes = config->num_overlay_plane; + /* * This ioctl is called twice, once to determine how much space is * needed, and the 2nd time to fill it. */ - if (config->num_plane && - (plane_resp->count_planes >= config->num_plane)) { + if (num_planes && + (plane_resp->count_planes >= num_planes)) { plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr; list_for_each_entry(plane, &config->plane_list, head) { + /* + * Unless userspace set the 'universal planes' + * capability bit, only advertise overlays. + */ + if (plane->type != DRM_PLANE_TYPE_OVERLAY && + !file_priv->universal_planes) + continue; + if (put_user(plane->base.id, plane_ptr + copied)) { ret = -EFAULT; goto out; @@ -1793,7 +2045,7 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data, copied++; } } - plane_resp->count_planes = config->num_plane; + plane_resp->count_planes = num_planes; out: drm_modeset_unlock_all(dev); @@ -1801,19 +2053,22 @@ out: } /** - * drm_mode_getplane - get plane info + * drm_mode_getplane - get plane configuration * @dev: DRM device * @data: ioctl data * @file_priv: DRM file info * - * Return plane info, including formats supported, gamma size, any - * current fb, etc. + * Construct a plane configuration structure to return to the user. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. */ int drm_mode_getplane(struct drm_device *dev, void *data, - struct drm_file *file_priv) + struct drm_file *file_priv) { struct drm_mode_get_plane *plane_resp = data; - struct drm_mode_object *obj; struct drm_plane *plane; uint32_t __user *format_ptr; int ret = 0; @@ -1822,13 +2077,11 @@ int drm_mode_getplane(struct drm_device *dev, void *data, return -EINVAL; drm_modeset_lock_all(dev); - obj = drm_mode_object_find(dev, plane_resp->plane_id, - DRM_MODE_OBJECT_PLANE); - if (!obj) { + plane = drm_plane_find(dev, plane_resp->plane_id); + if (!plane) { ret = -ENOENT; goto out; } - plane = obj_to_plane(obj); if (plane->crtc) plane_resp->crtc_id = plane->crtc->base.id; @@ -1866,19 +2119,21 @@ out: } /** - * drm_mode_setplane - set up or tear down an plane + * drm_mode_setplane - configure a plane's configuration * @dev: DRM device * @data: ioctl data* * @file_priv: DRM file info * - * Set plane info, including placement, fb, scaling, and other factors. + * Set plane configuration, including placement, fb, scaling, and other factors. * Or pass a NULL fb to disable. + * + * Returns: + * Zero on success, errno on failure. */ int drm_mode_setplane(struct drm_device *dev, void *data, - struct drm_file *file_priv) + struct drm_file *file_priv) { struct drm_mode_set_plane *plane_req = data; - struct drm_mode_object *obj; struct drm_plane *plane; struct drm_crtc *crtc; struct drm_framebuffer *fb = NULL, *old_fb = NULL; @@ -1893,35 +2148,42 @@ int drm_mode_setplane(struct drm_device *dev, void *data, * First, find the plane, crtc, and fb objects. If not available, * we don't bother to call the driver. */ - obj = drm_mode_object_find(dev, plane_req->plane_id, - DRM_MODE_OBJECT_PLANE); - if (!obj) { + plane = drm_plane_find(dev, plane_req->plane_id); + if (!plane) { DRM_DEBUG_KMS("Unknown plane ID %d\n", plane_req->plane_id); return -ENOENT; } - plane = obj_to_plane(obj); /* No fb means shut it down */ if (!plane_req->fb_id) { drm_modeset_lock_all(dev); old_fb = plane->fb; - plane->funcs->disable_plane(plane); - plane->crtc = NULL; - plane->fb = NULL; + ret = plane->funcs->disable_plane(plane); + if (!ret) { + plane->crtc = NULL; + plane->fb = NULL; + } else { + old_fb = NULL; + } drm_modeset_unlock_all(dev); goto out; } - obj = drm_mode_object_find(dev, plane_req->crtc_id, - DRM_MODE_OBJECT_CRTC); - if (!obj) { + crtc = drm_crtc_find(dev, plane_req->crtc_id); + if (!crtc) { DRM_DEBUG_KMS("Unknown crtc ID %d\n", plane_req->crtc_id); ret = -ENOENT; goto out; } - crtc = obj_to_crtc(obj); + + /* Check whether this plane is usable on this CRTC */ + if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) { + DRM_DEBUG_KMS("Invalid crtc for plane\n"); + ret = -EINVAL; + goto out; + } fb = drm_framebuffer_lookup(dev, plane_req->fb_id); if (!fb) { @@ -1977,16 +2239,18 @@ int drm_mode_setplane(struct drm_device *dev, void *data, } drm_modeset_lock_all(dev); + old_fb = plane->fb; ret = plane->funcs->update_plane(plane, crtc, fb, plane_req->crtc_x, plane_req->crtc_y, plane_req->crtc_w, plane_req->crtc_h, plane_req->src_x, plane_req->src_y, plane_req->src_w, plane_req->src_h); if (!ret) { - old_fb = plane->fb; plane->crtc = crtc; plane->fb = fb; fb = NULL; + } else { + old_fb = NULL; } drm_modeset_unlock_all(dev); @@ -2005,6 +2269,9 @@ out: * * This is a little helper to wrap internal calls to the ->set_config driver * interface. The only thing it adds is correct refcounting dance. + * + * Returns: + * Zero on success, errno on failure. */ int drm_mode_set_config_internal(struct drm_mode_set *set) { @@ -2019,19 +2286,19 @@ int drm_mode_set_config_internal(struct drm_mode_set *set) * crtcs. Atomic modeset will have saner semantics ... */ list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) - tmp->old_fb = tmp->fb; + tmp->old_fb = tmp->primary->fb; fb = set->fb; ret = crtc->funcs->set_config(set); if (ret == 0) { - /* crtc->fb must be updated by ->set_config, enforces this. */ - WARN_ON(fb != crtc->fb); + crtc->primary->crtc = crtc; + crtc->primary->fb = fb; } list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) { - if (tmp->fb) - drm_framebuffer_reference(tmp->fb); + if (tmp->primary->fb) + drm_framebuffer_reference(tmp->primary->fb); if (tmp->old_fb) drm_framebuffer_unreference(tmp->old_fb); } @@ -2041,6 +2308,51 @@ int drm_mode_set_config_internal(struct drm_mode_set *set) EXPORT_SYMBOL(drm_mode_set_config_internal); /** + * drm_crtc_check_viewport - Checks that a framebuffer is big enough for the + * CRTC viewport + * @crtc: CRTC that framebuffer will be displayed on + * @x: x panning + * @y: y panning + * @mode: mode that framebuffer will be displayed under + * @fb: framebuffer to check size of + */ +int drm_crtc_check_viewport(const struct drm_crtc *crtc, + int x, int y, + const struct drm_display_mode *mode, + const struct drm_framebuffer *fb) + +{ + int hdisplay, vdisplay; + + hdisplay = mode->hdisplay; + vdisplay = mode->vdisplay; + + if (drm_mode_is_stereo(mode)) { + struct drm_display_mode adjusted = *mode; + + drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE); + hdisplay = adjusted.crtc_hdisplay; + vdisplay = adjusted.crtc_vdisplay; + } + + if (crtc->invert_dimensions) + swap(hdisplay, vdisplay); + + if (hdisplay > fb->width || + vdisplay > fb->height || + x > fb->width - hdisplay || + y > fb->height - vdisplay) { + DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n", + fb->width, fb->height, hdisplay, vdisplay, x, y, + crtc->invert_dimensions ? " (inverted)" : ""); + return -ENOSPC; + } + + return 0; +} +EXPORT_SYMBOL(drm_crtc_check_viewport); + +/** * drm_mode_setcrtc - set CRTC configuration * @dev: drm device for the ioctl * @data: data pointer for the ioctl @@ -2050,7 +2362,7 @@ EXPORT_SYMBOL(drm_mode_set_config_internal); * * Called by the user via ioctl. * - * RETURNS: + * Returns: * Zero on success, errno on failure. */ int drm_mode_setcrtc(struct drm_device *dev, void *data, @@ -2058,7 +2370,6 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, { struct drm_mode_config *config = &dev->mode_config; struct drm_mode_crtc *crtc_req = data; - struct drm_mode_object *obj; struct drm_crtc *crtc; struct drm_connector **connector_set = NULL, *connector; struct drm_framebuffer *fb = NULL; @@ -2076,27 +2387,24 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, return -ERANGE; drm_modeset_lock_all(dev); - obj = drm_mode_object_find(dev, crtc_req->crtc_id, - DRM_MODE_OBJECT_CRTC); - if (!obj) { + crtc = drm_crtc_find(dev, crtc_req->crtc_id); + if (!crtc) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id); - ret = -EINVAL; + ret = -ENOENT; goto out; } - crtc = obj_to_crtc(obj); DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); if (crtc_req->mode_valid) { - int hdisplay, vdisplay; /* If we have a mode we need a framebuffer. */ /* If we pass -1, set the mode with the currently bound fb */ if (crtc_req->fb_id == -1) { - if (!crtc->fb) { + if (!crtc->primary->fb) { DRM_DEBUG_KMS("CRTC doesn't have current FB\n"); ret = -EINVAL; goto out; } - fb = crtc->fb; + fb = crtc->primary->fb; /* Make refcounting symmetric with the lookup path. */ drm_framebuffer_reference(fb); } else { @@ -2104,7 +2412,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, if (!fb) { DRM_DEBUG_KMS("Unknown FB ID%d\n", crtc_req->fb_id); - ret = -EINVAL; + ret = -ENOENT; goto out; } } @@ -2123,23 +2431,11 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); - hdisplay = mode->hdisplay; - vdisplay = mode->vdisplay; - - if (crtc->invert_dimensions) - swap(hdisplay, vdisplay); - - if (hdisplay > fb->width || - vdisplay > fb->height || - crtc_req->x > fb->width - hdisplay || - crtc_req->y > fb->height - vdisplay) { - DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n", - fb->width, fb->height, - hdisplay, vdisplay, crtc_req->x, crtc_req->y, - crtc->invert_dimensions ? " (inverted)" : ""); - ret = -ENOSPC; + ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y, + mode, fb); + if (ret) goto out; - } + } if (crtc_req->count_connectors == 0 && mode) { @@ -2179,18 +2475,16 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, goto out; } - obj = drm_mode_object_find(dev, out_id, - DRM_MODE_OBJECT_CONNECTOR); - if (!obj) { + connector = drm_connector_find(dev, out_id); + if (!connector) { DRM_DEBUG_KMS("Connector id %d unknown\n", out_id); - ret = -EINVAL; + ret = -ENOENT; goto out; } - connector = obj_to_connector(obj); DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, - drm_get_connector_name(connector)); + connector->name); connector_set[i] = connector; } @@ -2219,7 +2513,6 @@ static int drm_mode_cursor_common(struct drm_device *dev, struct drm_mode_cursor2 *req, struct drm_file *file_priv) { - struct drm_mode_object *obj; struct drm_crtc *crtc; int ret = 0; @@ -2229,14 +2522,13 @@ static int drm_mode_cursor_common(struct drm_device *dev, if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) return -EINVAL; - obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC); - if (!obj) { + crtc = drm_crtc_find(dev, req->crtc_id); + if (!crtc) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); - return -EINVAL; + return -ENOENT; } - crtc = obj_to_crtc(obj); - mutex_lock(&crtc->mutex); + drm_modeset_lock(&crtc->mutex, NULL); if (req->flags & DRM_MODE_CURSOR_BO) { if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) { ret = -ENXIO; @@ -2260,13 +2552,28 @@ static int drm_mode_cursor_common(struct drm_device *dev, } } out: - mutex_unlock(&crtc->mutex); + drm_modeset_unlock(&crtc->mutex); return ret; } + + +/** + * drm_mode_cursor_ioctl - set CRTC's cursor configuration + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + * Set the cursor configuration based on user request. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_cursor_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) + void *data, struct drm_file *file_priv) { struct drm_mode_cursor *req = data; struct drm_mode_cursor2 new_req; @@ -2277,6 +2584,21 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, return drm_mode_cursor_common(dev, &new_req, file_priv); } +/** + * drm_mode_cursor2_ioctl - set CRTC's cursor configuration + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + * Set the cursor configuration based on user request. This implements the 2nd + * version of the cursor ioctl, which allows userspace to additionally specify + * the hotspot of the pointer. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_cursor2_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -2284,7 +2606,14 @@ int drm_mode_cursor2_ioctl(struct drm_device *dev, return drm_mode_cursor_common(dev, req, file_priv); } -/* Original addfb only supported RGB formats, so figure out which one */ +/** + * drm_mode_legacy_fb_format - compute drm fourcc code from legacy description + * @bpp: bits per pixels + * @depth: bit depth per pixel + * + * Computes a drm fourcc pixel format code for the given @bpp/@depth values. + * Useful in fbdev emulation code, since that deals in those values. + */ uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth) { uint32_t fmt; @@ -2326,11 +2655,12 @@ EXPORT_SYMBOL(drm_mode_legacy_fb_format); * @data: data pointer for the ioctl * @file_priv: drm file for the ioctl call * - * Add a new FB to the specified CRTC, given a user request. + * Add a new FB to the specified CRTC, given a user request. This is the + * original addfb ioclt which only supported RGB formats. * * Called by the user via ioctl. * - * RETURNS: + * Returns: * Zero on success, errno on failure. */ int drm_mode_addfb(struct drm_device *dev, @@ -2441,6 +2771,8 @@ static int format_check(const struct drm_mode_fb_cmd2 *r) case DRM_FORMAT_YVU444: return 0; default: + DRM_DEBUG_KMS("invalid pixel format %s\n", + drm_get_format_name(r->pixel_format)); return -EINVAL; } } @@ -2501,11 +2833,13 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) * @data: data pointer for the ioctl * @file_priv: drm file for the ioctl call * - * Add a new FB to the specified CRTC, given a user request with format. + * Add a new FB to the specified CRTC, given a user request with format. This is + * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers + * and uses fourcc codes as pixel format specifiers. * * Called by the user via ioctl. * - * RETURNS: + * Returns: * Zero on success, errno on failure. */ int drm_mode_addfb2(struct drm_device *dev, @@ -2565,7 +2899,7 @@ int drm_mode_addfb2(struct drm_device *dev, * * Called by the user via ioctl. * - * RETURNS: + * Returns: * Zero on success, errno on failure. */ int drm_mode_rmfb(struct drm_device *dev, @@ -2606,7 +2940,7 @@ fail_lookup: mutex_unlock(&dev->mode_config.fb_lock); mutex_unlock(&file_priv->fbs_lock); - return -EINVAL; + return -ENOENT; } /** @@ -2619,7 +2953,7 @@ fail_lookup: * * Called by the user via ioctl. * - * RETURNS: + * Returns: * Zero on success, errno on failure. */ int drm_mode_getfb(struct drm_device *dev, @@ -2634,7 +2968,7 @@ int drm_mode_getfb(struct drm_device *dev, fb = drm_framebuffer_lookup(dev, r->fb_id); if (!fb) - return -EINVAL; + return -ENOENT; r->height = fb->height; r->width = fb->width; @@ -2642,7 +2976,8 @@ int drm_mode_getfb(struct drm_device *dev, r->bpp = fb->bits_per_pixel; r->pitch = fb->pitches[0]; if (fb->funcs->create_handle) { - if (file_priv->is_master || capable(CAP_SYS_ADMIN)) { + if (file_priv->is_master || capable(CAP_SYS_ADMIN) || + drm_is_control_client(file_priv)) { ret = fb->funcs->create_handle(fb, file_priv, &r->handle); } else { @@ -2663,6 +2998,25 @@ int drm_mode_getfb(struct drm_device *dev, return ret; } +/** + * drm_mode_dirtyfb_ioctl - flush frontbuffer rendering on an FB + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + * Lookup the FB and flush out the damaged area supplied by userspace as a clip + * rectangle list. Generic userspace which does frontbuffer rendering must call + * this ioctl to flush out the changes on manual-update display outputs, e.g. + * usb display-link, mipi manual update panels or edp panel self refresh modes. + * + * Modesetting drivers which always update the frontbuffer do not need to + * implement the corresponding ->dirty framebuffer callback. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -2679,7 +3033,7 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, fb = drm_framebuffer_lookup(dev, r->fb_id); if (!fb) - return -EINVAL; + return -ENOENT; num_clips = r->num_clips; clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr; @@ -2717,10 +3071,8 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, } if (fb->funcs->dirty) { - drm_modeset_lock_all(dev); ret = fb->funcs->dirty(fb, file_priv, flags, r->color, clips, num_clips); - drm_modeset_unlock_all(dev); } else { ret = -ENOSYS; } @@ -2742,7 +3094,7 @@ out_err1: * * Called by the user via ioctl. * - * RETURNS: + * Returns: * Zero on success, errno on failure. */ void drm_fb_release(struct drm_file *priv) @@ -2766,6 +3118,20 @@ void drm_fb_release(struct drm_file *priv) mutex_unlock(&priv->fbs_lock); } +/** + * drm_property_create - create a new property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * @num_values: number of pre-defined values + * + * This creates a new generic drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy. + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ struct drm_property *drm_property_create(struct drm_device *dev, int flags, const char *name, int num_values) { @@ -2776,6 +3142,8 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags, if (!property) return NULL; + property->dev = dev; + if (num_values) { property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL); if (!property->values) @@ -2796,6 +3164,9 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags, } list_add_tail(&property->head, &dev->mode_config.property_list); + + WARN_ON(!drm_property_type_valid(property)); + return property; fail: kfree(property->values); @@ -2804,6 +3175,24 @@ fail: } EXPORT_SYMBOL(drm_property_create); +/** + * drm_property_create - create a new enumeration property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * @props: enumeration lists with property values + * @num_values: number of pre-defined values + * + * This creates a new generic drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy. + * + * Userspace is only allowed to set one of the predefined values for enumeration + * properties. + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, const char *name, const struct drm_prop_enum_list *props, @@ -2832,6 +3221,24 @@ struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, } EXPORT_SYMBOL(drm_property_create_enum); +/** + * drm_property_create - create a new bitmask property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * @props: enumeration lists with property bitflags + * @num_values: number of pre-defined values + * + * This creates a new generic drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy. + * + * Compared to plain enumeration properties userspace is allowed to set any + * or'ed together combination of the predefined property bitflag values + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ struct drm_property *drm_property_create_bitmask(struct drm_device *dev, int flags, const char *name, const struct drm_prop_enum_list *props, @@ -2860,14 +3267,12 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev, } EXPORT_SYMBOL(drm_property_create_bitmask); -struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, - const char *name, +static struct drm_property *property_create_range(struct drm_device *dev, + int flags, const char *name, uint64_t min, uint64_t max) { struct drm_property *property; - flags |= DRM_MODE_PROP_RANGE; - property = drm_property_create(dev, flags, name, 2); if (!property) return NULL; @@ -2877,21 +3282,90 @@ struct drm_property *drm_property_create_range(struct drm_device *dev, int flags return property; } + +/** + * drm_property_create - create a new ranged property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * @min: minimum value of the property + * @max: maximum value of the property + * + * This creates a new generic drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy. + * + * Userspace is allowed to set any interger value in the (min, max) range + * inclusive. + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ +struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, + const char *name, + uint64_t min, uint64_t max) +{ + return property_create_range(dev, DRM_MODE_PROP_RANGE | flags, + name, min, max); +} EXPORT_SYMBOL(drm_property_create_range); +struct drm_property *drm_property_create_signed_range(struct drm_device *dev, + int flags, const char *name, + int64_t min, int64_t max) +{ + return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags, + name, I642U64(min), I642U64(max)); +} +EXPORT_SYMBOL(drm_property_create_signed_range); + +struct drm_property *drm_property_create_object(struct drm_device *dev, + int flags, const char *name, uint32_t type) +{ + struct drm_property *property; + + flags |= DRM_MODE_PROP_OBJECT; + + property = drm_property_create(dev, flags, name, 1); + if (!property) + return NULL; + + property->values[0] = type; + + return property; +} +EXPORT_SYMBOL(drm_property_create_object); + +/** + * drm_property_add_enum - add a possible value to an enumeration property + * @property: enumeration property to change + * @index: index of the new enumeration + * @value: value of the new enumeration + * @name: symbolic name of the new enumeration + * + * This functions adds enumerations to a property. + * + * It's use is deprecated, drivers should use one of the more specific helpers + * to directly create the property with all enumerations already attached. + * + * Returns: + * Zero on success, error code on failure. + */ int drm_property_add_enum(struct drm_property *property, int index, uint64_t value, const char *name) { struct drm_property_enum *prop_enum; - if (!(property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK))) + if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) || + drm_property_type_is(property, DRM_MODE_PROP_BITMASK))) return -EINVAL; /* * Bitmask enum properties have the additional constraint of values * from 0 to 63 */ - if ((property->flags & DRM_MODE_PROP_BITMASK) && (value > 63)) + if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) && + (value > 63)) return -EINVAL; if (!list_empty(&property->enum_blob_list)) { @@ -2918,6 +3392,14 @@ int drm_property_add_enum(struct drm_property *property, int index, } EXPORT_SYMBOL(drm_property_add_enum); +/** + * drm_property_destroy - destroy a drm property + * @dev: drm device + * @property: property to destry + * + * This function frees a property including any attached resources like + * enumeration values. + */ void drm_property_destroy(struct drm_device *dev, struct drm_property *property) { struct drm_property_enum *prop_enum, *pt; @@ -2935,6 +3417,16 @@ void drm_property_destroy(struct drm_device *dev, struct drm_property *property) } EXPORT_SYMBOL(drm_property_destroy); +/** + * drm_object_attach_property - attach a property to a modeset object + * @obj: drm modeset object + * @property: property to attach + * @init_val: initial value of the property + * + * This attaches the given property to the modeset object with the given initial + * value. Currently this function cannot fail since the properties are stored in + * a statically sized array. + */ void drm_object_attach_property(struct drm_mode_object *obj, struct drm_property *property, uint64_t init_val) @@ -2955,6 +3447,19 @@ void drm_object_attach_property(struct drm_mode_object *obj, } EXPORT_SYMBOL(drm_object_attach_property); +/** + * drm_object_property_set_value - set the value of a property + * @obj: drm mode object to set property value for + * @property: property to set + * @val: value the property should be set to + * + * This functions sets a given property on a given object. This function only + * changes the software state of the property, it does not call into the + * driver's ->set_property callback. + * + * Returns: + * Zero on success, error code on failure. + */ int drm_object_property_set_value(struct drm_mode_object *obj, struct drm_property *property, uint64_t val) { @@ -2971,6 +3476,20 @@ int drm_object_property_set_value(struct drm_mode_object *obj, } EXPORT_SYMBOL(drm_object_property_set_value); +/** + * drm_object_property_get_value - retrieve the value of a property + * @obj: drm mode object to get property value from + * @property: property to retrieve + * @val: storage for the property value + * + * This function retrieves the softare state of the given property for the given + * property. Since there is no driver callback to retrieve the current property + * value this might be out of sync with the hardware, depending upon the driver + * and property. + * + * Returns: + * Zero on success, error code on failure. + */ int drm_object_property_get_value(struct drm_mode_object *obj, struct drm_property *property, uint64_t *val) { @@ -2987,10 +3506,22 @@ int drm_object_property_get_value(struct drm_mode_object *obj, } EXPORT_SYMBOL(drm_object_property_get_value); +/** + * drm_mode_getproperty_ioctl - get the current value of a connector's property + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This function retrieves the current value for an connectors's property. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_getproperty_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - struct drm_mode_object *obj; struct drm_mode_get_property *out_resp = data; struct drm_property *property; int enum_count = 0; @@ -3009,17 +3540,17 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, return -EINVAL; drm_modeset_lock_all(dev); - obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); - if (!obj) { - ret = -EINVAL; + property = drm_property_find(dev, out_resp->prop_id); + if (!property) { + ret = -ENOENT; goto done; } - property = obj_to_property(obj); - if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) { + if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || + drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { list_for_each_entry(prop_enum, &property->enum_blob_list, head) enum_count++; - } else if (property->flags & DRM_MODE_PROP_BLOB) { + } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { list_for_each_entry(prop_blob, &property->enum_blob_list, head) blob_count++; } @@ -3041,7 +3572,8 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, } out_resp->count_values = value_count; - if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) { + if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || + drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { copied = 0; enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr; @@ -3063,7 +3595,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, out_resp->count_enum_blobs = enum_count; } - if (property->flags & DRM_MODE_PROP_BLOB) { + if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { if ((out_resp->count_enum_blobs >= blob_count) && blob_count) { copied = 0; blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr; @@ -3125,10 +3657,23 @@ static void drm_property_destroy_blob(struct drm_device *dev, kfree(blob); } +/** + * drm_mode_getblob_ioctl - get the contents of a blob property value + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This function retrieves the contents of a blob property. The value stored in + * an object's blob property is just a normal modeset object id. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_getblob_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - struct drm_mode_object *obj; struct drm_mode_get_blob *out_resp = data; struct drm_property_blob *blob; int ret = 0; @@ -3138,12 +3683,11 @@ int drm_mode_getblob_ioctl(struct drm_device *dev, return -EINVAL; drm_modeset_lock_all(dev); - obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB); - if (!obj) { - ret = -EINVAL; + blob = drm_property_blob_find(dev, out_resp->blob_id); + if (!blob) { + ret = -ENOENT; goto done; } - blob = obj_to_blob(obj); if (out_resp->length == blob->length) { blob_ptr = (void __user *)(unsigned long)out_resp->data; @@ -3159,6 +3703,17 @@ done: return ret; } +/** + * drm_mode_connector_update_edid_property - update the edid property of a connector + * @connector: drm connector + * @edid: new value of the edid property + * + * This function creates a new blob modeset object and assigns its id to the + * connector's edid property. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_connector_update_edid_property(struct drm_connector *connector, struct edid *edid) { @@ -3194,19 +3749,40 @@ static bool drm_property_change_is_valid(struct drm_property *property, { if (property->flags & DRM_MODE_PROP_IMMUTABLE) return false; - if (property->flags & DRM_MODE_PROP_RANGE) { + + if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) { if (value < property->values[0] || value > property->values[1]) return false; return true; - } else if (property->flags & DRM_MODE_PROP_BITMASK) { + } else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) { + int64_t svalue = U642I64(value); + if (svalue < U642I64(property->values[0]) || + svalue > U642I64(property->values[1])) + return false; + return true; + } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { int i; uint64_t valid_mask = 0; for (i = 0; i < property->num_values; i++) valid_mask |= (1ULL << property->values[i]); return !(value & ~valid_mask); - } else if (property->flags & DRM_MODE_PROP_BLOB) { + } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { /* Only the driver knows */ return true; + } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { + struct drm_mode_object *obj; + /* a zero value for an object property translates to null: */ + if (value == 0) + return true; + /* + * NOTE: use _object_find() directly to bypass restriction on + * looking up refcnt'd objects (ie. fb's). For a refcnt'd + * object this could race against object finalization, so it + * simply tells us that the object *was* valid. Which is good + * enough. + */ + obj = _object_find(property->dev, value, property->values[0]); + return obj != NULL; } else { int i; for (i = 0; i < property->num_values; i++) @@ -3216,6 +3792,20 @@ static bool drm_property_change_is_valid(struct drm_property *property, } } +/** + * drm_mode_connector_property_set_ioctl - set the current value of a connector property + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This function sets the current value for a connectors's property. It also + * calls into a driver's ->set_property callback to update the hardware state + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_connector_property_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -3282,6 +3872,21 @@ static int drm_mode_plane_set_obj_prop(struct drm_mode_object *obj, return ret; } +/** + * drm_mode_getproperty_ioctl - get the current value of a object's property + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This function retrieves the current value for an object's property. Compared + * to the connector specific ioctl this one is extended to also work on crtc and + * plane objects. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -3301,7 +3906,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); if (!obj) { - ret = -EINVAL; + ret = -ENOENT; goto out; } if (!obj->properties) { @@ -3338,6 +3943,22 @@ out: return ret; } +/** + * drm_mode_obj_set_property_ioctl - set the current value of an object's property + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This function sets the current value for an object's property. It also calls + * into a driver's ->set_property callback to update the hardware state. + * Compared to the connector specific ioctl this one is extended to also work on + * crtc and plane objects. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -3354,8 +3975,10 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, drm_modeset_lock_all(dev); arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); - if (!arg_obj) + if (!arg_obj) { + ret = -ENOENT; goto out; + } if (!arg_obj->properties) goto out; @@ -3368,8 +3991,10 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, prop_obj = drm_mode_object_find(dev, arg->prop_id, DRM_MODE_OBJECT_PROPERTY); - if (!prop_obj) + if (!prop_obj) { + ret = -ENOENT; goto out; + } property = obj_to_property(prop_obj); if (!drm_property_change_is_valid(property, arg->value)) @@ -3393,6 +4018,18 @@ out: return ret; } +/** + * drm_mode_connector_attach_encoder - attach a connector to an encoder + * @connector: connector to attach + * @encoder: encoder to attach @connector to + * + * This function links up a connector to an encoder. Note that the routing + * restrictions between encoders and crtcs are exposed to userspace through the + * possible_clones and possible_crtcs bitmasks. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_connector_attach_encoder(struct drm_connector *connector, struct drm_encoder *encoder) { @@ -3408,23 +4045,20 @@ int drm_mode_connector_attach_encoder(struct drm_connector *connector, } EXPORT_SYMBOL(drm_mode_connector_attach_encoder); -void drm_mode_connector_detach_encoder(struct drm_connector *connector, - struct drm_encoder *encoder) -{ - int i; - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] == encoder->base.id) { - connector->encoder_ids[i] = 0; - if (connector->encoder == encoder) - connector->encoder = NULL; - break; - } - } -} -EXPORT_SYMBOL(drm_mode_connector_detach_encoder); - +/** + * drm_mode_crtc_set_gamma_size - set the gamma table size + * @crtc: CRTC to set the gamma table size for + * @gamma_size: size of the gamma table + * + * Drivers which support gamma tables should set this to the supported gamma + * table size when initializing the CRTC. Currently the drm core only supports a + * fixed gamma table size. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, - int gamma_size) + int gamma_size) { crtc->gamma_size = gamma_size; @@ -3438,11 +4072,24 @@ int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, } EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size); +/** + * drm_mode_gamma_set_ioctl - set the gamma table + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * Set the gamma table of a CRTC to the one passed in by the user. Userspace can + * inquire the required gamma table size through drm_mode_gamma_get_ioctl. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_gamma_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_crtc_lut *crtc_lut = data; - struct drm_mode_object *obj; struct drm_crtc *crtc; void *r_base, *g_base, *b_base; int size; @@ -3452,12 +4099,11 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, return -EINVAL; drm_modeset_lock_all(dev); - obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); - if (!obj) { - ret = -EINVAL; + crtc = drm_crtc_find(dev, crtc_lut->crtc_id); + if (!crtc) { + ret = -ENOENT; goto out; } - crtc = obj_to_crtc(obj); if (crtc->funcs->gamma_set == NULL) { ret = -ENOSYS; @@ -3497,11 +4143,25 @@ out: } +/** + * drm_mode_gamma_get_ioctl - get the gamma table + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * Copy the current gamma table into the storage provided. This also provides + * the gamma table size the driver expects, which can be used to size the + * allocated storage. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_gamma_get_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_crtc_lut *crtc_lut = data; - struct drm_mode_object *obj; struct drm_crtc *crtc; void *r_base, *g_base, *b_base; int size; @@ -3511,12 +4171,11 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev, return -EINVAL; drm_modeset_lock_all(dev); - obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); - if (!obj) { - ret = -EINVAL; + crtc = drm_crtc_find(dev, crtc_lut->crtc_id); + if (!crtc) { + ret = -ENOENT; goto out; } - crtc = obj_to_crtc(obj); /* memcpy into gamma store */ if (crtc_lut->gamma_size != crtc->gamma_size) { @@ -3547,16 +4206,32 @@ out: return ret; } +/** + * drm_mode_page_flip_ioctl - schedule an asynchronous fb update + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This schedules an asynchronous update on a given CRTC, called page flip. + * Optionally a drm event is generated to signal the completion of the event. + * Generic drivers cannot assume that a pageflip with changed framebuffer + * properties (including driver specific metadata like tiling layout) will work, + * but some drivers support e.g. pixel format changes through the pageflip + * ioctl. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_page_flip_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_crtc_page_flip *page_flip = data; - struct drm_mode_object *obj; struct drm_crtc *crtc; struct drm_framebuffer *fb = NULL, *old_fb = NULL; struct drm_pending_vblank_event *e = NULL; unsigned long flags; - int hdisplay, vdisplay; int ret = -EINVAL; if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS || @@ -3566,13 +4241,12 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip) return -EINVAL; - obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC); - if (!obj) - return -EINVAL; - crtc = obj_to_crtc(obj); + crtc = drm_crtc_find(dev, page_flip->crtc_id); + if (!crtc) + return -ENOENT; - mutex_lock(&crtc->mutex); - if (crtc->fb == NULL) { + drm_modeset_lock(&crtc->mutex, NULL); + if (crtc->primary->fb == NULL) { /* The framebuffer is currently unbound, presumably * due to a hotplug event, that userspace has not * yet discovered. @@ -3585,27 +4259,16 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, goto out; fb = drm_framebuffer_lookup(dev, page_flip->fb_id); - if (!fb) + if (!fb) { + ret = -ENOENT; goto out; + } - hdisplay = crtc->mode.hdisplay; - vdisplay = crtc->mode.vdisplay; - - if (crtc->invert_dimensions) - swap(hdisplay, vdisplay); - - if (hdisplay > fb->width || - vdisplay > fb->height || - crtc->x > fb->width - hdisplay || - crtc->y > fb->height - vdisplay) { - DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n", - fb->width, fb->height, hdisplay, vdisplay, crtc->x, crtc->y, - crtc->invert_dimensions ? " (inverted)" : ""); - ret = -ENOSPC; + ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb); + if (ret) goto out; - } - if (crtc->fb->pixel_format != fb->pixel_format) { + if (crtc->primary->fb->pixel_format != fb->pixel_format) { DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n"); ret = -EINVAL; goto out; @@ -3638,7 +4301,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, (void (*) (struct drm_pending_event *)) kfree; } - old_fb = crtc->fb; + old_fb = crtc->primary->fb; ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags); if (ret) { if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { @@ -3656,7 +4319,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, * Failing to do so will screw with the reference counting * on framebuffers. */ - WARN_ON(crtc->fb != fb); + WARN_ON(crtc->primary->fb != fb); /* Unref only the old framebuffer. */ fb = NULL; } @@ -3666,11 +4329,19 @@ out: drm_framebuffer_unreference(fb); if (old_fb) drm_framebuffer_unreference(old_fb); - mutex_unlock(&crtc->mutex); + drm_modeset_unlock(&crtc->mutex); return ret; } +/** + * drm_mode_config_reset - call ->reset callbacks + * @dev: drm device + * + * This functions calls all the crtc's, encoder's and connector's ->reset + * callback. Drivers can use this in e.g. their driver load or resume code to + * reset hardware and software state. + */ void drm_mode_config_reset(struct drm_device *dev) { struct drm_crtc *crtc; @@ -3694,16 +4365,66 @@ void drm_mode_config_reset(struct drm_device *dev) } EXPORT_SYMBOL(drm_mode_config_reset); +/** + * drm_mode_create_dumb_ioctl - create a dumb backing storage buffer + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This creates a new dumb buffer in the driver's backing storage manager (GEM, + * TTM or something else entirely) and returns the resulting buffer handle. This + * handle can then be wrapped up into a framebuffer modeset object. + * + * Note that userspace is not allowed to use such objects for render + * acceleration - drivers must create their own private ioctls for such a use + * case. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_create_dumb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_create_dumb *args = data; + u32 cpp, stride, size; if (!dev->driver->dumb_create) return -ENOSYS; + if (!args->width || !args->height || !args->bpp) + return -EINVAL; + + /* overflow checks for 32bit size calculations */ + cpp = DIV_ROUND_UP(args->bpp, 8); + if (cpp > 0xffffffffU / args->width) + return -EINVAL; + stride = cpp * args->width; + if (args->height > 0xffffffffU / stride) + return -EINVAL; + + /* test for wrap-around */ + size = args->height * stride; + if (PAGE_ALIGN(size) == 0) + return -EINVAL; + return dev->driver->dumb_create(file_priv, dev, args); } +/** + * drm_mode_mmap_dumb_ioctl - create an mmap offset for a dumb backing storage buffer + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * Allocate an offset in the drm device node's address space to be able to + * memory map a dumb buffer. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_mmap_dumb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -3716,6 +4437,21 @@ int drm_mode_mmap_dumb_ioctl(struct drm_device *dev, return dev->driver->dumb_map_offset(file_priv, dev, args->handle, &args->offset); } +/** + * drm_mode_destroy_dumb_ioctl - destroy a dumb backing strage buffer + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This destroys the userspace handle for the given dumb backing storage buffer. + * Since buffer objects must be reference counted in the kernel a buffer object + * won't be immediately freed if a framebuffer modeset object still uses it. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_destroy_dumb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -3727,9 +4463,14 @@ int drm_mode_destroy_dumb_ioctl(struct drm_device *dev, return dev->driver->dumb_destroy(file_priv, dev, args->handle); } -/* - * Just need to support RGB formats here for compat with code that doesn't - * use pixel formats directly yet. +/** + * drm_fb_get_bpp_depth - get the bpp/depth values for format + * @format: pixel format (DRM_FORMAT_*) + * @depth: storage for the depth value + * @bpp: storage for the bpp value + * + * This only supports RGB formats here for compat with code that doesn't use + * pixel formats directly yet. */ void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, int *bpp) @@ -3788,7 +4529,8 @@ void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, *bpp = 32; break; default: - DRM_DEBUG_KMS("unsupported pixel format\n"); + DRM_DEBUG_KMS("unsupported pixel format %s\n", + drm_get_format_name(format)); *depth = 0; *bpp = 0; break; @@ -3800,7 +4542,7 @@ EXPORT_SYMBOL(drm_fb_get_bpp_depth); * drm_format_num_planes - get the number of planes for format * @format: pixel format (DRM_FORMAT_*) * - * RETURNS: + * Returns: * The number of planes used by the specified pixel format. */ int drm_format_num_planes(uint32_t format) @@ -3835,7 +4577,7 @@ EXPORT_SYMBOL(drm_format_num_planes); * @format: pixel format (DRM_FORMAT_*) * @plane: plane index * - * RETURNS: + * Returns: * The bytes per pixel value for the specified plane. */ int drm_format_plane_cpp(uint32_t format, int plane) @@ -3881,7 +4623,7 @@ EXPORT_SYMBOL(drm_format_plane_cpp); * drm_format_horz_chroma_subsampling - get the horizontal chroma subsampling factor * @format: pixel format (DRM_FORMAT_*) * - * RETURNS: + * Returns: * The horizontal chroma subsampling factor for the * specified pixel format. */ @@ -3916,7 +4658,7 @@ EXPORT_SYMBOL(drm_format_horz_chroma_subsampling); * drm_format_vert_chroma_subsampling - get the vertical chroma subsampling factor * @format: pixel format (DRM_FORMAT_*) * - * RETURNS: + * Returns: * The vertical chroma subsampling factor for the * specified pixel format. */ @@ -3952,6 +4694,7 @@ EXPORT_SYMBOL(drm_format_vert_chroma_subsampling); void drm_mode_config_init(struct drm_device *dev) { mutex_init(&dev->mode_config.mutex); + drm_modeset_lock_init(&dev->mode_config.connection_mutex); mutex_init(&dev->mode_config.idr_mutex); mutex_init(&dev->mode_config.fb_lock); INIT_LIST_HEAD(&dev->mode_config.fb_list); @@ -3966,6 +4709,7 @@ void drm_mode_config_init(struct drm_device *dev) drm_modeset_lock_all(dev); drm_mode_create_standard_connector_properties(dev); + drm_mode_create_standard_plane_properties(dev); drm_modeset_unlock_all(dev); /* Just to be sure */ @@ -3973,6 +4717,8 @@ void drm_mode_config_init(struct drm_device *dev) dev->mode_config.num_connector = 0; dev->mode_config.num_crtc = 0; dev->mode_config.num_encoder = 0; + dev->mode_config.num_overlay_plane = 0; + dev->mode_config.num_total_plane = 0; } EXPORT_SYMBOL(drm_mode_config_init); @@ -4048,5 +4794,6 @@ void drm_mode_config_cleanup(struct drm_device *dev) } idr_destroy(&dev->mode_config.crtc_idr); + drm_modeset_lock_fini(&dev->mode_config.connection_mutex); } EXPORT_SYMBOL(drm_mode_config_cleanup); |
