diff options
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/drm_atomic.c | 730 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_atomic_helper.c | 106 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_crtc.c | 345 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_crtc_helper.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_crtc_internal.h | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_drv.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_ioctl.c | 13 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_plane_helper.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_atomic.c | 20 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_drv.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/msm/msm_drv.h | 2 |
12 files changed, 1083 insertions, 150 deletions
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index ff5f034cc405..1e38dfc8e462 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -56,6 +56,11 @@ drm_atomic_state_alloc(struct drm_device *dev) if (!state) return NULL; + /* TODO legacy paths should maybe do a better job about + * setting this appropriately? + */ + state->allow_modeset = true; + state->num_connector = ACCESS_ONCE(dev->mode_config.num_connector); state->crtcs = kcalloc(dev->mode_config.num_crtc, @@ -217,6 +222,70 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state, EXPORT_SYMBOL(drm_atomic_get_crtc_state); /** + * drm_atomic_crtc_set_property - set property on CRTC + * @crtc: the drm CRTC to set a property on + * @state: the state object to update with the new property value + * @property: the property to set + * @val: the new property value + * + * Use this instead of calling crtc->atomic_set_property directly. + * This function handles generic/core properties and calls out to + * driver's ->atomic_set_property() for driver properties. To ensure + * consistent behavior you must call this function rather than the + * driver hook directly. + * + * RETURNS: + * Zero on success, error code on failure + */ +int drm_atomic_crtc_set_property(struct drm_crtc *crtc, + struct drm_crtc_state *state, struct drm_property *property, + uint64_t val) +{ + if (crtc->funcs->atomic_set_property) + return crtc->funcs->atomic_set_property(crtc, state, property, val); + return -EINVAL; +} +EXPORT_SYMBOL(drm_atomic_crtc_set_property); + +/* + * This function handles generic/core properties and calls out to + * driver's ->atomic_get_property() for driver properties. To ensure + * consistent behavior you must call this function rather than the + * driver hook directly. + */ +int drm_atomic_crtc_get_property(struct drm_crtc *crtc, + const struct drm_crtc_state *state, + struct drm_property *property, uint64_t *val) +{ + if (crtc->funcs->atomic_get_property) + return crtc->funcs->atomic_get_property(crtc, state, property, val); + return -EINVAL; +} + +/** + * drm_atomic_crtc_check - check crtc state + * @crtc: crtc to check + * @state: crtc state to check + * + * Provides core sanity checks for crtc state. + * + * RETURNS: + * Zero on success, error code on failure + */ +static int drm_atomic_crtc_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + /* NOTE: we explicitly don't enforce constraints such as primary + * layer covering entire screen, since that is something we want + * to allow (on hw that supports it). For hw that does not, it + * should be checked in driver's crtc->atomic_check() vfunc. + * + * TODO: Add generic modeset state checks once we support those. + */ + return 0; +} + +/** * drm_atomic_get_plane_state - get plane state * @state: global atomic state object * @plane: plane to get state object for @@ -272,6 +341,183 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state, EXPORT_SYMBOL(drm_atomic_get_plane_state); /** + * drm_atomic_plane_set_property - set property on plane + * @plane: the drm plane to set a property on + * @state: the state object to update with the new property value + * @property: the property to set + * @val: the new property value + * + * Use this instead of calling plane->atomic_set_property directly. + * This function handles generic/core properties and calls out to + * driver's ->atomic_set_property() for driver properties. To ensure + * consistent behavior you must call this function rather than the + * driver hook directly. + * + * RETURNS: + * Zero on success, error code on failure + */ +int drm_atomic_plane_set_property(struct drm_plane *plane, + struct drm_plane_state *state, struct drm_property *property, + uint64_t val) +{ + struct drm_device *dev = plane->dev; + struct drm_mode_config *config = &dev->mode_config; + + if (property == config->prop_fb_id) { + struct drm_framebuffer *fb = drm_framebuffer_lookup(dev, val); + drm_atomic_set_fb_for_plane(state, fb); + if (fb) + drm_framebuffer_unreference(fb); + } else if (property == config->prop_crtc_id) { + struct drm_crtc *crtc = drm_crtc_find(dev, val); + return drm_atomic_set_crtc_for_plane(state, crtc); + } else if (property == config->prop_crtc_x) { + state->crtc_x = U642I64(val); + } else if (property == config->prop_crtc_y) { + state->crtc_y = U642I64(val); + } else if (property == config->prop_crtc_w) { + state->crtc_w = val; + } else if (property == config->prop_crtc_h) { + state->crtc_h = val; + } else if (property == config->prop_src_x) { + state->src_x = val; + } else if (property == config->prop_src_y) { + state->src_y = val; + } else if (property == config->prop_src_w) { + state->src_w = val; + } else if (property == config->prop_src_h) { + state->src_h = val; + } else if (plane->funcs->atomic_set_property) { + return plane->funcs->atomic_set_property(plane, state, + property, val); + } else { + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_plane_set_property); + +/* + * This function handles generic/core properties and calls out to + * driver's ->atomic_get_property() for driver properties. To ensure + * consistent behavior you must call this function rather than the + * driver hook directly. + */ +static int +drm_atomic_plane_get_property(struct drm_plane *plane, + const struct drm_plane_state *state, + struct drm_property *property, uint64_t *val) +{ + struct drm_device *dev = plane->dev; + struct drm_mode_config *config = &dev->mode_config; + + if (property == config->prop_fb_id) { + *val = (state->fb) ? state->fb->base.id : 0; + } else if (property == config->prop_crtc_id) { + *val = (state->crtc) ? state->crtc->base.id : 0; + } else if (property == config->prop_crtc_x) { + *val = I642U64(state->crtc_x); + } else if (property == config->prop_crtc_y) { + *val = I642U64(state->crtc_y); + } else if (property == config->prop_crtc_w) { + *val = state->crtc_w; + } else if (property == config->prop_crtc_h) { + *val = state->crtc_h; + } else if (property == config->prop_src_x) { + *val = state->src_x; + } else if (property == config->prop_src_y) { + *val = state->src_y; + } else if (property == config->prop_src_w) { + *val = state->src_w; + } else if (property == config->prop_src_h) { + *val = state->src_h; + } else if (plane->funcs->atomic_get_property) { + return plane->funcs->atomic_get_property(plane, state, property, val); + } else { + return -EINVAL; + } + + return 0; +} + +/** + * drm_atomic_plane_check - check plane state + * @plane: plane to check + * @state: plane state to check + * + * Provides core sanity checks for plane state. + * + * RETURNS: + * Zero on success, error code on failure + */ +static int drm_atomic_plane_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + unsigned int fb_width, fb_height; + unsigned int i; + + /* either *both* CRTC and FB must be set, or neither */ + if (WARN_ON(state->crtc && !state->fb)) { + DRM_DEBUG_KMS("CRTC set but no FB\n"); + return -EINVAL; + } else if (WARN_ON(state->fb && !state->crtc)) { + DRM_DEBUG_KMS("FB set but no CRTC\n"); + return -EINVAL; + } + + /* if disabled, we don't care about the rest of the state: */ + if (!state->crtc) + return 0; + + /* Check whether this plane is usable on this CRTC */ + if (!(plane->possible_crtcs & drm_crtc_mask(state->crtc))) { + DRM_DEBUG_KMS("Invalid crtc for plane\n"); + return -EINVAL; + } + + /* Check whether this plane supports the fb pixel format. */ + for (i = 0; i < plane->format_count; i++) + if (state->fb->pixel_format == plane->format_types[i]) + break; + if (i == plane->format_count) { + DRM_DEBUG_KMS("Invalid pixel format %s\n", + drm_get_format_name(state->fb->pixel_format)); + return -EINVAL; + } + + /* Give drivers some help against integer overflows */ + if (state->crtc_w > INT_MAX || + state->crtc_x > INT_MAX - (int32_t) state->crtc_w || + state->crtc_h > INT_MAX || + state->crtc_y > INT_MAX - (int32_t) state->crtc_h) { + DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", + state->crtc_w, state->crtc_h, + state->crtc_x, state->crtc_y); + return -ERANGE; + } + + fb_width = state->fb->width << 16; + fb_height = state->fb->height << 16; + + /* Make sure source coordinates are inside the fb. */ + if (state->src_w > fb_width || + state->src_x > fb_width - state->src_w || + state->src_h > fb_height || + state->src_y > fb_height - state->src_h) { + DRM_DEBUG_KMS("Invalid source coordinates " + "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", + state->src_w >> 16, ((state->src_w & 0xffff) * 15625) >> 10, + state->src_h >> 16, ((state->src_h & 0xffff) * 15625) >> 10, + state->src_x >> 16, ((state->src_x & 0xffff) * 15625) >> 10, + state->src_y >> 16, ((state->src_y & 0xffff) * 15625) >> 10); + return -ENOSPC; + } + + return 0; +} + +/** * drm_atomic_get_connector_state - get connector state * @state: global atomic state object * @connector: connector to get state object for @@ -343,9 +589,113 @@ drm_atomic_get_connector_state(struct drm_atomic_state *state, EXPORT_SYMBOL(drm_atomic_get_connector_state); /** + * drm_atomic_connector_set_property - set property on connector. + * @connector: the drm connector to set a property on + * @state: the state object to update with the new property value + * @property: the property to set + * @val: the new property value + * + * Use this instead of calling connector->atomic_set_property directly. + * This function handles generic/core properties and calls out to + * driver's ->atomic_set_property() for driver properties. To ensure + * consistent behavior you must call this function rather than the + * driver hook directly. + * + * RETURNS: + * Zero on success, error code on failure + */ +int drm_atomic_connector_set_property(struct drm_connector *connector, + struct drm_connector_state *state, struct drm_property *property, + uint64_t val) +{ + struct drm_device *dev = connector->dev; + struct drm_mode_config *config = &dev->mode_config; + + if (property == config->prop_crtc_id) { + struct drm_crtc *crtc = drm_crtc_find(dev, val); + return drm_atomic_set_crtc_for_connector(state, crtc); + } else if (property == config->dpms_property) { + /* setting DPMS property requires special handling, which + * is done in legacy setprop path for us. Disallow (for + * now?) atomic writes to DPMS property: + */ + return -EINVAL; + } else if (connector->funcs->atomic_set_property) { + return connector->funcs->atomic_set_property(connector, + state, property, val); + } else { + return -EINVAL; + } +} +EXPORT_SYMBOL(drm_atomic_connector_set_property); + +/* + * This function handles generic/core properties and calls out to + * driver's ->atomic_get_property() for driver properties. To ensure + * consistent behavior you must call this function rather than the + * driver hook directly. + */ +static int +drm_atomic_connector_get_property(struct drm_connector *connector, + const struct drm_connector_state *state, + struct drm_property *property, uint64_t *val) +{ + struct drm_device *dev = connector->dev; + struct drm_mode_config *config = &dev->mode_config; + + if (property == config->prop_crtc_id) { + *val = (state->crtc) ? state->crtc->base.id : 0; + } else if (property == config->dpms_property) { + *val = connector->dpms; + } else if (connector->funcs->atomic_get_property) { + return connector->funcs->atomic_get_property(connector, + state, property, val); + } else { + return -EINVAL; + } + + return 0; +} + +int drm_atomic_get_property(struct drm_mode_object *obj, + struct drm_property *property, uint64_t *val) +{ + struct drm_device *dev = property->dev; + int ret; + + switch (obj->type) { + case DRM_MODE_OBJECT_CONNECTOR: { + struct drm_connector *connector = obj_to_connector(obj); + WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); + ret = drm_atomic_connector_get_property(connector, + connector->state, property, val); + break; + } + case DRM_MODE_OBJECT_CRTC: { + struct drm_crtc *crtc = obj_to_crtc(obj); + WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); + ret = drm_atomic_crtc_get_property(crtc, + crtc->state, property, val); + break; + } + case DRM_MODE_OBJECT_PLANE: { + struct drm_plane *plane = obj_to_plane(obj); + WARN_ON(!drm_modeset_is_locked(&plane->mutex)); + ret = drm_atomic_plane_get_property(plane, + plane->state, property, val); + break; + } + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/** * drm_atomic_set_crtc_for_plane - set crtc for plane - * @state: the incoming atomic state - * @plane: the plane whose incoming state to update + * @plane_state: the plane whose incoming state to update * @crtc: crtc to use for the plane * * Changing the assigned crtc for a plane requires us to grab the lock and state @@ -358,16 +708,12 @@ EXPORT_SYMBOL(drm_atomic_get_connector_state); * sequence must be restarted. All other errors are fatal. */ int -drm_atomic_set_crtc_for_plane(struct drm_atomic_state *state, - struct drm_plane *plane, struct drm_crtc *crtc) +drm_atomic_set_crtc_for_plane(struct drm_plane_state *plane_state, + struct drm_crtc *crtc) { - struct drm_plane_state *plane_state = - drm_atomic_get_plane_state(state, plane); + struct drm_plane *plane = plane_state->plane; struct drm_crtc_state *crtc_state; - if (WARN_ON(IS_ERR(plane_state))) - return PTR_ERR(plane_state); - if (plane_state->crtc) { crtc_state = drm_atomic_get_crtc_state(plane_state->state, plane_state->crtc); @@ -583,14 +929,62 @@ EXPORT_SYMBOL(drm_atomic_legacy_backoff); */ int drm_atomic_check_only(struct drm_atomic_state *state) { - struct drm_mode_config *config = &state->dev->mode_config; + struct drm_device *dev = state->dev; + struct drm_mode_config *config = &dev->mode_config; + int nplanes = config->num_total_plane; + int ncrtcs = config->num_crtc; + int i, ret = 0; DRM_DEBUG_KMS("checking %p\n", state); + for (i = 0; i < nplanes; i++) { + struct drm_plane *plane = state->planes[i]; + + if (!plane) + continue; + + ret = drm_atomic_plane_check(plane, state->plane_states[i]); + if (ret) { + DRM_DEBUG_KMS("[PLANE:%d] atomic core check failed\n", + plane->base.id); + return ret; + } + } + + for (i = 0; i < ncrtcs; i++) { + struct drm_crtc *crtc = state->crtcs[i]; + + if (!crtc) + continue; + + ret = drm_atomic_crtc_check(crtc, state->crtc_states[i]); + if (ret) { + DRM_DEBUG_KMS("[CRTC:%d] atomic core check failed\n", + crtc->base.id); + return ret; + } + } + if (config->funcs->atomic_check) - return config->funcs->atomic_check(state->dev, state); - else - return 0; + ret = config->funcs->atomic_check(state->dev, state); + + if (!state->allow_modeset) { + for (i = 0; i < ncrtcs; i++) { + struct drm_crtc *crtc = state->crtcs[i]; + struct drm_crtc_state *crtc_state = state->crtc_states[i]; + + if (!crtc) + continue; + + if (crtc_state->mode_changed) { + DRM_DEBUG_KMS("[CRTC:%d] requires full modeset\n", + crtc->base.id); + return -EINVAL; + } + } + } + + return ret; } EXPORT_SYMBOL(drm_atomic_check_only); @@ -655,3 +1049,313 @@ int drm_atomic_async_commit(struct drm_atomic_state *state) return config->funcs->atomic_commit(state->dev, state, true); } EXPORT_SYMBOL(drm_atomic_async_commit); + +/* + * The big monstor ioctl + */ + +static struct drm_pending_vblank_event *create_vblank_event( + struct drm_device *dev, struct drm_file *file_priv, uint64_t user_data) +{ + struct drm_pending_vblank_event *e = NULL; + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + if (file_priv->event_space < sizeof e->event) { + spin_unlock_irqrestore(&dev->event_lock, flags); + goto out; + } + file_priv->event_space -= sizeof e->event; + spin_unlock_irqrestore(&dev->event_lock, flags); + + e = kzalloc(sizeof *e, GFP_KERNEL); + if (e == NULL) { + spin_lock_irqsave(&dev->event_lock, flags); + file_priv->event_space += sizeof e->event; + spin_unlock_irqrestore(&dev->event_lock, flags); + goto out; + } + + e->event.base.type = DRM_EVENT_FLIP_COMPLETE; + e->event.base.length = sizeof e->event; + e->event.user_data = user_data; + e->base.event = &e->event.base; + e->base.file_priv = file_priv; + e->base.destroy = (void (*) (struct drm_pending_event *)) kfree; + +out: + return e; +} + +static void destroy_vblank_event(struct drm_device *dev, + struct drm_file *file_priv, struct drm_pending_vblank_event *e) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + file_priv->event_space += sizeof e->event; + spin_unlock_irqrestore(&dev->event_lock, flags); + kfree(e); +} + +static int atomic_set_prop(struct drm_atomic_state *state, + struct drm_mode_object *obj, struct drm_property *prop, + uint64_t prop_value) +{ + struct drm_mode_object *ref; + int ret; + + if (!drm_property_change_valid_get(prop, prop_value, &ref)) + return -EINVAL; + + switch (obj->type) { + case DRM_MODE_OBJECT_CONNECTOR: { + struct drm_connector *connector = obj_to_connector(obj); + struct drm_connector_state *connector_state; + + connector_state = drm_atomic_get_connector_state(state, connector); + if (IS_ERR(connector_state)) { + ret = PTR_ERR(connector_state); + break; + } + + ret = drm_atomic_connector_set_property(connector, + connector_state, prop, prop_value); + break; + } + case DRM_MODE_OBJECT_CRTC: { + struct drm_crtc *crtc = obj_to_crtc(obj); + struct drm_crtc_state *crtc_state; + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + break; + } + + ret = drm_atomic_crtc_set_property(crtc, + crtc_state, prop, prop_value); + break; + } + case DRM_MODE_OBJECT_PLANE: { + struct drm_plane *plane = obj_to_plane(obj); + struct drm_plane_state *plane_state; + + plane_state = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(plane_state)) { + ret = PTR_ERR(plane_state); + break; + } + + ret = drm_atomic_plane_set_property(plane, + plane_state, prop, prop_value); + break; + } + default: + ret = -EINVAL; + break; + } + + drm_property_change_valid_put(prop, ref); + return ret; +} + +int drm_mode_atomic_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_atomic *arg = data; + uint32_t __user *objs_ptr = (uint32_t __user *)(unsigned long)(arg->objs_ptr); + uint32_t __user *count_props_ptr = (uint32_t __user *)(unsigned long)(arg->count_props_ptr); + uint32_t __user *props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr); + uint64_t __user *prop_values_ptr = (uint64_t __user *)(unsigned long)(arg->prop_values_ptr); + unsigned int copied_objs, copied_props; + struct drm_atomic_state *state; + struct drm_modeset_acquire_ctx ctx; + struct drm_plane *plane; + unsigned plane_mask = 0; + int ret = 0; + unsigned int i, j; + + /* disallow for drivers not supporting atomic: */ + if (!drm_core_check_feature(dev, DRIVER_ATOMIC)) + return -EINVAL; + + /* disallow for userspace that has not enabled atomic cap (even + * though this may be a bit overkill, since legacy userspace + * wouldn't know how to call this ioctl) + */ + if (!file_priv->atomic) + return -EINVAL; + + if (arg->flags & ~DRM_MODE_ATOMIC_FLAGS) + return -EINVAL; + + if (arg->reserved) + return -EINVAL; + + if ((arg->flags & DRM_MODE_PAGE_FLIP_ASYNC) && + !dev->mode_config.async_page_flip) + return -EINVAL; + + /* can't test and expect an event at the same time. */ + if ((arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) && + (arg->flags & DRM_MODE_PAGE_FLIP_EVENT)) + return -EINVAL; + + drm_modeset_acquire_init(&ctx, 0); + + state = drm_atomic_state_alloc(dev); + if (!state) + return -ENOMEM; + + state->acquire_ctx = &ctx; + state->allow_modeset = !!(arg->flags & DRM_MODE_ATOMIC_ALLOW_MODESET); + +retry: + copied_objs = 0; + copied_props = 0; + + for (i = 0; i < arg->count_objs; i++) { + uint32_t obj_id, count_props; + struct drm_mode_object *obj; + + if (get_user(obj_id, objs_ptr + copied_objs)) { + ret = -EFAULT; + goto fail; + } + + obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_ANY); + if (!obj || !obj->properties) { + ret = -ENOENT; + goto fail; + } + + if (obj->type == DRM_MODE_OBJECT_PLANE) { + plane = obj_to_plane(obj); + plane_mask |= (1 << drm_plane_index(plane)); + plane->old_fb = plane->fb; + } + + if (get_user(count_props, count_props_ptr + copied_objs)) { + ret = -EFAULT; + goto fail; + } + + copied_objs++; + + for (j = 0; j < count_props; j++) { + uint32_t prop_id; + uint64_t prop_value; + struct drm_property *prop; + + if (get_user(prop_id, props_ptr + copied_props)) { + ret = -EFAULT; + goto fail; + } + + prop = drm_property_find(dev, prop_id); + if (!prop) { + ret = -ENOENT; + goto fail; + } + + if (get_user(prop_value, prop_values_ptr + copied_props)) { + ret = -EFAULT; + goto fail; + } + + ret = atomic_set_prop(state, obj, prop, prop_value); + if (ret) + goto fail; + + copied_props++; + } + } + + if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) { + int ncrtcs = dev->mode_config.num_crtc; + + for (i = 0; i < ncrtcs; i++) { + struct drm_crtc_state *crtc_state = state->crtc_states[i]; + struct drm_pending_vblank_event *e; + + if (!crtc_state) + continue; + + e = create_vblank_event(dev, file_priv, arg->user_data); + if (!e) { + ret = -ENOMEM; + goto fail; + } + + crtc_state->event = e; + } + } + + if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) { + ret = drm_atomic_check_only(state); + /* _check_only() does not free state, unlike _commit() */ + drm_atomic_state_free(state); + } else if (arg->flags & DRM_MODE_ATOMIC_NONBLOCK) { + ret = drm_atomic_async_commit(state); + } else { + ret = drm_atomic_commit(state); + } + + /* if succeeded, fixup legacy plane crtc/fb ptrs before dropping + * locks (ie. while it is still safe to deref plane->state). We + * need to do this here because the driver entry points cannot + * distinguish between legacy and atomic ioctls. + */ + drm_for_each_plane_mask(plane, dev, plane_mask) { + if (ret == 0) { + struct drm_framebuffer *new_fb = plane->state->fb; + if (new_fb) + drm_framebuffer_reference(new_fb); + plane->fb = new_fb; + plane->crtc = plane->state->crtc; + } else { + plane->old_fb = NULL; + } + if (plane->old_fb) { + drm_framebuffer_unreference(plane->old_fb); + plane->old_fb = NULL; + } + } + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + + return ret; + +fail: + if (ret == -EDEADLK) + goto backoff; + + if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) { + int ncrtcs = dev->mode_config.num_crtc; + + for (i = 0; i < ncrtcs; i++) { + struct drm_crtc_state *crtc_state = state->crtc_states[i]; + + if (!crtc_state) + continue; + + destroy_vblank_event(dev, file_priv, crtc_state->event); + crtc_state->event = NULL; + } + } + + drm_atomic_state_free(state); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + + return ret; + +backoff: + drm_atomic_state_clear(state); + drm_modeset_backoff(&ctx); + + goto retry; +} diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index bbdbe4721573..541ba833ed36 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -330,7 +330,29 @@ mode_fixup(struct drm_atomic_state *state) return 0; } -static int +/** + * drm_atomic_helper_check - validate state object for modeset changes + * @dev: DRM device + * @state: the driver state object + * + * Check the state object to see if the requested state is physically possible. + * This does all the crtc and connector related computations for an atomic + * update. It computes and updates crtc_state->mode_changed, adds any additional + * connectors needed for full modesets and calls down into ->mode_fixup + * functions of the driver backend. + * + * IMPORTANT: + * + * Drivers which update ->mode_changed (e.g. in their ->atomic_check hooks if a + * plane update can't be done without a full modeset) _must_ call this function + * afterwards after that change. It is permitted to call this function multiple + * times for the same update, e.g. when the ->atomic_check functions depend upon + * the adjusted dotclock for fifo space allocation and watermark computation. + * + * RETURNS + * Zero for success or -errno + */ +int drm_atomic_helper_check_modeset(struct drm_device *dev, struct drm_atomic_state *state) { @@ -406,23 +428,23 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, return mode_fixup(state); } +EXPORT_SYMBOL(drm_atomic_helper_check_modeset); /** - * drm_atomic_helper_check - validate state object + * drm_atomic_helper_check - validate state object for modeset changes * @dev: DRM device * @state: the driver state object * * Check the state object to see if the requested state is physically possible. - * Only crtcs and planes have check callbacks, so for any additional (global) - * checking that a driver needs it can simply wrap that around this function. - * Drivers without such needs can directly use this as their ->atomic_check() - * callback. + * This does all the plane update related checks using by calling into the + * ->atomic_check hooks provided by the driver. * * RETURNS * Zero for success or -errno */ -int drm_atomic_helper_check(struct drm_device *dev, - struct drm_atomic_state *state) +int +drm_atomic_helper_check_planes(struct drm_device *dev, + struct drm_atomic_state *state) { int nplanes = dev->mode_config.num_total_plane; int ncrtcs = dev->mode_config.num_crtc; @@ -445,7 +467,7 @@ int drm_atomic_helper_check(struct drm_device *dev, ret = funcs->atomic_check(plane, plane_state); if (ret) { - DRM_DEBUG_KMS("[PLANE:%d] atomic check failed\n", + DRM_DEBUG_KMS("[PLANE:%d] atomic driver check failed\n", plane->base.id); return ret; } @@ -465,16 +487,49 @@ int drm_atomic_helper_check(struct drm_device *dev, ret = funcs->atomic_check(crtc, state->crtc_states[i]); if (ret) { - DRM_DEBUG_KMS("[CRTC:%d] atomic check failed\n", + DRM_DEBUG_KMS("[CRTC:%d] atomic driver check failed\n", crtc->base.id); return ret; } } + return ret; +} +EXPORT_SYMBOL(drm_atomic_helper_check_planes); + +/** + * drm_atomic_helper_check - validate state object + * @dev: DRM device + * @state: the driver state object + * + * Check the state object to see if the requested state is physically possible. + * Only crtcs and planes have check callbacks, so for any additional (global) + * checking that a driver needs it can simply wrap that around this function. + * Drivers without such needs can directly use this as their ->atomic_check() + * callback. + * + * This just wraps the two parts of the state checking for planes and modeset + * state in the default order: First it calls drm_atomic_helper_check_modeset() + * and then drm_atomic_helper_check_planes(). The assumption is that the + * ->atomic_check functions depend upon an updated adjusted_mode.clock to + * e.g. properly compute watermarks. + * + * RETURNS + * Zero for success or -errno + */ +int drm_atomic_helper_check(struct drm_device *dev, + struct drm_atomic_state *state) +{ + int ret; + ret = drm_atomic_helper_check_modeset(dev, state); if (ret) return ret; + ret = drm_atomic_helper_check_planes(dev, state); + if (ret) + return ret; + return ret; } EXPORT_SYMBOL(drm_atomic_helper_check); @@ -1222,7 +1277,7 @@ retry: goto fail; } - ret = drm_atomic_set_crtc_for_plane(state, plane, crtc); + ret = drm_atomic_set_crtc_for_plane(plane_state, crtc); if (ret != 0) goto fail; drm_atomic_set_fb_for_plane(plane_state, fb); @@ -1301,7 +1356,7 @@ retry: goto fail; } - ret = drm_atomic_set_crtc_for_plane(state, plane, NULL); + ret = drm_atomic_set_crtc_for_plane(plane_state, NULL); if (ret != 0) goto fail; drm_atomic_set_fb_for_plane(plane_state, NULL); @@ -1464,7 +1519,7 @@ retry: crtc_state->enable = false; - ret = drm_atomic_set_crtc_for_plane(state, crtc->primary, NULL); + ret = drm_atomic_set_crtc_for_plane(primary_state, NULL); if (ret != 0) goto fail; @@ -1479,7 +1534,7 @@ retry: crtc_state->enable = true; drm_mode_copy(&crtc_state->mode, set->mode); - ret = drm_atomic_set_crtc_for_plane(state, crtc->primary, crtc); + ret = drm_atomic_set_crtc_for_plane(primary_state, crtc); if (ret != 0) goto fail; drm_atomic_set_fb_for_plane(primary_state, set->fb); @@ -1558,8 +1613,8 @@ retry: goto fail; } - ret = crtc->funcs->atomic_set_property(crtc, crtc_state, - property, val); + ret = drm_atomic_crtc_set_property(crtc, crtc_state, + property, val); if (ret) goto fail; @@ -1617,8 +1672,8 @@ retry: goto fail; } - ret = plane->funcs->atomic_set_property(plane, plane_state, - property, val); + ret = drm_atomic_plane_set_property(plane, plane_state, + property, val); if (ret) goto fail; @@ -1676,8 +1731,8 @@ retry: goto fail; } - ret = connector->funcs->atomic_set_property(connector, connector_state, - property, val); + ret = drm_atomic_connector_set_property(connector, connector_state, + property, val); if (ret) goto fail; @@ -1751,7 +1806,7 @@ retry: goto fail; } - ret = drm_atomic_set_crtc_for_plane(state, plane, crtc); + ret = drm_atomic_set_crtc_for_plane(plane_state, crtc); if (ret != 0) goto fail; drm_atomic_set_fb_for_plane(plane_state, fb); @@ -1814,6 +1869,9 @@ void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc) { kfree(crtc->state); crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL); + + if (crtc->state) + crtc->state->crtc = crtc; } EXPORT_SYMBOL(drm_atomic_helper_crtc_reset); @@ -1873,6 +1931,9 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane) kfree(plane->state); plane->state = kzalloc(sizeof(*plane->state), GFP_KERNEL); + + if (plane->state) + plane->state->plane = plane; } EXPORT_SYMBOL(drm_atomic_helper_plane_reset); @@ -1930,6 +1991,9 @@ void drm_atomic_helper_connector_reset(struct drm_connector *connector) { kfree(connector->state); connector->state = kzalloc(sizeof(*connector->state), GFP_KERNEL); + + if (connector->state) + connector->state->connector = connector; } EXPORT_SYMBOL(drm_atomic_helper_connector_reset); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 2892d746a1e9..7e4acad3f6f9 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -38,6 +38,7 @@ #include <drm/drm_edid.h> #include <drm/drm_fourcc.h> #include <drm/drm_modeset_lock.h> +#include <drm/drm_atomic.h> #include "drm_crtc_internal.h" #include "drm_internal.h" @@ -830,6 +831,7 @@ int drm_connector_init(struct drm_device *dev, const struct drm_connector_funcs *funcs, int connector_type) { + struct drm_mode_config *config = &dev->mode_config; int ret; struct ida *connector_ida = &drm_connector_enum_list[connector_type].ida; @@ -868,16 +870,20 @@ int drm_connector_init(struct drm_device *dev, /* We should add connectors at the end to avoid upsetting the connector * index too much. */ - list_add_tail(&connector->head, &dev->mode_config.connector_list); - dev->mode_config.num_connector++; + list_add_tail(&connector->head, &config->connector_list); + config->num_connector++; if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL) drm_object_attach_property(&connector->base, - dev->mode_config.edid_property, + config->edid_property, 0); drm_object_attach_property(&connector->base, - dev->mode_config.dpms_property, 0); + config->dpms_property, 0); + + if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { + drm_object_attach_property(&connector->base, config->prop_crtc_id, 0); + } connector->debugfs_entry = NULL; @@ -1168,6 +1174,7 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, const uint32_t *formats, uint32_t format_count, enum drm_plane_type type) { + struct drm_mode_config *config = &dev->mode_config; int ret; ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); @@ -1192,15 +1199,28 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, plane->possible_crtcs = possible_crtcs; plane->type = type; - list_add_tail(&plane->head, &dev->mode_config.plane_list); - dev->mode_config.num_total_plane++; + list_add_tail(&plane->head, &config->plane_list); + config->num_total_plane++; if (plane->type == DRM_PLANE_TYPE_OVERLAY) - dev->mode_config.num_overlay_plane++; + config->num_overlay_plane++; drm_object_attach_property(&plane->base, - dev->mode_config.plane_type_property, + config->plane_type_property, plane->type); + if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { + drm_object_attach_property(&plane->base, config->prop_fb_id, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_id, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_x, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_y, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_w, 0); + drm_object_attach_property(&plane->base, config->prop_crtc_h, 0); + drm_object_attach_property(&plane->base, config->prop_src_x, 0); + drm_object_attach_property(&plane->base, config->prop_src_y, 0); + drm_object_attach_property(&plane->base, config->prop_src_w, 0); + drm_object_attach_property(&plane->base, config->prop_src_h, 0); + } + return 0; } EXPORT_SYMBOL(drm_universal_plane_init); @@ -1322,50 +1342,109 @@ void drm_plane_force_disable(struct drm_plane *plane) } EXPORT_SYMBOL(drm_plane_force_disable); -static int drm_mode_create_standard_connector_properties(struct drm_device *dev) +static int drm_mode_create_standard_properties(struct drm_device *dev) { - struct drm_property *edid; - struct drm_property *dpms; - struct drm_property *dev_path; + struct drm_property *prop; /* * Standard properties (apply to all connectors) */ - edid = drm_property_create(dev, DRM_MODE_PROP_BLOB | + prop = drm_property_create(dev, DRM_MODE_PROP_BLOB | DRM_MODE_PROP_IMMUTABLE, "EDID", 0); - dev->mode_config.edid_property = edid; + if (!prop) + return -ENOMEM; + dev->mode_config.edid_property = prop; - dpms = drm_property_create_enum(dev, 0, + prop = drm_property_create_enum(dev, 0, "DPMS", drm_dpms_enum_list, ARRAY_SIZE(drm_dpms_enum_list)); - dev->mode_config.dpms_property = dpms; - - dev_path = drm_property_create(dev, - DRM_MODE_PROP_BLOB | - DRM_MODE_PROP_IMMUTABLE, - "PATH", 0); - dev->mode_config.path_property = dev_path; - - dev->mode_config.tile_property = drm_property_create(dev, - DRM_MODE_PROP_BLOB | - DRM_MODE_PROP_IMMUTABLE, - "TILE", 0); + if (!prop) + return -ENOMEM; + dev->mode_config.dpms_property = prop; - return 0; -} + prop = drm_property_create(dev, + DRM_MODE_PROP_BLOB | + DRM_MODE_PROP_IMMUTABLE, + "PATH", 0); + if (!prop) + return -ENOMEM; + dev->mode_config.path_property = prop; -static int drm_mode_create_standard_plane_properties(struct drm_device *dev) -{ - struct drm_property *type; + prop = drm_property_create(dev, + DRM_MODE_PROP_BLOB | + DRM_MODE_PROP_IMMUTABLE, + "TILE", 0); + if (!prop) + return -ENOMEM; + dev->mode_config.tile_property = prop; - /* - * Standard properties (apply to all planes) - */ - type = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, + prop = 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; + if (!prop) + return -ENOMEM; + dev->mode_config.plane_type_property = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "SRC_X", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_x = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "SRC_Y", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_y = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "SRC_W", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_w = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "SRC_H", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_h = prop; + + prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC, + "CRTC_X", INT_MIN, INT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_x = prop; + + prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC, + "CRTC_Y", INT_MIN, INT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_y = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "CRTC_W", 0, INT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_w = prop; + + prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, + "CRTC_H", 0, INT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_h = prop; + + prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC, + "FB_ID", DRM_MODE_OBJECT_FB); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_fb_id = prop; + + prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC, + "CRTC_ID", DRM_MODE_OBJECT_CRTC); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_id = prop; return 0; } @@ -1991,6 +2070,44 @@ static struct drm_encoder *drm_connector_get_encoder(struct drm_connector *conne return connector->encoder; } +/* helper for getconnector and getproperties ioctls */ +static int get_properties(struct drm_mode_object *obj, bool atomic, + uint32_t __user *prop_ptr, uint64_t __user *prop_values, + uint32_t *arg_count_props) +{ + int props_count; + int i, ret, copied; + + props_count = obj->properties->count; + if (!atomic) + props_count -= obj->properties->atomic_count; + + if ((*arg_count_props >= props_count) && props_count) { + for (i = 0, copied = 0; copied < props_count; i++) { + struct drm_property *prop = obj->properties->properties[i]; + uint64_t val; + + if ((prop->flags & DRM_MODE_PROP_ATOMIC) && !atomic) + continue; + + ret = drm_object_property_get_value(obj, prop, &val); + if (ret) + return ret; + + if (put_user(prop->base.id, prop_ptr + copied)) + return -EFAULT; + + if (put_user(val, prop_values + copied)) + return -EFAULT; + + copied++; + } + } + *arg_count_props = props_count; + + return 0; +} + /** * drm_mode_getconnector - get connector configuration * @dev: drm device for the ioctl @@ -2012,15 +2129,12 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, struct drm_encoder *encoder; struct drm_display_mode *mode; int mode_count = 0; - int props_count = 0; int encoders_count = 0; int ret = 0; int copied = 0; int i; struct drm_mode_modeinfo u_mode; struct drm_mode_modeinfo __user *mode_ptr; - uint32_t __user *prop_ptr; - uint64_t __user *prop_values; uint32_t __user *encoder_ptr; if (!drm_core_check_feature(dev, DRIVER_MODESET)) @@ -2031,6 +2145,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id); mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); connector = drm_connector_find(dev, out_resp->connector_id); if (!connector) { @@ -2038,8 +2153,6 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, goto out; } - props_count = connector->properties.count; - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) if (connector->encoder_ids[i] != 0) encoders_count++; @@ -2062,14 +2175,11 @@ 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); - encoder = drm_connector_get_encoder(connector); if (encoder) out_resp->encoder_id = 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 @@ -2093,26 +2203,12 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, } out_resp->count_modes = mode_count; - if ((out_resp->count_props >= props_count) && props_count) { - copied = 0; - prop_ptr = (uint32_t __user *)(unsigned long)(out_resp->props_ptr); - prop_values = (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr); - for (i = 0; i < connector->properties.count; i++) { - if (put_user(connector->properties.ids[i], - prop_ptr + copied)) { - ret = -EFAULT; - goto out; - } - - if (put_user(connector->properties.values[i], - prop_values + copied)) { - ret = -EFAULT; - goto out; - } - copied++; - } - } - out_resp->count_props = props_count; + ret = get_properties(&connector->base, file_priv->atomic, + (uint32_t __user *)(unsigned long)(out_resp->props_ptr), + (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr), + &out_resp->count_props); + if (ret) + goto out; if ((out_resp->count_encoders >= encoders_count) && encoders_count) { copied = 0; @@ -2131,6 +2227,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, out_resp->count_encoders = encoders_count; out: + drm_modeset_unlock(&dev->mode_config.connection_mutex); mutex_unlock(&dev->mode_config.mutex); return ret; @@ -3823,9 +3920,11 @@ void drm_object_attach_property(struct drm_mode_object *obj, return; } - obj->properties->ids[count] = property->base.id; + obj->properties->properties[count] = property; obj->properties->values[count] = init_val; obj->properties->count++; + if (property->flags & DRM_MODE_PROP_ATOMIC) + obj->properties->atomic_count++; } EXPORT_SYMBOL(drm_object_attach_property); @@ -3848,7 +3947,7 @@ int drm_object_property_set_value(struct drm_mode_object *obj, int i; for (i = 0; i < obj->properties->count; i++) { - if (obj->properties->ids[i] == property->base.id) { + if (obj->properties->properties[i] == property) { obj->properties->values[i] = val; return 0; } @@ -3877,8 +3976,16 @@ int drm_object_property_get_value(struct drm_mode_object *obj, { int i; + /* read-only properties bypass atomic mechanism and still store + * their value in obj->properties->values[].. mostly to avoid + * having to deal w/ EDID and similar props in atomic paths: + */ + if (drm_core_check_feature(property->dev, DRIVER_ATOMIC) && + !(property->flags & DRM_MODE_PROP_IMMUTABLE)) + return drm_atomic_get_property(obj, property, val); + for (i = 0; i < obj->properties->count; i++) { - if (obj->properties->ids[i] == property->base.id) { + if (obj->properties->properties[i] == property) { *val = obj->properties->values[i]; return 0; } @@ -4194,14 +4301,24 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector, } EXPORT_SYMBOL(drm_mode_connector_update_edid_property); -static bool drm_property_change_is_valid(struct drm_property *property, - uint64_t value) +/* Some properties could refer to dynamic refcnt'd objects, or things that + * need special locking to handle lifetime issues (ie. to ensure the prop + * value doesn't become invalid part way through the property update due to + * race). The value returned by reference via 'obj' should be passed back + * to drm_property_change_valid_put() after the property is set (and the + * object to which the property is attached has a chance to take it's own + * reference). + */ +bool drm_property_change_valid_get(struct drm_property *property, + uint64_t value, struct drm_mode_object **ref) { int i; if (property->flags & DRM_MODE_PROP_IMMUTABLE) return false; + *ref = NULL; + if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) { if (value < property->values[0] || value > property->values[1]) return false; @@ -4223,20 +4340,29 @@ static bool drm_property_change_is_valid(struct drm_property *property, /* 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; + + /* handle refcnt'd objects specially: */ + if (property->values[0] == DRM_MODE_OBJECT_FB) { + struct drm_framebuffer *fb; + fb = drm_framebuffer_lookup(property->dev, value); + if (fb) { + *ref = &fb->base; + return true; + } else { + return false; + } + } else { + return _object_find(property->dev, value, property->values[0]) != NULL; + } + } else { + int i; + for (i = 0; i < property->num_values; i++) + if (property->values[i] == value) + return true; + return false; } for (i = 0; i < property->num_values; i++) @@ -4245,6 +4371,18 @@ static bool drm_property_change_is_valid(struct drm_property *property, return false; } +void drm_property_change_valid_put(struct drm_property *property, + struct drm_mode_object *ref) +{ + if (!ref) + return; + + if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { + if (property->values[0] == DRM_MODE_OBJECT_FB) + drm_framebuffer_unreference(obj_to_fb(ref)); + } +} + /** * drm_mode_connector_property_set_ioctl - set the current value of a connector property * @dev: DRM device @@ -4360,11 +4498,6 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, struct drm_mode_obj_get_properties *arg = data; struct drm_mode_object *obj; int ret = 0; - int i; - int copied = 0; - int props_count = 0; - uint32_t __user *props_ptr; - uint64_t __user *prop_values_ptr; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; @@ -4381,30 +4514,11 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, goto out; } - props_count = obj->properties->count; + ret = get_properties(obj, file_priv->atomic, + (uint32_t __user *)(unsigned long)(arg->props_ptr), + (uint64_t __user *)(unsigned long)(arg->prop_values_ptr), + &arg->count_props); - /* This ioctl is called twice, once to determine how much space is - * needed, and the 2nd time to fill it. */ - if ((arg->count_props >= props_count) && props_count) { - copied = 0; - props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr); - prop_values_ptr = (uint64_t __user *)(unsigned long) - (arg->prop_values_ptr); - for (i = 0; i < props_count; i++) { - if (put_user(obj->properties->ids[i], - props_ptr + copied)) { - ret = -EFAULT; - goto out; - } - if (put_user(obj->properties->values[i], - prop_values_ptr + copied)) { - ret = -EFAULT; - goto out; - } - copied++; - } - } - arg->count_props = props_count; out: drm_modeset_unlock_all(dev); return ret; @@ -4433,8 +4547,8 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, struct drm_mode_object *arg_obj; struct drm_mode_object *prop_obj; struct drm_property *property; - int ret = -EINVAL; - int i; + int i, ret = -EINVAL; + struct drm_mode_object *ref; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; @@ -4450,7 +4564,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, goto out; for (i = 0; i < arg_obj->properties->count; i++) - if (arg_obj->properties->ids[i] == arg->prop_id) + if (arg_obj->properties->properties[i]->base.id == arg->prop_id) break; if (i == arg_obj->properties->count) @@ -4464,7 +4578,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, } property = obj_to_property(prop_obj); - if (!drm_property_change_is_valid(property, arg->value)) + if (!drm_property_change_valid_get(property, arg->value, &ref)) goto out; switch (arg_obj->type) { @@ -4481,6 +4595,8 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, break; } + drm_property_change_valid_put(property, ref); + out: drm_modeset_unlock_all(dev); return ret; @@ -5225,8 +5341,7 @@ void drm_mode_config_init(struct drm_device *dev) idr_init(&dev->mode_config.tile_idr); drm_modeset_lock_all(dev); - drm_mode_create_standard_connector_properties(dev); - drm_mode_create_standard_plane_properties(dev); + drm_mode_create_standard_properties(dev); drm_modeset_unlock_all(dev); /* Just to be sure */ diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index d552708409de..b1979e7bdc88 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -946,6 +946,7 @@ int drm_helper_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mod crtc_state = kzalloc(sizeof(*crtc_state), GFP_KERNEL); if (!crtc_state) return -ENOMEM; + crtc_state->crtc = crtc; crtc_state->enable = true; crtc_state->planes_changed = true; @@ -1005,6 +1006,7 @@ int drm_helper_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, plane_state = kzalloc(sizeof(*plane_state), GFP_KERNEL); if (!plane_state) return -ENOMEM; + plane_state->plane = plane; plane_state->crtc = crtc; drm_atomic_set_fb_for_plane(plane_state, crtc->primary->fb); diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index a2945ee6d675..247dc8b62564 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -36,3 +36,9 @@ int drm_mode_object_get(struct drm_device *dev, void drm_mode_object_put(struct drm_device *dev, struct drm_mode_object *object); +/* drm_atomic.c */ +int drm_atomic_get_property(struct drm_mode_object *obj, + struct drm_property *property, uint64_t *val); +int drm_mode_atomic_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); + diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 4f41377b0b80..d51213464672 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -40,15 +40,19 @@ unsigned int drm_debug = 0; /* 1 to enable debug output */ EXPORT_SYMBOL(drm_debug); +bool drm_atomic = 0; + MODULE_AUTHOR(CORE_AUTHOR); MODULE_DESCRIPTION(CORE_DESC); MODULE_LICENSE("GPL and additional rights"); MODULE_PARM_DESC(debug, "Enable debug output"); +MODULE_PARM_DESC(atomic, "Enable experimental atomic KMS API"); MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs] (0: never disable, <0: disable immediately)"); MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]"); MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps"); module_param_named(debug, drm_debug, int, 0600); +module_param_named_unsafe(atomic, drm_atomic, bool, 0600); static DEFINE_SPINLOCK(drm_minor_lock); static struct idr drm_minors_idr; diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 00587a1e3c83..3785d66721f2 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -32,6 +32,7 @@ #include <drm/drm_core.h> #include "drm_legacy.h" #include "drm_internal.h" +#include "drm_crtc_internal.h" #include <linux/pci.h> #include <linux/export.h> @@ -345,6 +346,17 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv) return -EINVAL; file_priv->universal_planes = req->value; break; + case DRM_CLIENT_CAP_ATOMIC: + /* for now, hide behind experimental drm.atomic moduleparam */ + if (!drm_atomic) + return -EINVAL; + if (!drm_core_check_feature(dev, DRIVER_ATOMIC)) + return -EINVAL; + if (req->value > 1) + return -EINVAL; + file_priv->atomic = req->value; + file_priv->universal_planes = req->value; + break; default: return -EINVAL; } @@ -620,6 +632,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR2, drm_mode_cursor2_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATOMIC, drm_mode_atomic_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), }; #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index 391dfb7d1086..f24c4cfe674b 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -523,6 +523,7 @@ int drm_plane_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, plane_state = kzalloc(sizeof(*plane_state), GFP_KERNEL); if (!plane_state) return -ENOMEM; + plane_state->plane = plane; plane_state->crtc = crtc; drm_atomic_set_fb_for_plane(plane_state, fb); @@ -569,6 +570,7 @@ int drm_plane_helper_disable(struct drm_plane *plane) plane_state = kzalloc(sizeof(*plane_state), GFP_KERNEL); if (!plane_state) return -ENOMEM; + plane_state->plane = plane; plane_state->crtc = NULL; drm_atomic_set_fb_for_plane(plane_state, NULL); diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index 26e5fdea6594..fc76f630e5b1 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -113,6 +113,7 @@ static void mdp5_plane_reset(struct drm_plane *plane) } else { mdp5_state->zpos = 1 + drm_plane_index(plane); } + mdp5_state->base.plane = plane; plane->state = &mdp5_state->base; } diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index 191968256c58..2c396540e279 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -127,6 +127,26 @@ static void add_fb(struct msm_commit *c, struct drm_framebuffer *fb) } +int msm_atomic_check(struct drm_device *dev, + struct drm_atomic_state *state) +{ + int ret; + + /* + * msm ->atomic_check can update ->mode_changed for pixel format + * changes, hence must be run before we check the modeset changes. + */ + ret = drm_atomic_helper_check_planes(dev, state); + if (ret) + return ret; + + ret = drm_atomic_helper_check_modeset(dev, state); + if (ret) + return ret; + + return ret; +} + /** * drm_atomic_helper_commit - commit validated state object * @dev: DRM device diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 9a61546a0b05..f1ebedde6346 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -29,7 +29,7 @@ static void msm_fb_output_poll_changed(struct drm_device *dev) static const struct drm_mode_config_funcs mode_config_funcs = { .fb_create = msm_framebuffer_create, .output_poll_changed = msm_fb_output_poll_changed, - .atomic_check = drm_atomic_helper_check, + .atomic_check = msm_atomic_check, .atomic_commit = msm_atomic_commit, }; diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index b69ef2d5a26c..22e5391a7ce8 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -148,6 +148,8 @@ void __msm_fence_worker(struct work_struct *work); (_cb)->func = _func; \ } while (0) +int msm_atomic_check(struct drm_device *dev, + struct drm_atomic_state *state); int msm_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, bool async); |