Message ID | 1381771608-15237-11-git-send-email-robdclark@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Mon, Oct 14, 2013 at 01:26:45PM -0400, Rob Clark wrote: > Break the mutable state of a plane out into a separate structure > and use atomic properties mechanism to set plane attributes. This > makes it easier to have some helpers for plane->set_property() > and for checking for invalid params. The idea is that individual > drivers can wrap the state struct in their own struct which adds > driver specific parameters, for easy build-up of state across > multiple set_property() calls and for easy atomic commit or roll- > back. > > The same should be done for CRTC, encoder, and connector, but this > patch only includes the first part (plane). > --- > drivers/gpu/drm/drm_atomic_helper.c | 137 +++++++++- > drivers/gpu/drm/drm_crtc.c | 399 +++++++++++++++++++--------- > drivers/gpu/drm/drm_fb_helper.c | 17 +- > drivers/gpu/drm/exynos/exynos_drm_crtc.c | 4 +- > drivers/gpu/drm/exynos/exynos_drm_encoder.c | 6 +- > drivers/gpu/drm/exynos/exynos_drm_plane.c | 13 +- > drivers/gpu/drm/i915/intel_sprite.c | 19 +- > drivers/gpu/drm/msm/mdp4/mdp4_crtc.c | 2 +- > drivers/gpu/drm/msm/mdp4/mdp4_plane.c | 18 +- > drivers/gpu/drm/omapdrm/omap_crtc.c | 4 +- > drivers/gpu/drm/omapdrm/omap_drv.c | 2 +- > drivers/gpu/drm/omapdrm/omap_plane.c | 30 ++- > drivers/gpu/drm/rcar-du/rcar_du_plane.c | 5 +- > drivers/gpu/drm/shmobile/shmob_drm_crtc.c | 2 +- > drivers/gpu/drm/shmobile/shmob_drm_plane.c | 6 +- > drivers/gpu/host1x/drm/dc.c | 16 +- > include/drm/drm_atomic_helper.h | 37 ++- > include/drm/drm_crtc.h | 88 +++++- > 18 files changed, 615 insertions(+), 190 deletions(-) > > diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c > index 46c67b8..0618113 100644 > --- a/drivers/gpu/drm/drm_atomic_helper.c > +++ b/drivers/gpu/drm/drm_atomic_helper.c > @@ -39,10 +39,12 @@ > void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags) > { > struct drm_atomic_helper_state *state; > + int nplanes = dev->mode_config.num_plane; > int sz; > void *ptr; > > sz = sizeof(*state); > + sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes; > > ptr = kzalloc(sz, GFP_KERNEL); > > @@ -52,6 +54,13 @@ void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags) > kref_init(&state->refcount); > state->dev = dev; > state->flags = flags; > + > + state->planes = ptr; > + ptr = &state->planes[nplanes]; > + > + state->pstates = ptr; > + ptr = &state->pstates[nplanes]; > + > return state; > } > EXPORT_SYMBOL(drm_atomic_helper_begin); > @@ -87,7 +96,19 @@ EXPORT_SYMBOL(drm_atomic_helper_set_event); > */ > int drm_atomic_helper_check(struct drm_device *dev, void *state) > { > - return 0; /* for now */ > + struct drm_atomic_helper_state *a = state; > + int nplanes = dev->mode_config.num_plane; > + int i, ret = 0; > + > + for (i = 0; i < nplanes; i++) { > + if (a->planes[i]) { > + ret = drm_atomic_check_plane_state(a->planes[i], a->pstates[i]); > + if (ret) > + break; > + } > + } > + > + return ret; > } > EXPORT_SYMBOL(drm_atomic_helper_check); > > @@ -104,7 +125,19 @@ EXPORT_SYMBOL(drm_atomic_helper_check); > */ > int drm_atomic_helper_commit(struct drm_device *dev, void *state) > { > - return 0; /* for now */ > + struct drm_atomic_helper_state *a = state; > + int nplanes = dev->mode_config.num_plane; > + int i, ret = 0; > + > + for (i = 0; i < nplanes; i++) { > + if (a->planes[i]) { > + ret = drm_atomic_commit_plane_state(a->planes[i], a->pstates[i]); > + if (ret) > + break; > + } > + } > + > + return ret; > } > EXPORT_SYMBOL(drm_atomic_helper_commit); > > @@ -125,11 +158,111 @@ void _drm_atomic_helper_state_free(struct kref *kref) > { > struct drm_atomic_helper_state *state = > container_of(kref, struct drm_atomic_helper_state, refcount); > + struct drm_device *dev = state->dev; > + int nplanes = dev->mode_config.num_plane; > + int i; > + > + for (i = 0; i < nplanes; i++) { > + if (state->pstates[i]) { > + state->planes[i]->state->state = NULL; > + kfree(state->pstates[i]); > + } > + } > + > kfree(state); > } > EXPORT_SYMBOL(_drm_atomic_helper_state_free); > > +int drm_atomic_helper_plane_set_property(struct drm_plane *plane, void *state, > + struct drm_property *property, uint64_t val, void *blob_data) > +{ > + return drm_plane_set_property(plane, > + drm_atomic_get_plane_state(plane, state), > + property, val, blob_data); > +} > +EXPORT_SYMBOL(drm_atomic_helper_plane_set_property); > + > +void drm_atomic_helper_init_plane_state(struct drm_plane *plane, > + struct drm_plane_state *pstate, void *state) > +{ > + /* snapshot current state: */ > + *pstate = *plane->state; > + pstate->state = state; > +} > +EXPORT_SYMBOL(drm_atomic_helper_init_plane_state); > + > +static struct drm_plane_state * > +drm_atomic_helper_get_plane_state(struct drm_plane *plane, void *state) > +{ > + struct drm_atomic_helper_state *a = state; > + struct drm_plane_state *pstate = a->pstates[plane->id]; > + if (!pstate) { > + pstate = kzalloc(sizeof(*pstate), GFP_KERNEL); > + drm_atomic_helper_init_plane_state(plane, pstate, state); > + a->planes[plane->id] = plane; > + a->pstates[plane->id] = pstate; > + } > + return pstate; > +} > + > +static void > +swap_plane_state(struct drm_plane *plane, struct drm_atomic_helper_state *a) > +{ > + swap(plane->state, a->pstates[plane->id]); > + plane->base.propvals = &plane->state->propvals; > +} > + > +static int > +drm_atomic_helper_commit_plane_state(struct drm_plane *plane, > + struct drm_plane_state *pstate) > +{ > + struct drm_device *dev = plane->dev; > + struct drm_framebuffer *old_fb = NULL, *fb = NULL; > + int ret = 0; > + > + /* probably more fine grain locking would be ok of old crtc > + * and new crtc were same.. > + */ > + drm_modeset_lock_all(dev); > + > + fb = pstate->fb; > + > + if (pstate->crtc && fb) { > + ret = plane->funcs->update_plane(plane, pstate->crtc, pstate->fb, > + pstate->crtc_x, pstate->crtc_y, pstate->crtc_w, pstate->crtc_h, > + pstate->src_x, pstate->src_y, pstate->src_w, pstate->src_h); > + if (!ret) { > + /* on success, update state and fb refcnting: */ > + /* NOTE: if we ensure no driver sets plane->state->fb = NULL > + * on disable, we can move this up a level and not duplicate > + * nearly the same thing for both update_plane and disable_plane > + * cases.. I leave it like this for now to be paranoid due to > + * the slightly different ordering in the two cases in the > + * original code. > + */ > + old_fb = plane->state->fb; > + swap_plane_state(plane, pstate->state); > + fb = NULL; > + } > + } else { > + old_fb = plane->state->fb; > + plane->funcs->disable_plane(plane); > + swap_plane_state(plane, pstate->state); > + } > + > + drm_modeset_unlock_all(dev); > + > + if (fb) > + drm_framebuffer_unreference(fb); > + if (old_fb) > + drm_framebuffer_unreference(old_fb); > + > + return ret; > +} > > const struct drm_atomic_helper_funcs drm_atomic_helper_funcs = { > + .get_plane_state = drm_atomic_helper_get_plane_state, > + .check_plane_state = drm_plane_check_state, > + .commit_plane_state = drm_atomic_helper_commit_plane_state, > }; > EXPORT_SYMBOL(drm_atomic_helper_funcs); > diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c > index 9f46f3b..3cf235e 100644 > --- a/drivers/gpu/drm/drm_crtc.c > +++ b/drivers/gpu/drm/drm_crtc.c > @@ -595,7 +595,20 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) > * in this manner. > */ > if (atomic_read(&fb->refcount.refcount) > 1) { > + void *state; > + > + state = dev->driver->atomic_begin(dev, 0); > + if (IS_ERR(state)) { > + DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n"); > + return; > + } > + > + /* TODO once CRTC is converted to state/properties, we can push the > + * locking down into drm_atomic_helper_commit(), since that is where > + * the actual changes take place.. > + */ > drm_modeset_lock_all(dev); > + > /* remove from any CRTC */ > list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { > if (crtc->fb == fb) { > @@ -610,9 +623,18 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) > } > > list_for_each_entry(plane, &dev->mode_config.plane_list, head) { > - if (plane->fb == fb) > - drm_plane_force_disable(plane); > + if (plane->state->fb == fb) > + drm_plane_force_disable(plane, state); > } > + > + /* just disabling stuff shouldn't fail, hopefully: */ > + if(dev->driver->atomic_check(dev, state)) > + DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n"); > + else > + dev->driver->atomic_commit(dev, state); > + > + dev->driver->atomic_end(dev, state); > + > drm_modeset_unlock_all(dev); > } > > @@ -904,8 +926,12 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, > const uint32_t *formats, uint32_t format_count, > bool priv) > { > + struct drm_mode_config *config = &dev->mode_config; > int ret; > > + if (!plane->state) > + plane->state = kzalloc(sizeof(plane->state), GFP_KERNEL); > + > drm_modeset_lock_all(dev); > > ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); > @@ -913,7 +939,7 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, > goto out; > > plane->base.properties = &plane->properties; > - plane->base.propvals = &plane->propvals; > + plane->base.propvals = &plane->state->propvals; > plane->dev = dev; > plane->funcs = funcs; > plane->format_types = kmalloc(sizeof(uint32_t) * format_count, > @@ -935,11 +961,23 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, > */ > if (!priv) { > list_add_tail(&plane->head, &dev->mode_config.plane_list); > + plane->id = dev->mode_config.num_plane; > dev->mode_config.num_plane++; > } else { > INIT_LIST_HEAD(&plane->head); > } > > + 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); > + > out: > drm_modeset_unlock_all(dev); > > @@ -971,6 +1009,111 @@ void drm_plane_cleanup(struct drm_plane *plane) > } > EXPORT_SYMBOL(drm_plane_cleanup); > > +int drm_plane_check_state(struct drm_plane *plane, > + struct drm_plane_state *state) > +{ > + unsigned int fb_width, fb_height; > + struct drm_framebuffer *fb = state->fb; > + int i; > + > + /* disabling the plane is allowed: */ > + if (!fb) > + return 0; > + > + fb_width = fb->width << 16; > + fb_height = fb->height << 16; > + > + /* Check whether this plane supports the fb pixel format. */ > + for (i = 0; i < plane->format_count; i++) > + if (fb->pixel_format == plane->format_types[i]) > + break; > + if (i == plane->format_count) { > + DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format); > + return -EINVAL; > + } > + > + /* 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; > + } > + > + /* 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; > + } > + > + return 0; > +} > +EXPORT_SYMBOL(drm_plane_check_state); > + > +void drm_plane_commit_state(struct drm_plane *plane, > + struct drm_plane_state *state) > +{ > + plane->state = state; > + plane->base.propvals = &state->propvals; > +} > +EXPORT_SYMBOL(drm_plane_commit_state); > + > +int drm_plane_set_property(struct drm_plane *plane, > + struct drm_plane_state *state, > + struct drm_property *property, > + uint64_t value, void *blob_data) > +{ > + struct drm_device *dev = plane->dev; > + struct drm_mode_config *config = &dev->mode_config; > + > + drm_object_property_set_value(&plane->base, > + &state->propvals, property, value, blob_data); > + > + if (property == config->prop_fb_id) { > + state->new_fb = true; > + state->fb = drm_framebuffer_lookup(dev, value); > + } else if (property == config->prop_crtc_id) { > + struct drm_mode_object *obj = drm_property_get_obj(property, value); > + state->crtc = obj ? obj_to_crtc(obj) : NULL; > + } else if (property == config->prop_crtc_x) { > + state->crtc_x = U642I64(value); > + } else if (property == config->prop_crtc_y) { > + state->crtc_y = U642I64(value); > + } else if (property == config->prop_crtc_w) { > + state->crtc_w = value; > + } else if (property == config->prop_crtc_h) { > + state->crtc_h = value; > + } else if (property == config->prop_src_x) { > + state->src_x = value; > + } else if (property == config->prop_src_y) { > + state->src_y = value; > + } else if (property == config->prop_src_w) { > + state->src_w = value; > + } else if (property == config->prop_src_h) { > + state->src_h = value; > + } else { > + return -EINVAL; > + } > + > + return 0; > +} > +EXPORT_SYMBOL(drm_plane_set_property); > + > /** > * drm_plane_force_disable - Forcibly disable a plane > * @plane: plane to disable > @@ -980,20 +1123,15 @@ EXPORT_SYMBOL(drm_plane_cleanup); > * Used when the plane's current framebuffer is destroyed, > * and when restoring fbdev mode. > */ > -void drm_plane_force_disable(struct drm_plane *plane) > +void drm_plane_force_disable(struct drm_plane *plane, void *state) > { > - int ret; > + struct drm_mode_config *config = &plane->dev->mode_config; > > - if (!plane->fb) > - return; > - > - ret = plane->funcs->disable_plane(plane); > - if (ret) > - DRM_ERROR("failed to disable plane with busy fb\n"); > - /* disconnect the plane from the fb and crtc: */ > - __drm_framebuffer_unreference(plane->fb); > - plane->fb = NULL; > - plane->crtc = NULL; > + /* should turn off the crtc */ > + drm_mode_plane_set_obj_prop(plane, state, > + config->prop_crtc_id, 0, NULL); > + drm_mode_plane_set_obj_prop(plane, state, > + config->prop_fb_id, 0, NULL); > } > EXPORT_SYMBOL(drm_plane_force_disable); > > @@ -1043,21 +1181,89 @@ EXPORT_SYMBOL(drm_mode_destroy); > > static int drm_mode_create_standard_connector_properties(struct drm_device *dev) > { > - struct drm_property *edid; > - struct drm_property *dpms; > + 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; > + if (!prop) > + return -ENOMEM; > + dev->mode_config.dpms_property = prop; > + > + > + /* TODO we need the driver to control which of these are dynamic > + * and which are not.. or maybe we should just set all to zero > + * and let the individual drivers frob the prop->flags for the > + * properties they can support dynamic changes on.. > + */ > + > + prop = drm_property_create_range(dev, DRM_MODE_PROP_DYNAMIC, > + "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_DYNAMIC, > + "SRC_Y", 0, UINT_MAX); > + if (!prop) > + return -ENOMEM; > + dev->mode_config.prop_src_y = prop; > + > + prop = drm_property_create_range(dev, 0, "SRC_W", 0, UINT_MAX); > + if (!prop) > + return -ENOMEM; > + dev->mode_config.prop_src_w = prop; > + > + prop = drm_property_create_range(dev, 0, "SRC_H", 0, UINT_MAX); > + if (!prop) > + return -ENOMEM; > + dev->mode_config.prop_src_h = prop; > + > + prop = drm_property_create_range(dev, > + DRM_MODE_PROP_DYNAMIC | DRM_MODE_PROP_SIGNED, > + "CRTC_X", I642U64(INT_MIN), I642U64(INT_MAX)); > + if (!prop) > + return -ENOMEM; > + dev->mode_config.prop_crtc_x = prop; > + > + prop = drm_property_create_range(dev, > + DRM_MODE_PROP_DYNAMIC | DRM_MODE_PROP_SIGNED, > + "CRTC_Y", I642U64(INT_MIN), I642U64(INT_MAX)); > + if (!prop) > + return -ENOMEM; > + dev->mode_config.prop_crtc_y = prop; > + > + prop = drm_property_create_range(dev, 0, "CRTC_W", 0, INT_MAX); > + if (!prop) > + return -ENOMEM; > + dev->mode_config.prop_crtc_w = prop; > + > + prop = drm_property_create_range(dev, 0, "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_DYNAMIC, > + "FB_ID", DRM_MODE_OBJECT_FB); > + if (!prop) > + return -ENOMEM; > + dev->mode_config.prop_fb_id = prop; > + > + prop = drm_property_create_object(dev, 0, > + "CRTC_ID", DRM_MODE_OBJECT_CRTC); > + if (!prop) > + return -ENOMEM; > + dev->mode_config.prop_crtc_id = prop; > > return 0; > } > @@ -1842,13 +2048,13 @@ int drm_mode_getplane(struct drm_device *dev, void *data, > } > plane = obj_to_plane(obj); > > - if (plane->crtc) > - plane_resp->crtc_id = plane->crtc->base.id; > + if (plane->state->crtc) > + plane_resp->crtc_id = plane->state->crtc->base.id; > else > plane_resp->crtc_id = 0; > > - if (plane->fb) > - plane_resp->fb_id = plane->fb->base.id; > + if (plane->state->fb) > + plane_resp->fb_id = plane->state->fb->base.id; > else > plane_resp->fb_id = 0; > > @@ -1890,21 +2096,18 @@ int drm_mode_setplane(struct drm_device *dev, void *data, > struct drm_file *file_priv) > { > struct drm_mode_set_plane *plane_req = data; > + struct drm_mode_config *config = &dev->mode_config; > struct drm_mode_object *obj; > - struct drm_plane *plane; > - struct drm_crtc *crtc; > - struct drm_framebuffer *fb = NULL, *old_fb = NULL; > + void *state; > int ret = 0; > - unsigned int fb_width, fb_height; > - int i; > > if (!drm_core_check_feature(dev, DRIVER_MODESET)) > return -EINVAL; > > - /* > - * First, find the plane, crtc, and fb objects. If not available, > - * we don't bother to call the driver. > - */ > + state = dev->driver->atomic_begin(dev, 0); > + if (IS_ERR(state)) > + return PTR_ERR(state); > + > obj = drm_mode_object_find(dev, plane_req->plane_id, > DRM_MODE_OBJECT_PLANE); > if (!obj) { > @@ -1912,102 +2115,36 @@ int drm_mode_setplane(struct drm_device *dev, void *data, > 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; > - drm_modeset_unlock_all(dev); > - goto out; > - } > > - obj = drm_mode_object_find(dev, plane_req->crtc_id, > - DRM_MODE_OBJECT_CRTC); > - if (!obj) { > - DRM_DEBUG_KMS("Unknown crtc ID %d\n", > - plane_req->crtc_id); > - ret = -ENOENT; > - goto out; > - } > - crtc = obj_to_crtc(obj); > - > - fb = drm_framebuffer_lookup(dev, plane_req->fb_id); > - if (!fb) { > - DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", > - plane_req->fb_id); > - ret = -ENOENT; > - goto out; > - } > - > - /* Check whether this plane supports the fb pixel format. */ > - for (i = 0; i < plane->format_count; i++) > - if (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(fb->pixel_format)); > - ret = -EINVAL; > - goto out; > - } > - > - fb_width = fb->width << 16; > - fb_height = fb->height << 16; > - > - /* Make sure source coordinates are inside the fb. */ > - if (plane_req->src_w > fb_width || > - plane_req->src_x > fb_width - plane_req->src_w || > - plane_req->src_h > fb_height || > - plane_req->src_y > fb_height - plane_req->src_h) { > - DRM_DEBUG_KMS("Invalid source coordinates " > - "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", > - plane_req->src_w >> 16, > - ((plane_req->src_w & 0xffff) * 15625) >> 10, > - plane_req->src_h >> 16, > - ((plane_req->src_h & 0xffff) * 15625) >> 10, > - plane_req->src_x >> 16, > - ((plane_req->src_x & 0xffff) * 15625) >> 10, > - plane_req->src_y >> 16, > - ((plane_req->src_y & 0xffff) * 15625) >> 10); > - ret = -ENOSPC; > - goto out; > - } > - > - /* Give drivers some help against integer overflows */ > - if (plane_req->crtc_w > INT_MAX || > - plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w || > - plane_req->crtc_h > INT_MAX || > - plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) { > - DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", > - plane_req->crtc_w, plane_req->crtc_h, > - plane_req->crtc_x, plane_req->crtc_y); > - ret = -ERANGE; > + ret = > + drm_mode_set_obj_prop(obj, state, > + config->prop_crtc_id, plane_req->crtc_id, NULL) || > + drm_mode_set_obj_prop(obj, state, > + config->prop_fb_id, plane_req->fb_id, NULL) || > + drm_mode_set_obj_prop(obj, state, > + config->prop_crtc_x, I642U64(plane_req->crtc_x), NULL) || > + drm_mode_set_obj_prop(obj, state, > + config->prop_crtc_y, I642U64(plane_req->crtc_y), NULL) || > + drm_mode_set_obj_prop(obj, state, > + config->prop_crtc_w, plane_req->crtc_w, NULL) || > + drm_mode_set_obj_prop(obj, state, > + config->prop_crtc_h, plane_req->crtc_h, NULL) || > + drm_mode_set_obj_prop(obj, state, > + config->prop_src_w, plane_req->src_w, NULL) || > + drm_mode_set_obj_prop(obj, state, > + config->prop_src_h, plane_req->src_h, NULL) || > + drm_mode_set_obj_prop(obj, state, > + config->prop_src_x, plane_req->src_x, NULL) || > + drm_mode_set_obj_prop(obj, state, > + config->prop_src_y, plane_req->src_y, NULL) || > + dev->driver->atomic_check(dev, state); > + if (ret) > goto out; > - } > > - drm_modeset_lock_all(dev); > - 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; > - } > - drm_modeset_unlock_all(dev); > + ret = dev->driver->atomic_commit(dev, state); > > out: > - if (fb) > - drm_framebuffer_unreference(fb); > - if (old_fb) > - drm_framebuffer_unreference(old_fb); > - > + dev->driver->atomic_end(dev, state); > return ret; > } > > @@ -3296,7 +3433,7 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev, > return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv); > } > > -static int drm_mode_connector_set_obj_prop(struct drm_connector *connector, > +int drm_mode_connector_set_obj_prop(struct drm_connector *connector, > void *state, struct drm_property *property, > uint64_t value, void *blob_data) > { > @@ -3319,8 +3456,9 @@ static int drm_mode_connector_set_obj_prop(struct drm_connector *connector, > > return ret; > } > +EXPORT_SYMBOL(drm_mode_connector_set_obj_prop); > > -static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, > +int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, > void *state, struct drm_property *property, > uint64_t value, void *blob_data) > { > @@ -3335,8 +3473,9 @@ static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, > > return ret; > } > +EXPORT_SYMBOL(drm_mode_crtc_set_obj_prop); > > -static int drm_mode_plane_set_obj_prop(struct drm_plane *plane, > +int drm_mode_plane_set_obj_prop(struct drm_plane *plane, > void *state, struct drm_property *property, > uint64_t value, void *blob_data) > { > @@ -3345,12 +3484,10 @@ static int drm_mode_plane_set_obj_prop(struct drm_plane *plane, > if (plane->funcs->set_property) > ret = plane->funcs->set_property(plane, state, property, > value, blob_data); > - if (!ret) > - drm_object_property_set_value(&plane->base, &plane->propvals, > - property, value, NULL); > > return ret; > } > +EXPORT_SYMBOL(drm_mode_plane_set_obj_prop); > > static int drm_mode_set_obj_prop(struct drm_mode_object *obj, > void *state, struct drm_property *property, > diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c > index 24898dc..f1fc605 100644 > --- a/drivers/gpu/drm/drm_fb_helper.c > +++ b/drivers/gpu/drm/drm_fb_helper.c > @@ -290,12 +290,27 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) > struct drm_device *dev = fb_helper->dev; > struct drm_plane *plane; > bool error = false; > + void *state; > int i; > > drm_warn_on_modeset_not_all_locked(dev); > > + state = dev->driver->atomic_begin(dev, 0); > + if (IS_ERR(state)) { > + DRM_ERROR("failed to restore fbdev mode\n"); > + return true; > + } > + > list_for_each_entry(plane, &dev->mode_config.plane_list, head) > - drm_plane_force_disable(plane); > + drm_plane_force_disable(plane, state); > + > + /* just disabling stuff shouldn't fail, hopefully: */ > + if(dev->driver->atomic_check(dev, state)) > + DRM_ERROR("failed to restore fbdev mode\n"); > + else > + dev->driver->atomic_commit(dev, state); I'm seeing some deadlocks here on i915 when we trigger the lastclose handler. We're already holding mode_config.mutex when we enter this function, but drm_atomic_helper_commit_plane_state does another drm_modeset_lock_all() call. Matt > + > + dev->driver->atomic_end(dev, state); > > for (i = 0; i < fb_helper->crtc_count; i++) { > struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; > diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c > index 82a9fca..4ae55b8 100644 > --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c > +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c > @@ -124,8 +124,8 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, > if (ret) > return ret; > > - plane->crtc = crtc; > - plane->fb = crtc->fb; > + plane->state->crtc = crtc; > + plane->state->fb = crtc->fb; > > exynos_drm_fn_encoder(crtc, &pipe, exynos_drm_encoder_crtc_pipe); > > diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c > index 06f1b2a..dbe2e19 100644 > --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c > +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c > @@ -127,7 +127,7 @@ static void disable_plane_to_crtc(struct drm_device *dev, > * (encoder->crtc) > */ > list_for_each_entry(plane, &dev->mode_config.plane_list, head) { > - if (plane->crtc == old_crtc) { > + if (plane->state->crtc == old_crtc) { > /* > * do not change below call order. > * > @@ -138,7 +138,7 @@ static void disable_plane_to_crtc(struct drm_device *dev, > * have new_crtc because new_crtc was set to > * encoder->crtc in advance. > */ > - plane->crtc = new_crtc; > + plane->state->crtc = new_crtc; > plane->funcs->disable_plane(plane); > } > } > @@ -247,7 +247,7 @@ static void exynos_drm_encoder_disable(struct drm_encoder *encoder) > > /* all planes connected to this encoder should be also disabled. */ > list_for_each_entry(plane, &dev->mode_config.plane_list, head) { > - if (plane->crtc == encoder->crtc) > + if (plane->state->crtc == encoder->crtc) > plane->funcs->disable_plane(plane); > } > } > diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c > index 2e31fb8..45d0fa3 100644 > --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c > +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c > @@ -10,6 +10,7 @@ > */ > > #include <drm/drmP.h> > +#include <drm/drm_atomic_helper.h> > > #include <drm/exynos_drm.h> > #include "exynos_drm_drv.h" > @@ -149,7 +150,7 @@ void exynos_plane_commit(struct drm_plane *plane) > struct exynos_plane *exynos_plane = to_exynos_plane(plane); > struct exynos_drm_overlay *overlay = &exynos_plane->overlay; > > - exynos_drm_fn_encoder(plane->crtc, &overlay->zpos, > + exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos, > exynos_drm_encoder_plane_commit); > } > > @@ -162,7 +163,7 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode) > if (exynos_plane->enabled) > return; > > - exynos_drm_fn_encoder(plane->crtc, &overlay->zpos, > + exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos, > exynos_drm_encoder_plane_enable); > > exynos_plane->enabled = true; > @@ -170,7 +171,7 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode) > if (!exynos_plane->enabled) > return; > > - exynos_drm_fn_encoder(plane->crtc, &overlay->zpos, > + exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos, > exynos_drm_encoder_plane_disable); > > exynos_plane->enabled = false; > @@ -192,7 +193,7 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, > if (ret < 0) > return ret; > > - plane->crtc = crtc; > + plane->state->crtc = crtc; > > exynos_plane_commit(plane); > exynos_plane_dpms(plane, DRM_MODE_DPMS_ON); > @@ -229,6 +230,10 @@ static int exynos_plane_set_property(struct drm_plane *plane, > if (property == dev_priv->plane_zpos_property) { > exynos_plane->overlay.zpos = val; > return 0; > + } else { > + return drm_plane_set_property(plane, > + drm_atomic_get_plane_state(plane, state), > + property, val, blob_data); > } > > return -EINVAL; > diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c > index ad6ec4b..d8c2869 100644 > --- a/drivers/gpu/drm/i915/intel_sprite.c > +++ b/drivers/gpu/drm/i915/intel_sprite.c > @@ -868,16 +868,17 @@ intel_disable_plane(struct drm_plane *plane) > { > struct drm_device *dev = plane->dev; > struct intel_plane *intel_plane = to_intel_plane(plane); > + struct drm_plane_state *state = plane->state; > int ret = 0; > > - if (!plane->fb) > + if (!state->fb) > return 0; > > - if (WARN_ON(!plane->crtc)) > + if (WARN_ON(!state->crtc)) > return -EINVAL; > > - intel_enable_primary(plane->crtc); > - intel_plane->disable_plane(plane, plane->crtc); > + intel_enable_primary(state->crtc); > + intel_plane->disable_plane(plane, state->crtc); > > if (!intel_plane->obj) > goto out; > @@ -966,11 +967,12 @@ out_unlock: > void intel_plane_restore(struct drm_plane *plane) > { > struct intel_plane *intel_plane = to_intel_plane(plane); > + struct drm_plane_state *state = plane->state; > > - if (!plane->crtc || !plane->fb) > + if (!state->crtc || !state->fb) > return; > > - intel_update_plane(plane, plane->crtc, plane->fb, > + intel_update_plane(plane, state->crtc, state->fb, > intel_plane->crtc_x, intel_plane->crtc_y, > intel_plane->crtc_w, intel_plane->crtc_h, > intel_plane->src_x, intel_plane->src_y, > @@ -979,7 +981,9 @@ void intel_plane_restore(struct drm_plane *plane) > > void intel_plane_disable(struct drm_plane *plane) > { > - if (!plane->crtc || !plane->fb) > + struct drm_plane_state *state = plane->state; > + > + if (!state->crtc || !state->fb) > return; > > intel_disable_plane(plane); > @@ -989,6 +993,7 @@ static const struct drm_plane_funcs intel_plane_funcs = { > .update_plane = intel_update_plane, > .disable_plane = intel_disable_plane, > .destroy = intel_destroy_plane, > + .set_property = drm_atomic_helper_plane_set_property, > }; > > static uint32_t ilk_plane_formats[] = { > diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c > index d3fd7ca..1d6a6ee 100644 > --- a/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c > +++ b/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c > @@ -243,7 +243,7 @@ static void blend_setup(struct drm_crtc *crtc) > int idx = idxs[pipe_id]; > if (idx > 0) { > const struct mdp4_format *format = > - to_mdp4_format(msm_framebuffer_format(plane->fb)); > + to_mdp4_format(msm_framebuffer_format(plane->state->fb)); > alpha[idx-1] = format->alpha_enable; > } > mixer_cfg |= mixercfg(mdp4_crtc->mixer, pipe_id, stages[idx]); > diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp4/mdp4_plane.c > index 880e96d..8338b1a 100644 > --- a/drivers/gpu/drm/msm/mdp4/mdp4_plane.c > +++ b/drivers/gpu/drm/msm/mdp4/mdp4_plane.c > @@ -45,11 +45,14 @@ static int mdp4_plane_update(struct drm_plane *plane, > uint32_t src_w, uint32_t src_h) > { > struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); > + struct drm_plane_state *state = plane->state; > > mdp4_plane->enabled = true; > > - if (plane->fb) > - drm_framebuffer_unreference(plane->fb); > + if (state->fb) { > + drm_framebuffer_unreference(state->fb); > + state->fb = NULL; > + } > > drm_framebuffer_reference(fb); > > @@ -62,8 +65,8 @@ static int mdp4_plane_disable(struct drm_plane *plane) > { > struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); > DBG("%s: disable", mdp4_plane->name); > - if (plane->crtc) > - mdp4_crtc_detach(plane->crtc, plane); > + if (plane->state->crtc) > + mdp4_crtc_detach(plane->state->crtc, plane); > return 0; > } > > @@ -87,8 +90,9 @@ void mdp4_plane_install_properties(struct drm_plane *plane, > int mdp4_plane_set_property(struct drm_plane *plane, void *state, > struct drm_property *property, uint64_t val, void *blob_data) > { > - // XXX > - return -EINVAL; > + return drm_plane_set_property(plane, > + drm_atomic_get_plane_state(plane, state), > + property, val, blob_data); > } > > static const struct drm_plane_funcs mdp4_plane_funcs = { > @@ -117,7 +121,7 @@ void mdp4_plane_set_scanout(struct drm_plane *plane, > msm_gem_get_iova(msm_framebuffer_bo(fb, 0), mdp4_kms->id, &iova); > mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRCP0_BASE(pipe), iova); > > - plane->fb = fb; > + plane->state->fb = fb; > } > > #define MDP4_VG_PHASE_STEP_DEFAULT 0x20000000 > diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c > index 5dd22ab..d53a689 100644 > --- a/drivers/gpu/drm/omapdrm/omap_crtc.c > +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c > @@ -210,7 +210,7 @@ static void omap_crtc_dpms(struct drm_crtc *crtc, int mode) > /* and any attached overlay planes: */ > for (i = 0; i < priv->num_planes; i++) { > struct drm_plane *plane = priv->planes[i]; > - if (plane->crtc == crtc) > + if (plane->state->crtc == crtc) > WARN_ON(omap_plane_dpms(plane, mode)); > } > } > @@ -651,7 +651,7 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, > > omap_crtc->channel = channel; > omap_crtc->plane = plane; > - omap_crtc->plane->crtc = crtc; > + omap_crtc->plane->state->crtc = crtc; > omap_crtc->name = channel_names[channel]; > omap_crtc->pipe = id; > > diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c > index 225d0e9..0e15e2c 100644 > --- a/drivers/gpu/drm/omapdrm/omap_drv.c > +++ b/drivers/gpu/drm/omapdrm/omap_drv.c > @@ -559,7 +559,7 @@ static void dev_lastclose(struct drm_device *dev) > > for (i = 0; i < priv->num_planes; i++) { > drm_object_property_set_value(&priv->planes[i]->base, > - &priv->planes[i]->propvals, > + &priv->planes[i]->state->propvals, > priv->rotation_prop, 0, NULL); > } > } > diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c > index fe32f1b..d11a879 100644 > --- a/drivers/gpu/drm/omapdrm/omap_plane.c > +++ b/drivers/gpu/drm/omapdrm/omap_plane.c > @@ -116,9 +116,10 @@ static void omap_plane_pre_apply(struct omap_drm_apply *apply) > container_of(apply, struct omap_plane, apply); > struct omap_drm_window *win = &omap_plane->win; > struct drm_plane *plane = &omap_plane->base; > + struct drm_plane_state *state = plane->state; > struct drm_device *dev = plane->dev; > struct omap_overlay_info *info = &omap_plane->info; > - struct drm_crtc *crtc = plane->crtc; > + struct drm_crtc *crtc = state->crtc; > enum omap_channel channel; > bool enabled = omap_plane->enabled && crtc; > bool ilace, replication; > @@ -127,7 +128,7 @@ static void omap_plane_pre_apply(struct omap_drm_apply *apply) > DBG("%s, enabled=%d", omap_plane->name, enabled); > > /* if fb has changed, pin new fb: */ > - update_pin(plane, enabled ? plane->fb : NULL); > + update_pin(plane, enabled ? state->fb : NULL); > > if (!enabled) { > dispc_ovl_enable(omap_plane->id, false); > @@ -137,7 +138,7 @@ static void omap_plane_pre_apply(struct omap_drm_apply *apply) > channel = omap_crtc_channel(crtc); > > /* update scanout: */ > - omap_framebuffer_update_scanout(plane->fb, win, info); > + omap_framebuffer_update_scanout(state->fb, win, info); > > DBG("%dx%d -> %dx%d (%d)", info->width, info->height, > info->out_width, info->out_height, > @@ -179,16 +180,18 @@ static void omap_plane_post_apply(struct omap_drm_apply *apply) > cb.fxn(cb.arg); > > if (omap_plane->enabled) { > - omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y, > + omap_framebuffer_flush(plane->state->fb, > + info->pos_x, info->pos_y, > info->out_width, info->out_height); > } > } > > static int apply(struct drm_plane *plane) > { > - if (plane->crtc) { > + struct drm_plane_state *state = plane->state; > + if (state->crtc) { > struct omap_plane *omap_plane = to_omap_plane(plane); > - return omap_crtc_apply(plane->crtc, &omap_plane->apply); > + return omap_crtc_apply(state->crtc, &omap_plane->apply); > } > return 0; > } > @@ -203,6 +206,7 @@ int omap_plane_mode_set(struct drm_plane *plane, > { > struct omap_plane *omap_plane = to_omap_plane(plane); > struct omap_drm_window *win = &omap_plane->win; > + struct drm_plane_state *state = plane->state; > > win->crtc_x = crtc_x; > win->crtc_y = crtc_y; > @@ -225,8 +229,8 @@ int omap_plane_mode_set(struct drm_plane *plane, > omap_plane->apply_done_cb.arg = arg; > } > > - plane->fb = fb; > - plane->crtc = crtc; > + state->fb = fb; > + state->crtc = crtc; > > return apply(plane); > } > @@ -239,10 +243,12 @@ static int omap_plane_update(struct drm_plane *plane, > uint32_t src_w, uint32_t src_h) > { > struct omap_plane *omap_plane = to_omap_plane(plane); > + struct drm_plane_state *state = plane->state; > + > omap_plane->enabled = true; > > - if (plane->fb) > - drm_framebuffer_unreference(plane->fb); > + if (state->fb) > + drm_framebuffer_unreference(state->fb); > > drm_framebuffer_reference(fb); > > @@ -342,6 +348,10 @@ int omap_plane_set_property(struct drm_plane *plane, void *state, > DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val); > omap_plane->info.zorder = val; > ret = apply(plane); > + } else { > + ret = drm_plane_set_property(plane, > + drm_atomic_get_plane_state(plane, state), > + property, val, blob_data); > } > > return ret; > diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c > index 5691743..909e241 100644 > --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c > +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c > @@ -14,6 +14,7 @@ > #include <drm/drmP.h> > #include <drm/drm_crtc.h> > #include <drm/drm_crtc_helper.h> > +#include <drm/drm_atomic_helper.h> > #include <drm/drm_fb_cma_helper.h> > #include <drm/drm_gem_cma_helper.h> > > @@ -411,7 +412,9 @@ static int rcar_du_plane_set_property(struct drm_plane *plane, > else if (property == rgrp->planes.zpos) > rcar_du_plane_set_zpos(rplane, value); > else > - return -EINVAL; > + return drm_plane_set_property(plane, > + drm_atomic_get_plane_state(plane, state), > + property, value, blob_data); > > return 0; > } > diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c > index 035bd67..e62b0f2 100644 > --- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c > +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c > @@ -238,7 +238,7 @@ static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc) > > /* Setup planes. */ > list_for_each_entry(plane, &dev->mode_config.plane_list, head) { > - if (plane->crtc == crtc) > + if (plane->state->crtc == crtc) > shmob_drm_plane_setup(plane); > } > > diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/shmobile/shmob_drm_plane.c > index 060ae03..22da5c1 100644 > --- a/drivers/gpu/drm/shmobile/shmob_drm_plane.c > +++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.c > @@ -14,6 +14,7 @@ > #include <drm/drmP.h> > #include <drm/drm_crtc.h> > #include <drm/drm_crtc_helper.h> > +#include <drm/drm_atomic_helper.h> > #include <drm/drm_fb_cma_helper.h> > #include <drm/drm_gem_cma_helper.h> > > @@ -166,10 +167,10 @@ void shmob_drm_plane_setup(struct drm_plane *plane) > { > struct shmob_drm_plane *splane = to_shmob_plane(plane); > > - if (plane->fb == NULL) > + if (plane->state->fb == NULL) > return; > > - __shmob_drm_plane_setup(splane, plane->fb); > + __shmob_drm_plane_setup(splane, plane->state->fb); > } > > static int > @@ -228,6 +229,7 @@ static void shmob_drm_plane_destroy(struct drm_plane *plane) > static const struct drm_plane_funcs shmob_drm_plane_funcs = { > .update_plane = shmob_drm_plane_update, > .disable_plane = shmob_drm_plane_disable, > + .set_property = drm_atomic_helper_plane_set_property, > .destroy = shmob_drm_plane_destroy, > }; > > diff --git a/drivers/gpu/host1x/drm/dc.c b/drivers/gpu/host1x/drm/dc.c > index b1a05ad..5766010 100644 > --- a/drivers/gpu/host1x/drm/dc.c > +++ b/drivers/gpu/host1x/drm/dc.c > @@ -75,11 +75,11 @@ static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, > > static int tegra_plane_disable(struct drm_plane *plane) > { > - struct tegra_dc *dc = to_tegra_dc(plane->crtc); > + struct tegra_dc *dc = to_tegra_dc(plane->state->crtc); > struct tegra_plane *p = to_tegra_plane(plane); > unsigned long value; > > - if (!plane->crtc) > + if (!plane->state->crtc) > return 0; > > value = WINDOW_A_SELECT << p->index; > @@ -104,6 +104,7 @@ static void tegra_plane_destroy(struct drm_plane *plane) > static const struct drm_plane_funcs tegra_plane_funcs = { > .update_plane = tegra_plane_update, > .disable_plane = tegra_plane_disable, > + .set_property = drm_atomic_helper_plane_set_property, > .destroy = tegra_plane_destroy, > }; > > @@ -267,13 +268,14 @@ static void tegra_crtc_disable(struct drm_crtc *crtc) > struct drm_plane *plane; > > list_for_each_entry(plane, &drm->mode_config.plane_list, head) { > - if (plane->crtc == crtc) { > + struct drm_plane_state *state = plane->state; > + if (state->crtc == crtc) { > tegra_plane_disable(plane); > - plane->crtc = NULL; > + state->crtc = NULL; > > - if (plane->fb) { > - drm_framebuffer_unreference(plane->fb); > - plane->fb = NULL; > + if (state->fb) { > + drm_framebuffer_unreference(state->fb); > + state->fb = NULL; > } > } > } > diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h > index e70cd7b..d0d29e1 100644 > --- a/include/drm/drm_atomic_helper.h > +++ b/include/drm/drm_atomic_helper.h > @@ -62,7 +62,9 @@ > * struct drm_atomic_helper_funcs - helper funcs used by the atomic helpers > */ > struct drm_atomic_helper_funcs { > - int dummy; /* for now */ > + struct drm_plane_state *(*get_plane_state)(struct drm_plane *plane, void *state); > + int (*check_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate); > + int (*commit_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate); > }; > > const extern struct drm_atomic_helper_funcs drm_atomic_helper_funcs; > @@ -75,6 +77,37 @@ int drm_atomic_helper_check(struct drm_device *dev, void *state); > int drm_atomic_helper_commit(struct drm_device *dev, void *state); > void drm_atomic_helper_end(struct drm_device *dev, void *state); > > +int drm_atomic_helper_plane_set_property(struct drm_plane *plane, void *state, > + struct drm_property *property, uint64_t val, void *blob_data); > +void drm_atomic_helper_init_plane_state(struct drm_plane *plane, > + struct drm_plane_state *pstate, void *state); > + > +static inline struct drm_plane_state * > +drm_atomic_get_plane_state(struct drm_plane *plane, void *state) > +{ > + const struct drm_atomic_helper_funcs *funcs = > + plane->dev->driver->atomic_helpers; > + return funcs->get_plane_state(plane, state); > +} > + > +static inline int > +drm_atomic_check_plane_state(struct drm_plane *plane, > + struct drm_plane_state *pstate) > +{ > + const struct drm_atomic_helper_funcs *funcs = > + plane->dev->driver->atomic_helpers; > + return funcs->check_plane_state(plane, pstate); > +} > + > +static inline int > +drm_atomic_commit_plane_state(struct drm_plane *plane, > + struct drm_plane_state *pstate) > +{ > + const struct drm_atomic_helper_funcs *funcs = > + plane->dev->driver->atomic_helpers; > + return funcs->commit_plane_state(plane, pstate); > +} > + > /** > * struct drm_atomic_helper_state - the state object used by atomic helpers > */ > @@ -82,6 +115,8 @@ struct drm_atomic_helper_state { > struct kref refcount; > struct drm_device *dev; > uint32_t flags; > + struct drm_plane **planes; > + struct drm_plane_state **pstates; > }; > > static inline void > diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h > index 8831562..ee46a6a 100644 > --- a/include/drm/drm_crtc.h > +++ b/include/drm/drm_crtc.h > @@ -675,6 +675,45 @@ struct drm_plane_funcs { > }; > > /** > + * drm_plane_state - mutable plane state > + * @new_fb: has the fb been changed > + * @crtc: currently bound CRTC > + * @fb: currently bound fb > + * @crtc_x: left position of visible portion of plane on crtc > + * @crtc_y: upper position of visible portion of plane on crtc > + * @crtc_w: width of visible portion of plane on crtc > + * @crtc_h: height of visible portion of plane on crtc > + * @src_x: left position of visible portion of plane within > + * plane (in 16.16) > + * @src_y: upper position of visible portion of plane within > + * plane (in 16.16) > + * @src_w: width of visible portion of plane (in 16.16) > + * @src_h: height of visible portion of plane (in 16.16) > + * @propvals: property values > + * @state: current global/toplevel state object (for atomic) while an > + * update is in progress, NULL otherwise. > + */ > +struct drm_plane_state { > + bool new_fb : 1; > + struct drm_crtc *crtc; > + struct drm_framebuffer *fb; > + > + /* Signed dest location allows it to be partially off screen */ > + int32_t crtc_x, crtc_y; > + uint32_t crtc_w, crtc_h; > + > + /* Source values are 16.16 fixed point */ > + uint32_t src_x, src_y; > + uint32_t src_h, src_w; > + > + bool enabled; > + > + struct drm_object_property_values propvals; > + > + void *state; > +}; > + > +/** > * drm_plane - central DRM plane control structure > * @dev: DRM device this plane belongs to > * @head: for list management > @@ -682,8 +721,8 @@ struct drm_plane_funcs { > * @possible_crtcs: pipes this plane can be bound to > * @format_types: array of formats supported by this plane > * @format_count: number of formats supported > - * @crtc: currently bound CRTC > - * @fb: currently bound fb > + * @id: plane number, 0..n > + * @state: the mutable state > * @funcs: helper functions > * @properties: property tracking for this plane > */ > @@ -697,13 +736,17 @@ struct drm_plane { > uint32_t *format_types; > uint32_t format_count; > > - struct drm_crtc *crtc; > - struct drm_framebuffer *fb; > + int id; > + > + /* > + * State that can be updated from userspace, and atomically > + * commited or rolled back: > + */ > + struct drm_plane_state *state; > > const struct drm_plane_funcs *funcs; > > struct drm_object_properties properties; > - struct drm_object_property_values propvals; > }; > > /** > @@ -884,8 +927,20 @@ struct drm_mode_config { > bool poll_running; > struct delayed_work output_poll_work; > > - /* pointers to standard properties */ > + /* just so blob properties can always be in a list: */ > struct list_head property_blob_list; > + > + /* pointers to standard properties */ > + struct drm_property *prop_src_x; > + struct drm_property *prop_src_y; > + struct drm_property *prop_src_w; > + struct drm_property *prop_src_h; > + struct drm_property *prop_crtc_x; > + struct drm_property *prop_crtc_y; > + struct drm_property *prop_crtc_w; > + struct drm_property *prop_crtc_h; > + struct drm_property *prop_fb_id; > + struct drm_property *prop_crtc_id; > struct drm_property *edid_property; > struct drm_property *dpms_property; > > @@ -969,7 +1024,15 @@ extern int drm_plane_init(struct drm_device *dev, > const uint32_t *formats, uint32_t format_count, > bool priv); > extern void drm_plane_cleanup(struct drm_plane *plane); > -extern void drm_plane_force_disable(struct drm_plane *plane); > +extern void drm_plane_force_disable(struct drm_plane *plane, void *state); > +extern int drm_plane_check_state(struct drm_plane *plane, > + struct drm_plane_state *state); > +extern void drm_plane_commit_state(struct drm_plane *plane, > + struct drm_plane_state *state); > +extern int drm_plane_set_property(struct drm_plane *plane, > + struct drm_plane_state *state, > + struct drm_property *property, > + uint64_t value, void *blob_data); > > extern void drm_encoder_cleanup(struct drm_encoder *encoder); > > @@ -1023,6 +1086,17 @@ extern int drm_object_property_set_value(struct drm_mode_object *obj, > extern int drm_object_property_get_value(struct drm_mode_object *obj, > struct drm_property *property, > uint64_t *value); > + > +int drm_mode_connector_set_obj_prop(struct drm_connector *connector, > + void *state, struct drm_property *property, > + uint64_t value, void *blob_data); > +int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, > + void *state, struct drm_property *property, > + uint64_t value, void *blob_data); > +int drm_mode_plane_set_obj_prop(struct drm_plane *plane, > + void *state, struct drm_property *property, > + uint64_t value, void *blob_data); > + > extern int drm_framebuffer_init(struct drm_device *dev, > struct drm_framebuffer *fb, > const struct drm_framebuffer_funcs *funcs); > -- > 1.8.3.1 > > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/dri-devel
On Thu, Oct 24, 2013 at 5:48 PM, Matt Roper <matthew.d.roper@intel.com> wrote: > On Mon, Oct 14, 2013 at 01:26:45PM -0400, Rob Clark wrote: >> Break the mutable state of a plane out into a separate structure >> and use atomic properties mechanism to set plane attributes. This >> makes it easier to have some helpers for plane->set_property() >> and for checking for invalid params. The idea is that individual >> drivers can wrap the state struct in their own struct which adds >> driver specific parameters, for easy build-up of state across >> multiple set_property() calls and for easy atomic commit or roll- >> back. >> >> The same should be done for CRTC, encoder, and connector, but this >> patch only includes the first part (plane). >> --- >> drivers/gpu/drm/drm_atomic_helper.c | 137 +++++++++- >> drivers/gpu/drm/drm_crtc.c | 399 +++++++++++++++++++--------- >> drivers/gpu/drm/drm_fb_helper.c | 17 +- >> drivers/gpu/drm/exynos/exynos_drm_crtc.c | 4 +- >> drivers/gpu/drm/exynos/exynos_drm_encoder.c | 6 +- >> drivers/gpu/drm/exynos/exynos_drm_plane.c | 13 +- >> drivers/gpu/drm/i915/intel_sprite.c | 19 +- >> drivers/gpu/drm/msm/mdp4/mdp4_crtc.c | 2 +- >> drivers/gpu/drm/msm/mdp4/mdp4_plane.c | 18 +- >> drivers/gpu/drm/omapdrm/omap_crtc.c | 4 +- >> drivers/gpu/drm/omapdrm/omap_drv.c | 2 +- >> drivers/gpu/drm/omapdrm/omap_plane.c | 30 ++- >> drivers/gpu/drm/rcar-du/rcar_du_plane.c | 5 +- >> drivers/gpu/drm/shmobile/shmob_drm_crtc.c | 2 +- >> drivers/gpu/drm/shmobile/shmob_drm_plane.c | 6 +- >> drivers/gpu/host1x/drm/dc.c | 16 +- >> include/drm/drm_atomic_helper.h | 37 ++- >> include/drm/drm_crtc.h | 88 +++++- >> 18 files changed, 615 insertions(+), 190 deletions(-) >> >> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c >> index 46c67b8..0618113 100644 >> --- a/drivers/gpu/drm/drm_atomic_helper.c >> +++ b/drivers/gpu/drm/drm_atomic_helper.c >> @@ -39,10 +39,12 @@ >> void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags) >> { >> struct drm_atomic_helper_state *state; >> + int nplanes = dev->mode_config.num_plane; >> int sz; >> void *ptr; >> >> sz = sizeof(*state); >> + sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes; >> >> ptr = kzalloc(sz, GFP_KERNEL); >> >> @@ -52,6 +54,13 @@ void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags) >> kref_init(&state->refcount); >> state->dev = dev; >> state->flags = flags; >> + >> + state->planes = ptr; >> + ptr = &state->planes[nplanes]; >> + >> + state->pstates = ptr; >> + ptr = &state->pstates[nplanes]; >> + >> return state; >> } >> EXPORT_SYMBOL(drm_atomic_helper_begin); >> @@ -87,7 +96,19 @@ EXPORT_SYMBOL(drm_atomic_helper_set_event); >> */ >> int drm_atomic_helper_check(struct drm_device *dev, void *state) >> { >> - return 0; /* for now */ >> + struct drm_atomic_helper_state *a = state; >> + int nplanes = dev->mode_config.num_plane; >> + int i, ret = 0; >> + >> + for (i = 0; i < nplanes; i++) { >> + if (a->planes[i]) { >> + ret = drm_atomic_check_plane_state(a->planes[i], a->pstates[i]); >> + if (ret) >> + break; >> + } >> + } >> + >> + return ret; >> } >> EXPORT_SYMBOL(drm_atomic_helper_check); >> >> @@ -104,7 +125,19 @@ EXPORT_SYMBOL(drm_atomic_helper_check); >> */ >> int drm_atomic_helper_commit(struct drm_device *dev, void *state) >> { >> - return 0; /* for now */ >> + struct drm_atomic_helper_state *a = state; >> + int nplanes = dev->mode_config.num_plane; >> + int i, ret = 0; >> + >> + for (i = 0; i < nplanes; i++) { >> + if (a->planes[i]) { >> + ret = drm_atomic_commit_plane_state(a->planes[i], a->pstates[i]); >> + if (ret) >> + break; >> + } >> + } >> + >> + return ret; >> } >> EXPORT_SYMBOL(drm_atomic_helper_commit); >> >> @@ -125,11 +158,111 @@ void _drm_atomic_helper_state_free(struct kref *kref) >> { >> struct drm_atomic_helper_state *state = >> container_of(kref, struct drm_atomic_helper_state, refcount); >> + struct drm_device *dev = state->dev; >> + int nplanes = dev->mode_config.num_plane; >> + int i; >> + >> + for (i = 0; i < nplanes; i++) { >> + if (state->pstates[i]) { >> + state->planes[i]->state->state = NULL; >> + kfree(state->pstates[i]); >> + } >> + } >> + >> kfree(state); >> } >> EXPORT_SYMBOL(_drm_atomic_helper_state_free); >> >> +int drm_atomic_helper_plane_set_property(struct drm_plane *plane, void *state, >> + struct drm_property *property, uint64_t val, void *blob_data) >> +{ >> + return drm_plane_set_property(plane, >> + drm_atomic_get_plane_state(plane, state), >> + property, val, blob_data); >> +} >> +EXPORT_SYMBOL(drm_atomic_helper_plane_set_property); >> + >> +void drm_atomic_helper_init_plane_state(struct drm_plane *plane, >> + struct drm_plane_state *pstate, void *state) >> +{ >> + /* snapshot current state: */ >> + *pstate = *plane->state; >> + pstate->state = state; >> +} >> +EXPORT_SYMBOL(drm_atomic_helper_init_plane_state); >> + >> +static struct drm_plane_state * >> +drm_atomic_helper_get_plane_state(struct drm_plane *plane, void *state) >> +{ >> + struct drm_atomic_helper_state *a = state; >> + struct drm_plane_state *pstate = a->pstates[plane->id]; >> + if (!pstate) { >> + pstate = kzalloc(sizeof(*pstate), GFP_KERNEL); >> + drm_atomic_helper_init_plane_state(plane, pstate, state); >> + a->planes[plane->id] = plane; >> + a->pstates[plane->id] = pstate; >> + } >> + return pstate; >> +} >> + >> +static void >> +swap_plane_state(struct drm_plane *plane, struct drm_atomic_helper_state *a) >> +{ >> + swap(plane->state, a->pstates[plane->id]); >> + plane->base.propvals = &plane->state->propvals; >> +} >> + >> +static int >> +drm_atomic_helper_commit_plane_state(struct drm_plane *plane, >> + struct drm_plane_state *pstate) >> +{ >> + struct drm_device *dev = plane->dev; >> + struct drm_framebuffer *old_fb = NULL, *fb = NULL; >> + int ret = 0; >> + >> + /* probably more fine grain locking would be ok of old crtc >> + * and new crtc were same.. >> + */ >> + drm_modeset_lock_all(dev); >> + >> + fb = pstate->fb; >> + >> + if (pstate->crtc && fb) { >> + ret = plane->funcs->update_plane(plane, pstate->crtc, pstate->fb, >> + pstate->crtc_x, pstate->crtc_y, pstate->crtc_w, pstate->crtc_h, >> + pstate->src_x, pstate->src_y, pstate->src_w, pstate->src_h); >> + if (!ret) { >> + /* on success, update state and fb refcnting: */ >> + /* NOTE: if we ensure no driver sets plane->state->fb = NULL >> + * on disable, we can move this up a level and not duplicate >> + * nearly the same thing for both update_plane and disable_plane >> + * cases.. I leave it like this for now to be paranoid due to >> + * the slightly different ordering in the two cases in the >> + * original code. >> + */ >> + old_fb = plane->state->fb; >> + swap_plane_state(plane, pstate->state); >> + fb = NULL; >> + } >> + } else { >> + old_fb = plane->state->fb; >> + plane->funcs->disable_plane(plane); >> + swap_plane_state(plane, pstate->state); >> + } >> + >> + drm_modeset_unlock_all(dev); >> + >> + if (fb) >> + drm_framebuffer_unreference(fb); >> + if (old_fb) >> + drm_framebuffer_unreference(old_fb); >> + >> + return ret; >> +} >> >> const struct drm_atomic_helper_funcs drm_atomic_helper_funcs = { >> + .get_plane_state = drm_atomic_helper_get_plane_state, >> + .check_plane_state = drm_plane_check_state, >> + .commit_plane_state = drm_atomic_helper_commit_plane_state, >> }; >> EXPORT_SYMBOL(drm_atomic_helper_funcs); >> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c >> index 9f46f3b..3cf235e 100644 >> --- a/drivers/gpu/drm/drm_crtc.c >> +++ b/drivers/gpu/drm/drm_crtc.c >> @@ -595,7 +595,20 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) >> * in this manner. >> */ >> if (atomic_read(&fb->refcount.refcount) > 1) { >> + void *state; >> + >> + state = dev->driver->atomic_begin(dev, 0); >> + if (IS_ERR(state)) { >> + DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n"); >> + return; >> + } >> + >> + /* TODO once CRTC is converted to state/properties, we can push the >> + * locking down into drm_atomic_helper_commit(), since that is where >> + * the actual changes take place.. >> + */ >> drm_modeset_lock_all(dev); >> + >> /* remove from any CRTC */ >> list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { >> if (crtc->fb == fb) { >> @@ -610,9 +623,18 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) >> } >> >> list_for_each_entry(plane, &dev->mode_config.plane_list, head) { >> - if (plane->fb == fb) >> - drm_plane_force_disable(plane); >> + if (plane->state->fb == fb) >> + drm_plane_force_disable(plane, state); >> } >> + >> + /* just disabling stuff shouldn't fail, hopefully: */ >> + if(dev->driver->atomic_check(dev, state)) >> + DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n"); >> + else >> + dev->driver->atomic_commit(dev, state); >> + >> + dev->driver->atomic_end(dev, state); >> + >> drm_modeset_unlock_all(dev); >> } >> >> @@ -904,8 +926,12 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, >> const uint32_t *formats, uint32_t format_count, >> bool priv) >> { >> + struct drm_mode_config *config = &dev->mode_config; >> int ret; >> >> + if (!plane->state) >> + plane->state = kzalloc(sizeof(plane->state), GFP_KERNEL); >> + >> drm_modeset_lock_all(dev); >> >> ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); >> @@ -913,7 +939,7 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, >> goto out; >> >> plane->base.properties = &plane->properties; >> - plane->base.propvals = &plane->propvals; >> + plane->base.propvals = &plane->state->propvals; >> plane->dev = dev; >> plane->funcs = funcs; >> plane->format_types = kmalloc(sizeof(uint32_t) * format_count, >> @@ -935,11 +961,23 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, >> */ >> if (!priv) { >> list_add_tail(&plane->head, &dev->mode_config.plane_list); >> + plane->id = dev->mode_config.num_plane; >> dev->mode_config.num_plane++; >> } else { >> INIT_LIST_HEAD(&plane->head); >> } >> >> + 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); >> + >> out: >> drm_modeset_unlock_all(dev); >> >> @@ -971,6 +1009,111 @@ void drm_plane_cleanup(struct drm_plane *plane) >> } >> EXPORT_SYMBOL(drm_plane_cleanup); >> >> +int drm_plane_check_state(struct drm_plane *plane, >> + struct drm_plane_state *state) >> +{ >> + unsigned int fb_width, fb_height; >> + struct drm_framebuffer *fb = state->fb; >> + int i; >> + >> + /* disabling the plane is allowed: */ >> + if (!fb) >> + return 0; >> + >> + fb_width = fb->width << 16; >> + fb_height = fb->height << 16; >> + >> + /* Check whether this plane supports the fb pixel format. */ >> + for (i = 0; i < plane->format_count; i++) >> + if (fb->pixel_format == plane->format_types[i]) >> + break; >> + if (i == plane->format_count) { >> + DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format); >> + return -EINVAL; >> + } >> + >> + /* 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; >> + } >> + >> + /* 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; >> + } >> + >> + return 0; >> +} >> +EXPORT_SYMBOL(drm_plane_check_state); >> + >> +void drm_plane_commit_state(struct drm_plane *plane, >> + struct drm_plane_state *state) >> +{ >> + plane->state = state; >> + plane->base.propvals = &state->propvals; >> +} >> +EXPORT_SYMBOL(drm_plane_commit_state); >> + >> +int drm_plane_set_property(struct drm_plane *plane, >> + struct drm_plane_state *state, >> + struct drm_property *property, >> + uint64_t value, void *blob_data) >> +{ >> + struct drm_device *dev = plane->dev; >> + struct drm_mode_config *config = &dev->mode_config; >> + >> + drm_object_property_set_value(&plane->base, >> + &state->propvals, property, value, blob_data); >> + >> + if (property == config->prop_fb_id) { >> + state->new_fb = true; >> + state->fb = drm_framebuffer_lookup(dev, value); >> + } else if (property == config->prop_crtc_id) { >> + struct drm_mode_object *obj = drm_property_get_obj(property, value); >> + state->crtc = obj ? obj_to_crtc(obj) : NULL; >> + } else if (property == config->prop_crtc_x) { >> + state->crtc_x = U642I64(value); >> + } else if (property == config->prop_crtc_y) { >> + state->crtc_y = U642I64(value); >> + } else if (property == config->prop_crtc_w) { >> + state->crtc_w = value; >> + } else if (property == config->prop_crtc_h) { >> + state->crtc_h = value; >> + } else if (property == config->prop_src_x) { >> + state->src_x = value; >> + } else if (property == config->prop_src_y) { >> + state->src_y = value; >> + } else if (property == config->prop_src_w) { >> + state->src_w = value; >> + } else if (property == config->prop_src_h) { >> + state->src_h = value; >> + } else { >> + return -EINVAL; >> + } >> + >> + return 0; >> +} >> +EXPORT_SYMBOL(drm_plane_set_property); >> + >> /** >> * drm_plane_force_disable - Forcibly disable a plane >> * @plane: plane to disable >> @@ -980,20 +1123,15 @@ EXPORT_SYMBOL(drm_plane_cleanup); >> * Used when the plane's current framebuffer is destroyed, >> * and when restoring fbdev mode. >> */ >> -void drm_plane_force_disable(struct drm_plane *plane) >> +void drm_plane_force_disable(struct drm_plane *plane, void *state) >> { >> - int ret; >> + struct drm_mode_config *config = &plane->dev->mode_config; >> >> - if (!plane->fb) >> - return; >> - >> - ret = plane->funcs->disable_plane(plane); >> - if (ret) >> - DRM_ERROR("failed to disable plane with busy fb\n"); >> - /* disconnect the plane from the fb and crtc: */ >> - __drm_framebuffer_unreference(plane->fb); >> - plane->fb = NULL; >> - plane->crtc = NULL; >> + /* should turn off the crtc */ >> + drm_mode_plane_set_obj_prop(plane, state, >> + config->prop_crtc_id, 0, NULL); >> + drm_mode_plane_set_obj_prop(plane, state, >> + config->prop_fb_id, 0, NULL); >> } >> EXPORT_SYMBOL(drm_plane_force_disable); >> >> @@ -1043,21 +1181,89 @@ EXPORT_SYMBOL(drm_mode_destroy); >> >> static int drm_mode_create_standard_connector_properties(struct drm_device *dev) >> { >> - struct drm_property *edid; >> - struct drm_property *dpms; >> + 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; >> + if (!prop) >> + return -ENOMEM; >> + dev->mode_config.dpms_property = prop; >> + >> + >> + /* TODO we need the driver to control which of these are dynamic >> + * and which are not.. or maybe we should just set all to zero >> + * and let the individual drivers frob the prop->flags for the >> + * properties they can support dynamic changes on.. >> + */ >> + >> + prop = drm_property_create_range(dev, DRM_MODE_PROP_DYNAMIC, >> + "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_DYNAMIC, >> + "SRC_Y", 0, UINT_MAX); >> + if (!prop) >> + return -ENOMEM; >> + dev->mode_config.prop_src_y = prop; >> + >> + prop = drm_property_create_range(dev, 0, "SRC_W", 0, UINT_MAX); >> + if (!prop) >> + return -ENOMEM; >> + dev->mode_config.prop_src_w = prop; >> + >> + prop = drm_property_create_range(dev, 0, "SRC_H", 0, UINT_MAX); >> + if (!prop) >> + return -ENOMEM; >> + dev->mode_config.prop_src_h = prop; >> + >> + prop = drm_property_create_range(dev, >> + DRM_MODE_PROP_DYNAMIC | DRM_MODE_PROP_SIGNED, >> + "CRTC_X", I642U64(INT_MIN), I642U64(INT_MAX)); >> + if (!prop) >> + return -ENOMEM; >> + dev->mode_config.prop_crtc_x = prop; >> + >> + prop = drm_property_create_range(dev, >> + DRM_MODE_PROP_DYNAMIC | DRM_MODE_PROP_SIGNED, >> + "CRTC_Y", I642U64(INT_MIN), I642U64(INT_MAX)); >> + if (!prop) >> + return -ENOMEM; >> + dev->mode_config.prop_crtc_y = prop; >> + >> + prop = drm_property_create_range(dev, 0, "CRTC_W", 0, INT_MAX); >> + if (!prop) >> + return -ENOMEM; >> + dev->mode_config.prop_crtc_w = prop; >> + >> + prop = drm_property_create_range(dev, 0, "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_DYNAMIC, >> + "FB_ID", DRM_MODE_OBJECT_FB); >> + if (!prop) >> + return -ENOMEM; >> + dev->mode_config.prop_fb_id = prop; >> + >> + prop = drm_property_create_object(dev, 0, >> + "CRTC_ID", DRM_MODE_OBJECT_CRTC); >> + if (!prop) >> + return -ENOMEM; >> + dev->mode_config.prop_crtc_id = prop; >> >> return 0; >> } >> @@ -1842,13 +2048,13 @@ int drm_mode_getplane(struct drm_device *dev, void *data, >> } >> plane = obj_to_plane(obj); >> >> - if (plane->crtc) >> - plane_resp->crtc_id = plane->crtc->base.id; >> + if (plane->state->crtc) >> + plane_resp->crtc_id = plane->state->crtc->base.id; >> else >> plane_resp->crtc_id = 0; >> >> - if (plane->fb) >> - plane_resp->fb_id = plane->fb->base.id; >> + if (plane->state->fb) >> + plane_resp->fb_id = plane->state->fb->base.id; >> else >> plane_resp->fb_id = 0; >> >> @@ -1890,21 +2096,18 @@ int drm_mode_setplane(struct drm_device *dev, void *data, >> struct drm_file *file_priv) >> { >> struct drm_mode_set_plane *plane_req = data; >> + struct drm_mode_config *config = &dev->mode_config; >> struct drm_mode_object *obj; >> - struct drm_plane *plane; >> - struct drm_crtc *crtc; >> - struct drm_framebuffer *fb = NULL, *old_fb = NULL; >> + void *state; >> int ret = 0; >> - unsigned int fb_width, fb_height; >> - int i; >> >> if (!drm_core_check_feature(dev, DRIVER_MODESET)) >> return -EINVAL; >> >> - /* >> - * First, find the plane, crtc, and fb objects. If not available, >> - * we don't bother to call the driver. >> - */ >> + state = dev->driver->atomic_begin(dev, 0); >> + if (IS_ERR(state)) >> + return PTR_ERR(state); >> + >> obj = drm_mode_object_find(dev, plane_req->plane_id, >> DRM_MODE_OBJECT_PLANE); >> if (!obj) { >> @@ -1912,102 +2115,36 @@ int drm_mode_setplane(struct drm_device *dev, void *data, >> 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; >> - drm_modeset_unlock_all(dev); >> - goto out; >> - } >> >> - obj = drm_mode_object_find(dev, plane_req->crtc_id, >> - DRM_MODE_OBJECT_CRTC); >> - if (!obj) { >> - DRM_DEBUG_KMS("Unknown crtc ID %d\n", >> - plane_req->crtc_id); >> - ret = -ENOENT; >> - goto out; >> - } >> - crtc = obj_to_crtc(obj); >> - >> - fb = drm_framebuffer_lookup(dev, plane_req->fb_id); >> - if (!fb) { >> - DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", >> - plane_req->fb_id); >> - ret = -ENOENT; >> - goto out; >> - } >> - >> - /* Check whether this plane supports the fb pixel format. */ >> - for (i = 0; i < plane->format_count; i++) >> - if (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(fb->pixel_format)); >> - ret = -EINVAL; >> - goto out; >> - } >> - >> - fb_width = fb->width << 16; >> - fb_height = fb->height << 16; >> - >> - /* Make sure source coordinates are inside the fb. */ >> - if (plane_req->src_w > fb_width || >> - plane_req->src_x > fb_width - plane_req->src_w || >> - plane_req->src_h > fb_height || >> - plane_req->src_y > fb_height - plane_req->src_h) { >> - DRM_DEBUG_KMS("Invalid source coordinates " >> - "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", >> - plane_req->src_w >> 16, >> - ((plane_req->src_w & 0xffff) * 15625) >> 10, >> - plane_req->src_h >> 16, >> - ((plane_req->src_h & 0xffff) * 15625) >> 10, >> - plane_req->src_x >> 16, >> - ((plane_req->src_x & 0xffff) * 15625) >> 10, >> - plane_req->src_y >> 16, >> - ((plane_req->src_y & 0xffff) * 15625) >> 10); >> - ret = -ENOSPC; >> - goto out; >> - } >> - >> - /* Give drivers some help against integer overflows */ >> - if (plane_req->crtc_w > INT_MAX || >> - plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w || >> - plane_req->crtc_h > INT_MAX || >> - plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) { >> - DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", >> - plane_req->crtc_w, plane_req->crtc_h, >> - plane_req->crtc_x, plane_req->crtc_y); >> - ret = -ERANGE; >> + ret = >> + drm_mode_set_obj_prop(obj, state, >> + config->prop_crtc_id, plane_req->crtc_id, NULL) || >> + drm_mode_set_obj_prop(obj, state, >> + config->prop_fb_id, plane_req->fb_id, NULL) || >> + drm_mode_set_obj_prop(obj, state, >> + config->prop_crtc_x, I642U64(plane_req->crtc_x), NULL) || >> + drm_mode_set_obj_prop(obj, state, >> + config->prop_crtc_y, I642U64(plane_req->crtc_y), NULL) || >> + drm_mode_set_obj_prop(obj, state, >> + config->prop_crtc_w, plane_req->crtc_w, NULL) || >> + drm_mode_set_obj_prop(obj, state, >> + config->prop_crtc_h, plane_req->crtc_h, NULL) || >> + drm_mode_set_obj_prop(obj, state, >> + config->prop_src_w, plane_req->src_w, NULL) || >> + drm_mode_set_obj_prop(obj, state, >> + config->prop_src_h, plane_req->src_h, NULL) || >> + drm_mode_set_obj_prop(obj, state, >> + config->prop_src_x, plane_req->src_x, NULL) || >> + drm_mode_set_obj_prop(obj, state, >> + config->prop_src_y, plane_req->src_y, NULL) || >> + dev->driver->atomic_check(dev, state); >> + if (ret) >> goto out; >> - } >> >> - drm_modeset_lock_all(dev); >> - 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; >> - } >> - drm_modeset_unlock_all(dev); >> + ret = dev->driver->atomic_commit(dev, state); >> >> out: >> - if (fb) >> - drm_framebuffer_unreference(fb); >> - if (old_fb) >> - drm_framebuffer_unreference(old_fb); >> - >> + dev->driver->atomic_end(dev, state); >> return ret; >> } >> >> @@ -3296,7 +3433,7 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev, >> return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv); >> } >> >> -static int drm_mode_connector_set_obj_prop(struct drm_connector *connector, >> +int drm_mode_connector_set_obj_prop(struct drm_connector *connector, >> void *state, struct drm_property *property, >> uint64_t value, void *blob_data) >> { >> @@ -3319,8 +3456,9 @@ static int drm_mode_connector_set_obj_prop(struct drm_connector *connector, >> >> return ret; >> } >> +EXPORT_SYMBOL(drm_mode_connector_set_obj_prop); >> >> -static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, >> +int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, >> void *state, struct drm_property *property, >> uint64_t value, void *blob_data) >> { >> @@ -3335,8 +3473,9 @@ static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, >> >> return ret; >> } >> +EXPORT_SYMBOL(drm_mode_crtc_set_obj_prop); >> >> -static int drm_mode_plane_set_obj_prop(struct drm_plane *plane, >> +int drm_mode_plane_set_obj_prop(struct drm_plane *plane, >> void *state, struct drm_property *property, >> uint64_t value, void *blob_data) >> { >> @@ -3345,12 +3484,10 @@ static int drm_mode_plane_set_obj_prop(struct drm_plane *plane, >> if (plane->funcs->set_property) >> ret = plane->funcs->set_property(plane, state, property, >> value, blob_data); >> - if (!ret) >> - drm_object_property_set_value(&plane->base, &plane->propvals, >> - property, value, NULL); >> >> return ret; >> } >> +EXPORT_SYMBOL(drm_mode_plane_set_obj_prop); >> >> static int drm_mode_set_obj_prop(struct drm_mode_object *obj, >> void *state, struct drm_property *property, >> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c >> index 24898dc..f1fc605 100644 >> --- a/drivers/gpu/drm/drm_fb_helper.c >> +++ b/drivers/gpu/drm/drm_fb_helper.c >> @@ -290,12 +290,27 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) >> struct drm_device *dev = fb_helper->dev; >> struct drm_plane *plane; >> bool error = false; >> + void *state; >> int i; >> >> drm_warn_on_modeset_not_all_locked(dev); >> >> + state = dev->driver->atomic_begin(dev, 0); >> + if (IS_ERR(state)) { >> + DRM_ERROR("failed to restore fbdev mode\n"); >> + return true; >> + } >> + >> list_for_each_entry(plane, &dev->mode_config.plane_list, head) >> - drm_plane_force_disable(plane); >> + drm_plane_force_disable(plane, state); >> + >> + /* just disabling stuff shouldn't fail, hopefully: */ >> + if(dev->driver->atomic_check(dev, state)) >> + DRM_ERROR("failed to restore fbdev mode\n"); >> + else >> + dev->driver->atomic_commit(dev, state); > > I'm seeing some deadlocks here on i915 when we trigger the lastclose > handler. We're already holding mode_config.mutex when we enter this > function, but drm_atomic_helper_commit_plane_state does another > drm_modeset_lock_all() call. oh, sorry, I should have mentioned that for now I got rid of the locks around the call to drm_fb_helper_restore_fbdev_mode().. that hack is intended to be cleaned up somehow by (I think) pushing the lock down into _restore_fbdev_mode(), plus ww_mutex conversion, which will be part of next next round of RFC (as soon as I have time to implement it). BR, -R
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 46c67b8..0618113 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -39,10 +39,12 @@ void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags) { struct drm_atomic_helper_state *state; + int nplanes = dev->mode_config.num_plane; int sz; void *ptr; sz = sizeof(*state); + sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes; ptr = kzalloc(sz, GFP_KERNEL); @@ -52,6 +54,13 @@ void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags) kref_init(&state->refcount); state->dev = dev; state->flags = flags; + + state->planes = ptr; + ptr = &state->planes[nplanes]; + + state->pstates = ptr; + ptr = &state->pstates[nplanes]; + return state; } EXPORT_SYMBOL(drm_atomic_helper_begin); @@ -87,7 +96,19 @@ EXPORT_SYMBOL(drm_atomic_helper_set_event); */ int drm_atomic_helper_check(struct drm_device *dev, void *state) { - return 0; /* for now */ + struct drm_atomic_helper_state *a = state; + int nplanes = dev->mode_config.num_plane; + int i, ret = 0; + + for (i = 0; i < nplanes; i++) { + if (a->planes[i]) { + ret = drm_atomic_check_plane_state(a->planes[i], a->pstates[i]); + if (ret) + break; + } + } + + return ret; } EXPORT_SYMBOL(drm_atomic_helper_check); @@ -104,7 +125,19 @@ EXPORT_SYMBOL(drm_atomic_helper_check); */ int drm_atomic_helper_commit(struct drm_device *dev, void *state) { - return 0; /* for now */ + struct drm_atomic_helper_state *a = state; + int nplanes = dev->mode_config.num_plane; + int i, ret = 0; + + for (i = 0; i < nplanes; i++) { + if (a->planes[i]) { + ret = drm_atomic_commit_plane_state(a->planes[i], a->pstates[i]); + if (ret) + break; + } + } + + return ret; } EXPORT_SYMBOL(drm_atomic_helper_commit); @@ -125,11 +158,111 @@ void _drm_atomic_helper_state_free(struct kref *kref) { struct drm_atomic_helper_state *state = container_of(kref, struct drm_atomic_helper_state, refcount); + struct drm_device *dev = state->dev; + int nplanes = dev->mode_config.num_plane; + int i; + + for (i = 0; i < nplanes; i++) { + if (state->pstates[i]) { + state->planes[i]->state->state = NULL; + kfree(state->pstates[i]); + } + } + kfree(state); } EXPORT_SYMBOL(_drm_atomic_helper_state_free); +int drm_atomic_helper_plane_set_property(struct drm_plane *plane, void *state, + struct drm_property *property, uint64_t val, void *blob_data) +{ + return drm_plane_set_property(plane, + drm_atomic_get_plane_state(plane, state), + property, val, blob_data); +} +EXPORT_SYMBOL(drm_atomic_helper_plane_set_property); + +void drm_atomic_helper_init_plane_state(struct drm_plane *plane, + struct drm_plane_state *pstate, void *state) +{ + /* snapshot current state: */ + *pstate = *plane->state; + pstate->state = state; +} +EXPORT_SYMBOL(drm_atomic_helper_init_plane_state); + +static struct drm_plane_state * +drm_atomic_helper_get_plane_state(struct drm_plane *plane, void *state) +{ + struct drm_atomic_helper_state *a = state; + struct drm_plane_state *pstate = a->pstates[plane->id]; + if (!pstate) { + pstate = kzalloc(sizeof(*pstate), GFP_KERNEL); + drm_atomic_helper_init_plane_state(plane, pstate, state); + a->planes[plane->id] = plane; + a->pstates[plane->id] = pstate; + } + return pstate; +} + +static void +swap_plane_state(struct drm_plane *plane, struct drm_atomic_helper_state *a) +{ + swap(plane->state, a->pstates[plane->id]); + plane->base.propvals = &plane->state->propvals; +} + +static int +drm_atomic_helper_commit_plane_state(struct drm_plane *plane, + struct drm_plane_state *pstate) +{ + struct drm_device *dev = plane->dev; + struct drm_framebuffer *old_fb = NULL, *fb = NULL; + int ret = 0; + + /* probably more fine grain locking would be ok of old crtc + * and new crtc were same.. + */ + drm_modeset_lock_all(dev); + + fb = pstate->fb; + + if (pstate->crtc && fb) { + ret = plane->funcs->update_plane(plane, pstate->crtc, pstate->fb, + pstate->crtc_x, pstate->crtc_y, pstate->crtc_w, pstate->crtc_h, + pstate->src_x, pstate->src_y, pstate->src_w, pstate->src_h); + if (!ret) { + /* on success, update state and fb refcnting: */ + /* NOTE: if we ensure no driver sets plane->state->fb = NULL + * on disable, we can move this up a level and not duplicate + * nearly the same thing for both update_plane and disable_plane + * cases.. I leave it like this for now to be paranoid due to + * the slightly different ordering in the two cases in the + * original code. + */ + old_fb = plane->state->fb; + swap_plane_state(plane, pstate->state); + fb = NULL; + } + } else { + old_fb = plane->state->fb; + plane->funcs->disable_plane(plane); + swap_plane_state(plane, pstate->state); + } + + drm_modeset_unlock_all(dev); + + if (fb) + drm_framebuffer_unreference(fb); + if (old_fb) + drm_framebuffer_unreference(old_fb); + + return ret; +} const struct drm_atomic_helper_funcs drm_atomic_helper_funcs = { + .get_plane_state = drm_atomic_helper_get_plane_state, + .check_plane_state = drm_plane_check_state, + .commit_plane_state = drm_atomic_helper_commit_plane_state, }; EXPORT_SYMBOL(drm_atomic_helper_funcs); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 9f46f3b..3cf235e 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -595,7 +595,20 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) * in this manner. */ if (atomic_read(&fb->refcount.refcount) > 1) { + void *state; + + state = dev->driver->atomic_begin(dev, 0); + if (IS_ERR(state)) { + DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n"); + return; + } + + /* TODO once CRTC is converted to state/properties, we can push the + * locking down into drm_atomic_helper_commit(), since that is where + * the actual changes take place.. + */ drm_modeset_lock_all(dev); + /* remove from any CRTC */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { if (crtc->fb == fb) { @@ -610,9 +623,18 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) } list_for_each_entry(plane, &dev->mode_config.plane_list, head) { - if (plane->fb == fb) - drm_plane_force_disable(plane); + if (plane->state->fb == fb) + drm_plane_force_disable(plane, state); } + + /* just disabling stuff shouldn't fail, hopefully: */ + if(dev->driver->atomic_check(dev, state)) + DRM_ERROR("failed to disable crtc and/or plane when fb was deleted\n"); + else + dev->driver->atomic_commit(dev, state); + + dev->driver->atomic_end(dev, state); + drm_modeset_unlock_all(dev); } @@ -904,8 +926,12 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, const uint32_t *formats, uint32_t format_count, bool priv) { + struct drm_mode_config *config = &dev->mode_config; int ret; + if (!plane->state) + plane->state = kzalloc(sizeof(plane->state), GFP_KERNEL); + drm_modeset_lock_all(dev); ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); @@ -913,7 +939,7 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, goto out; plane->base.properties = &plane->properties; - plane->base.propvals = &plane->propvals; + plane->base.propvals = &plane->state->propvals; plane->dev = dev; plane->funcs = funcs; plane->format_types = kmalloc(sizeof(uint32_t) * format_count, @@ -935,11 +961,23 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, */ if (!priv) { list_add_tail(&plane->head, &dev->mode_config.plane_list); + plane->id = dev->mode_config.num_plane; dev->mode_config.num_plane++; } else { INIT_LIST_HEAD(&plane->head); } + 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); + out: drm_modeset_unlock_all(dev); @@ -971,6 +1009,111 @@ void drm_plane_cleanup(struct drm_plane *plane) } EXPORT_SYMBOL(drm_plane_cleanup); +int drm_plane_check_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + unsigned int fb_width, fb_height; + struct drm_framebuffer *fb = state->fb; + int i; + + /* disabling the plane is allowed: */ + if (!fb) + return 0; + + fb_width = fb->width << 16; + fb_height = fb->height << 16; + + /* Check whether this plane supports the fb pixel format. */ + for (i = 0; i < plane->format_count; i++) + if (fb->pixel_format == plane->format_types[i]) + break; + if (i == plane->format_count) { + DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format); + return -EINVAL; + } + + /* 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; + } + + /* 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; + } + + return 0; +} +EXPORT_SYMBOL(drm_plane_check_state); + +void drm_plane_commit_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + plane->state = state; + plane->base.propvals = &state->propvals; +} +EXPORT_SYMBOL(drm_plane_commit_state); + +int drm_plane_set_property(struct drm_plane *plane, + struct drm_plane_state *state, + struct drm_property *property, + uint64_t value, void *blob_data) +{ + struct drm_device *dev = plane->dev; + struct drm_mode_config *config = &dev->mode_config; + + drm_object_property_set_value(&plane->base, + &state->propvals, property, value, blob_data); + + if (property == config->prop_fb_id) { + state->new_fb = true; + state->fb = drm_framebuffer_lookup(dev, value); + } else if (property == config->prop_crtc_id) { + struct drm_mode_object *obj = drm_property_get_obj(property, value); + state->crtc = obj ? obj_to_crtc(obj) : NULL; + } else if (property == config->prop_crtc_x) { + state->crtc_x = U642I64(value); + } else if (property == config->prop_crtc_y) { + state->crtc_y = U642I64(value); + } else if (property == config->prop_crtc_w) { + state->crtc_w = value; + } else if (property == config->prop_crtc_h) { + state->crtc_h = value; + } else if (property == config->prop_src_x) { + state->src_x = value; + } else if (property == config->prop_src_y) { + state->src_y = value; + } else if (property == config->prop_src_w) { + state->src_w = value; + } else if (property == config->prop_src_h) { + state->src_h = value; + } else { + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(drm_plane_set_property); + /** * drm_plane_force_disable - Forcibly disable a plane * @plane: plane to disable @@ -980,20 +1123,15 @@ EXPORT_SYMBOL(drm_plane_cleanup); * Used when the plane's current framebuffer is destroyed, * and when restoring fbdev mode. */ -void drm_plane_force_disable(struct drm_plane *plane) +void drm_plane_force_disable(struct drm_plane *plane, void *state) { - int ret; + struct drm_mode_config *config = &plane->dev->mode_config; - if (!plane->fb) - return; - - ret = plane->funcs->disable_plane(plane); - if (ret) - DRM_ERROR("failed to disable plane with busy fb\n"); - /* disconnect the plane from the fb and crtc: */ - __drm_framebuffer_unreference(plane->fb); - plane->fb = NULL; - plane->crtc = NULL; + /* should turn off the crtc */ + drm_mode_plane_set_obj_prop(plane, state, + config->prop_crtc_id, 0, NULL); + drm_mode_plane_set_obj_prop(plane, state, + config->prop_fb_id, 0, NULL); } EXPORT_SYMBOL(drm_plane_force_disable); @@ -1043,21 +1181,89 @@ EXPORT_SYMBOL(drm_mode_destroy); static int drm_mode_create_standard_connector_properties(struct drm_device *dev) { - struct drm_property *edid; - struct drm_property *dpms; + 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; + if (!prop) + return -ENOMEM; + dev->mode_config.dpms_property = prop; + + + /* TODO we need the driver to control which of these are dynamic + * and which are not.. or maybe we should just set all to zero + * and let the individual drivers frob the prop->flags for the + * properties they can support dynamic changes on.. + */ + + prop = drm_property_create_range(dev, DRM_MODE_PROP_DYNAMIC, + "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_DYNAMIC, + "SRC_Y", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_y = prop; + + prop = drm_property_create_range(dev, 0, "SRC_W", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_w = prop; + + prop = drm_property_create_range(dev, 0, "SRC_H", 0, UINT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_src_h = prop; + + prop = drm_property_create_range(dev, + DRM_MODE_PROP_DYNAMIC | DRM_MODE_PROP_SIGNED, + "CRTC_X", I642U64(INT_MIN), I642U64(INT_MAX)); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_x = prop; + + prop = drm_property_create_range(dev, + DRM_MODE_PROP_DYNAMIC | DRM_MODE_PROP_SIGNED, + "CRTC_Y", I642U64(INT_MIN), I642U64(INT_MAX)); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_y = prop; + + prop = drm_property_create_range(dev, 0, "CRTC_W", 0, INT_MAX); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_w = prop; + + prop = drm_property_create_range(dev, 0, "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_DYNAMIC, + "FB_ID", DRM_MODE_OBJECT_FB); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_fb_id = prop; + + prop = drm_property_create_object(dev, 0, + "CRTC_ID", DRM_MODE_OBJECT_CRTC); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_crtc_id = prop; return 0; } @@ -1842,13 +2048,13 @@ int drm_mode_getplane(struct drm_device *dev, void *data, } plane = obj_to_plane(obj); - if (plane->crtc) - plane_resp->crtc_id = plane->crtc->base.id; + if (plane->state->crtc) + plane_resp->crtc_id = plane->state->crtc->base.id; else plane_resp->crtc_id = 0; - if (plane->fb) - plane_resp->fb_id = plane->fb->base.id; + if (plane->state->fb) + plane_resp->fb_id = plane->state->fb->base.id; else plane_resp->fb_id = 0; @@ -1890,21 +2096,18 @@ int drm_mode_setplane(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_set_plane *plane_req = data; + struct drm_mode_config *config = &dev->mode_config; struct drm_mode_object *obj; - struct drm_plane *plane; - struct drm_crtc *crtc; - struct drm_framebuffer *fb = NULL, *old_fb = NULL; + void *state; int ret = 0; - unsigned int fb_width, fb_height; - int i; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - /* - * First, find the plane, crtc, and fb objects. If not available, - * we don't bother to call the driver. - */ + state = dev->driver->atomic_begin(dev, 0); + if (IS_ERR(state)) + return PTR_ERR(state); + obj = drm_mode_object_find(dev, plane_req->plane_id, DRM_MODE_OBJECT_PLANE); if (!obj) { @@ -1912,102 +2115,36 @@ int drm_mode_setplane(struct drm_device *dev, void *data, 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; - drm_modeset_unlock_all(dev); - goto out; - } - obj = drm_mode_object_find(dev, plane_req->crtc_id, - DRM_MODE_OBJECT_CRTC); - if (!obj) { - DRM_DEBUG_KMS("Unknown crtc ID %d\n", - plane_req->crtc_id); - ret = -ENOENT; - goto out; - } - crtc = obj_to_crtc(obj); - - fb = drm_framebuffer_lookup(dev, plane_req->fb_id); - if (!fb) { - DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", - plane_req->fb_id); - ret = -ENOENT; - goto out; - } - - /* Check whether this plane supports the fb pixel format. */ - for (i = 0; i < plane->format_count; i++) - if (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(fb->pixel_format)); - ret = -EINVAL; - goto out; - } - - fb_width = fb->width << 16; - fb_height = fb->height << 16; - - /* Make sure source coordinates are inside the fb. */ - if (plane_req->src_w > fb_width || - plane_req->src_x > fb_width - plane_req->src_w || - plane_req->src_h > fb_height || - plane_req->src_y > fb_height - plane_req->src_h) { - DRM_DEBUG_KMS("Invalid source coordinates " - "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", - plane_req->src_w >> 16, - ((plane_req->src_w & 0xffff) * 15625) >> 10, - plane_req->src_h >> 16, - ((plane_req->src_h & 0xffff) * 15625) >> 10, - plane_req->src_x >> 16, - ((plane_req->src_x & 0xffff) * 15625) >> 10, - plane_req->src_y >> 16, - ((plane_req->src_y & 0xffff) * 15625) >> 10); - ret = -ENOSPC; - goto out; - } - - /* Give drivers some help against integer overflows */ - if (plane_req->crtc_w > INT_MAX || - plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w || - plane_req->crtc_h > INT_MAX || - plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) { - DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", - plane_req->crtc_w, plane_req->crtc_h, - plane_req->crtc_x, plane_req->crtc_y); - ret = -ERANGE; + ret = + drm_mode_set_obj_prop(obj, state, + config->prop_crtc_id, plane_req->crtc_id, NULL) || + drm_mode_set_obj_prop(obj, state, + config->prop_fb_id, plane_req->fb_id, NULL) || + drm_mode_set_obj_prop(obj, state, + config->prop_crtc_x, I642U64(plane_req->crtc_x), NULL) || + drm_mode_set_obj_prop(obj, state, + config->prop_crtc_y, I642U64(plane_req->crtc_y), NULL) || + drm_mode_set_obj_prop(obj, state, + config->prop_crtc_w, plane_req->crtc_w, NULL) || + drm_mode_set_obj_prop(obj, state, + config->prop_crtc_h, plane_req->crtc_h, NULL) || + drm_mode_set_obj_prop(obj, state, + config->prop_src_w, plane_req->src_w, NULL) || + drm_mode_set_obj_prop(obj, state, + config->prop_src_h, plane_req->src_h, NULL) || + drm_mode_set_obj_prop(obj, state, + config->prop_src_x, plane_req->src_x, NULL) || + drm_mode_set_obj_prop(obj, state, + config->prop_src_y, plane_req->src_y, NULL) || + dev->driver->atomic_check(dev, state); + if (ret) goto out; - } - drm_modeset_lock_all(dev); - 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; - } - drm_modeset_unlock_all(dev); + ret = dev->driver->atomic_commit(dev, state); out: - if (fb) - drm_framebuffer_unreference(fb); - if (old_fb) - drm_framebuffer_unreference(old_fb); - + dev->driver->atomic_end(dev, state); return ret; } @@ -3296,7 +3433,7 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev, return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv); } -static int drm_mode_connector_set_obj_prop(struct drm_connector *connector, +int drm_mode_connector_set_obj_prop(struct drm_connector *connector, void *state, struct drm_property *property, uint64_t value, void *blob_data) { @@ -3319,8 +3456,9 @@ static int drm_mode_connector_set_obj_prop(struct drm_connector *connector, return ret; } +EXPORT_SYMBOL(drm_mode_connector_set_obj_prop); -static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, +int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, void *state, struct drm_property *property, uint64_t value, void *blob_data) { @@ -3335,8 +3473,9 @@ static int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, return ret; } +EXPORT_SYMBOL(drm_mode_crtc_set_obj_prop); -static int drm_mode_plane_set_obj_prop(struct drm_plane *plane, +int drm_mode_plane_set_obj_prop(struct drm_plane *plane, void *state, struct drm_property *property, uint64_t value, void *blob_data) { @@ -3345,12 +3484,10 @@ static int drm_mode_plane_set_obj_prop(struct drm_plane *plane, if (plane->funcs->set_property) ret = plane->funcs->set_property(plane, state, property, value, blob_data); - if (!ret) - drm_object_property_set_value(&plane->base, &plane->propvals, - property, value, NULL); return ret; } +EXPORT_SYMBOL(drm_mode_plane_set_obj_prop); static int drm_mode_set_obj_prop(struct drm_mode_object *obj, void *state, struct drm_property *property, diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 24898dc..f1fc605 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -290,12 +290,27 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) struct drm_device *dev = fb_helper->dev; struct drm_plane *plane; bool error = false; + void *state; int i; drm_warn_on_modeset_not_all_locked(dev); + state = dev->driver->atomic_begin(dev, 0); + if (IS_ERR(state)) { + DRM_ERROR("failed to restore fbdev mode\n"); + return true; + } + list_for_each_entry(plane, &dev->mode_config.plane_list, head) - drm_plane_force_disable(plane); + drm_plane_force_disable(plane, state); + + /* just disabling stuff shouldn't fail, hopefully: */ + if(dev->driver->atomic_check(dev, state)) + DRM_ERROR("failed to restore fbdev mode\n"); + else + dev->driver->atomic_commit(dev, state); + + dev->driver->atomic_end(dev, state); for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c index 82a9fca..4ae55b8 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -124,8 +124,8 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, if (ret) return ret; - plane->crtc = crtc; - plane->fb = crtc->fb; + plane->state->crtc = crtc; + plane->state->fb = crtc->fb; exynos_drm_fn_encoder(crtc, &pipe, exynos_drm_encoder_crtc_pipe); diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c index 06f1b2a..dbe2e19 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c +++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c @@ -127,7 +127,7 @@ static void disable_plane_to_crtc(struct drm_device *dev, * (encoder->crtc) */ list_for_each_entry(plane, &dev->mode_config.plane_list, head) { - if (plane->crtc == old_crtc) { + if (plane->state->crtc == old_crtc) { /* * do not change below call order. * @@ -138,7 +138,7 @@ static void disable_plane_to_crtc(struct drm_device *dev, * have new_crtc because new_crtc was set to * encoder->crtc in advance. */ - plane->crtc = new_crtc; + plane->state->crtc = new_crtc; plane->funcs->disable_plane(plane); } } @@ -247,7 +247,7 @@ static void exynos_drm_encoder_disable(struct drm_encoder *encoder) /* all planes connected to this encoder should be also disabled. */ list_for_each_entry(plane, &dev->mode_config.plane_list, head) { - if (plane->crtc == encoder->crtc) + if (plane->state->crtc == encoder->crtc) plane->funcs->disable_plane(plane); } } diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c index 2e31fb8..45d0fa3 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_plane.c +++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c @@ -10,6 +10,7 @@ */ #include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> #include <drm/exynos_drm.h> #include "exynos_drm_drv.h" @@ -149,7 +150,7 @@ void exynos_plane_commit(struct drm_plane *plane) struct exynos_plane *exynos_plane = to_exynos_plane(plane); struct exynos_drm_overlay *overlay = &exynos_plane->overlay; - exynos_drm_fn_encoder(plane->crtc, &overlay->zpos, + exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos, exynos_drm_encoder_plane_commit); } @@ -162,7 +163,7 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode) if (exynos_plane->enabled) return; - exynos_drm_fn_encoder(plane->crtc, &overlay->zpos, + exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos, exynos_drm_encoder_plane_enable); exynos_plane->enabled = true; @@ -170,7 +171,7 @@ void exynos_plane_dpms(struct drm_plane *plane, int mode) if (!exynos_plane->enabled) return; - exynos_drm_fn_encoder(plane->crtc, &overlay->zpos, + exynos_drm_fn_encoder(plane->state->crtc, &overlay->zpos, exynos_drm_encoder_plane_disable); exynos_plane->enabled = false; @@ -192,7 +193,7 @@ exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, if (ret < 0) return ret; - plane->crtc = crtc; + plane->state->crtc = crtc; exynos_plane_commit(plane); exynos_plane_dpms(plane, DRM_MODE_DPMS_ON); @@ -229,6 +230,10 @@ static int exynos_plane_set_property(struct drm_plane *plane, if (property == dev_priv->plane_zpos_property) { exynos_plane->overlay.zpos = val; return 0; + } else { + return drm_plane_set_property(plane, + drm_atomic_get_plane_state(plane, state), + property, val, blob_data); } return -EINVAL; diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index ad6ec4b..d8c2869 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -868,16 +868,17 @@ intel_disable_plane(struct drm_plane *plane) { struct drm_device *dev = plane->dev; struct intel_plane *intel_plane = to_intel_plane(plane); + struct drm_plane_state *state = plane->state; int ret = 0; - if (!plane->fb) + if (!state->fb) return 0; - if (WARN_ON(!plane->crtc)) + if (WARN_ON(!state->crtc)) return -EINVAL; - intel_enable_primary(plane->crtc); - intel_plane->disable_plane(plane, plane->crtc); + intel_enable_primary(state->crtc); + intel_plane->disable_plane(plane, state->crtc); if (!intel_plane->obj) goto out; @@ -966,11 +967,12 @@ out_unlock: void intel_plane_restore(struct drm_plane *plane) { struct intel_plane *intel_plane = to_intel_plane(plane); + struct drm_plane_state *state = plane->state; - if (!plane->crtc || !plane->fb) + if (!state->crtc || !state->fb) return; - intel_update_plane(plane, plane->crtc, plane->fb, + intel_update_plane(plane, state->crtc, state->fb, intel_plane->crtc_x, intel_plane->crtc_y, intel_plane->crtc_w, intel_plane->crtc_h, intel_plane->src_x, intel_plane->src_y, @@ -979,7 +981,9 @@ void intel_plane_restore(struct drm_plane *plane) void intel_plane_disable(struct drm_plane *plane) { - if (!plane->crtc || !plane->fb) + struct drm_plane_state *state = plane->state; + + if (!state->crtc || !state->fb) return; intel_disable_plane(plane); @@ -989,6 +993,7 @@ static const struct drm_plane_funcs intel_plane_funcs = { .update_plane = intel_update_plane, .disable_plane = intel_disable_plane, .destroy = intel_destroy_plane, + .set_property = drm_atomic_helper_plane_set_property, }; static uint32_t ilk_plane_formats[] = { diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c index d3fd7ca..1d6a6ee 100644 --- a/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c +++ b/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c @@ -243,7 +243,7 @@ static void blend_setup(struct drm_crtc *crtc) int idx = idxs[pipe_id]; if (idx > 0) { const struct mdp4_format *format = - to_mdp4_format(msm_framebuffer_format(plane->fb)); + to_mdp4_format(msm_framebuffer_format(plane->state->fb)); alpha[idx-1] = format->alpha_enable; } mixer_cfg |= mixercfg(mdp4_crtc->mixer, pipe_id, stages[idx]); diff --git a/drivers/gpu/drm/msm/mdp4/mdp4_plane.c b/drivers/gpu/drm/msm/mdp4/mdp4_plane.c index 880e96d..8338b1a 100644 --- a/drivers/gpu/drm/msm/mdp4/mdp4_plane.c +++ b/drivers/gpu/drm/msm/mdp4/mdp4_plane.c @@ -45,11 +45,14 @@ static int mdp4_plane_update(struct drm_plane *plane, uint32_t src_w, uint32_t src_h) { struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); + struct drm_plane_state *state = plane->state; mdp4_plane->enabled = true; - if (plane->fb) - drm_framebuffer_unreference(plane->fb); + if (state->fb) { + drm_framebuffer_unreference(state->fb); + state->fb = NULL; + } drm_framebuffer_reference(fb); @@ -62,8 +65,8 @@ static int mdp4_plane_disable(struct drm_plane *plane) { struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane); DBG("%s: disable", mdp4_plane->name); - if (plane->crtc) - mdp4_crtc_detach(plane->crtc, plane); + if (plane->state->crtc) + mdp4_crtc_detach(plane->state->crtc, plane); return 0; } @@ -87,8 +90,9 @@ void mdp4_plane_install_properties(struct drm_plane *plane, int mdp4_plane_set_property(struct drm_plane *plane, void *state, struct drm_property *property, uint64_t val, void *blob_data) { - // XXX - return -EINVAL; + return drm_plane_set_property(plane, + drm_atomic_get_plane_state(plane, state), + property, val, blob_data); } static const struct drm_plane_funcs mdp4_plane_funcs = { @@ -117,7 +121,7 @@ void mdp4_plane_set_scanout(struct drm_plane *plane, msm_gem_get_iova(msm_framebuffer_bo(fb, 0), mdp4_kms->id, &iova); mdp4_write(mdp4_kms, REG_MDP4_PIPE_SRCP0_BASE(pipe), iova); - plane->fb = fb; + plane->state->fb = fb; } #define MDP4_VG_PHASE_STEP_DEFAULT 0x20000000 diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index 5dd22ab..d53a689 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -210,7 +210,7 @@ static void omap_crtc_dpms(struct drm_crtc *crtc, int mode) /* and any attached overlay planes: */ for (i = 0; i < priv->num_planes; i++) { struct drm_plane *plane = priv->planes[i]; - if (plane->crtc == crtc) + if (plane->state->crtc == crtc) WARN_ON(omap_plane_dpms(plane, mode)); } } @@ -651,7 +651,7 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, omap_crtc->channel = channel; omap_crtc->plane = plane; - omap_crtc->plane->crtc = crtc; + omap_crtc->plane->state->crtc = crtc; omap_crtc->name = channel_names[channel]; omap_crtc->pipe = id; diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 225d0e9..0e15e2c 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -559,7 +559,7 @@ static void dev_lastclose(struct drm_device *dev) for (i = 0; i < priv->num_planes; i++) { drm_object_property_set_value(&priv->planes[i]->base, - &priv->planes[i]->propvals, + &priv->planes[i]->state->propvals, priv->rotation_prop, 0, NULL); } } diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index fe32f1b..d11a879 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -116,9 +116,10 @@ static void omap_plane_pre_apply(struct omap_drm_apply *apply) container_of(apply, struct omap_plane, apply); struct omap_drm_window *win = &omap_plane->win; struct drm_plane *plane = &omap_plane->base; + struct drm_plane_state *state = plane->state; struct drm_device *dev = plane->dev; struct omap_overlay_info *info = &omap_plane->info; - struct drm_crtc *crtc = plane->crtc; + struct drm_crtc *crtc = state->crtc; enum omap_channel channel; bool enabled = omap_plane->enabled && crtc; bool ilace, replication; @@ -127,7 +128,7 @@ static void omap_plane_pre_apply(struct omap_drm_apply *apply) DBG("%s, enabled=%d", omap_plane->name, enabled); /* if fb has changed, pin new fb: */ - update_pin(plane, enabled ? plane->fb : NULL); + update_pin(plane, enabled ? state->fb : NULL); if (!enabled) { dispc_ovl_enable(omap_plane->id, false); @@ -137,7 +138,7 @@ static void omap_plane_pre_apply(struct omap_drm_apply *apply) channel = omap_crtc_channel(crtc); /* update scanout: */ - omap_framebuffer_update_scanout(plane->fb, win, info); + omap_framebuffer_update_scanout(state->fb, win, info); DBG("%dx%d -> %dx%d (%d)", info->width, info->height, info->out_width, info->out_height, @@ -179,16 +180,18 @@ static void omap_plane_post_apply(struct omap_drm_apply *apply) cb.fxn(cb.arg); if (omap_plane->enabled) { - omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y, + omap_framebuffer_flush(plane->state->fb, + info->pos_x, info->pos_y, info->out_width, info->out_height); } } static int apply(struct drm_plane *plane) { - if (plane->crtc) { + struct drm_plane_state *state = plane->state; + if (state->crtc) { struct omap_plane *omap_plane = to_omap_plane(plane); - return omap_crtc_apply(plane->crtc, &omap_plane->apply); + return omap_crtc_apply(state->crtc, &omap_plane->apply); } return 0; } @@ -203,6 +206,7 @@ int omap_plane_mode_set(struct drm_plane *plane, { struct omap_plane *omap_plane = to_omap_plane(plane); struct omap_drm_window *win = &omap_plane->win; + struct drm_plane_state *state = plane->state; win->crtc_x = crtc_x; win->crtc_y = crtc_y; @@ -225,8 +229,8 @@ int omap_plane_mode_set(struct drm_plane *plane, omap_plane->apply_done_cb.arg = arg; } - plane->fb = fb; - plane->crtc = crtc; + state->fb = fb; + state->crtc = crtc; return apply(plane); } @@ -239,10 +243,12 @@ static int omap_plane_update(struct drm_plane *plane, uint32_t src_w, uint32_t src_h) { struct omap_plane *omap_plane = to_omap_plane(plane); + struct drm_plane_state *state = plane->state; + omap_plane->enabled = true; - if (plane->fb) - drm_framebuffer_unreference(plane->fb); + if (state->fb) + drm_framebuffer_unreference(state->fb); drm_framebuffer_reference(fb); @@ -342,6 +348,10 @@ int omap_plane_set_property(struct drm_plane *plane, void *state, DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val); omap_plane->info.zorder = val; ret = apply(plane); + } else { + ret = drm_plane_set_property(plane, + drm_atomic_get_plane_state(plane, state), + property, val, blob_data); } return ret; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 5691743..909e241 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -14,6 +14,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> @@ -411,7 +412,9 @@ static int rcar_du_plane_set_property(struct drm_plane *plane, else if (property == rgrp->planes.zpos) rcar_du_plane_set_zpos(rplane, value); else - return -EINVAL; + return drm_plane_set_property(plane, + drm_atomic_get_plane_state(plane, state), + property, value, blob_data); return 0; } diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c index 035bd67..e62b0f2 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c @@ -238,7 +238,7 @@ static void shmob_drm_crtc_start(struct shmob_drm_crtc *scrtc) /* Setup planes. */ list_for_each_entry(plane, &dev->mode_config.plane_list, head) { - if (plane->crtc == crtc) + if (plane->state->crtc == crtc) shmob_drm_plane_setup(plane); } diff --git a/drivers/gpu/drm/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/shmobile/shmob_drm_plane.c index 060ae03..22da5c1 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_plane.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_plane.c @@ -14,6 +14,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> @@ -166,10 +167,10 @@ void shmob_drm_plane_setup(struct drm_plane *plane) { struct shmob_drm_plane *splane = to_shmob_plane(plane); - if (plane->fb == NULL) + if (plane->state->fb == NULL) return; - __shmob_drm_plane_setup(splane, plane->fb); + __shmob_drm_plane_setup(splane, plane->state->fb); } static int @@ -228,6 +229,7 @@ static void shmob_drm_plane_destroy(struct drm_plane *plane) static const struct drm_plane_funcs shmob_drm_plane_funcs = { .update_plane = shmob_drm_plane_update, .disable_plane = shmob_drm_plane_disable, + .set_property = drm_atomic_helper_plane_set_property, .destroy = shmob_drm_plane_destroy, }; diff --git a/drivers/gpu/host1x/drm/dc.c b/drivers/gpu/host1x/drm/dc.c index b1a05ad..5766010 100644 --- a/drivers/gpu/host1x/drm/dc.c +++ b/drivers/gpu/host1x/drm/dc.c @@ -75,11 +75,11 @@ static int tegra_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, static int tegra_plane_disable(struct drm_plane *plane) { - struct tegra_dc *dc = to_tegra_dc(plane->crtc); + struct tegra_dc *dc = to_tegra_dc(plane->state->crtc); struct tegra_plane *p = to_tegra_plane(plane); unsigned long value; - if (!plane->crtc) + if (!plane->state->crtc) return 0; value = WINDOW_A_SELECT << p->index; @@ -104,6 +104,7 @@ static void tegra_plane_destroy(struct drm_plane *plane) static const struct drm_plane_funcs tegra_plane_funcs = { .update_plane = tegra_plane_update, .disable_plane = tegra_plane_disable, + .set_property = drm_atomic_helper_plane_set_property, .destroy = tegra_plane_destroy, }; @@ -267,13 +268,14 @@ static void tegra_crtc_disable(struct drm_crtc *crtc) struct drm_plane *plane; list_for_each_entry(plane, &drm->mode_config.plane_list, head) { - if (plane->crtc == crtc) { + struct drm_plane_state *state = plane->state; + if (state->crtc == crtc) { tegra_plane_disable(plane); - plane->crtc = NULL; + state->crtc = NULL; - if (plane->fb) { - drm_framebuffer_unreference(plane->fb); - plane->fb = NULL; + if (state->fb) { + drm_framebuffer_unreference(state->fb); + state->fb = NULL; } } } diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index e70cd7b..d0d29e1 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -62,7 +62,9 @@ * struct drm_atomic_helper_funcs - helper funcs used by the atomic helpers */ struct drm_atomic_helper_funcs { - int dummy; /* for now */ + struct drm_plane_state *(*get_plane_state)(struct drm_plane *plane, void *state); + int (*check_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate); + int (*commit_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate); }; const extern struct drm_atomic_helper_funcs drm_atomic_helper_funcs; @@ -75,6 +77,37 @@ int drm_atomic_helper_check(struct drm_device *dev, void *state); int drm_atomic_helper_commit(struct drm_device *dev, void *state); void drm_atomic_helper_end(struct drm_device *dev, void *state); +int drm_atomic_helper_plane_set_property(struct drm_plane *plane, void *state, + struct drm_property *property, uint64_t val, void *blob_data); +void drm_atomic_helper_init_plane_state(struct drm_plane *plane, + struct drm_plane_state *pstate, void *state); + +static inline struct drm_plane_state * +drm_atomic_get_plane_state(struct drm_plane *plane, void *state) +{ + const struct drm_atomic_helper_funcs *funcs = + plane->dev->driver->atomic_helpers; + return funcs->get_plane_state(plane, state); +} + +static inline int +drm_atomic_check_plane_state(struct drm_plane *plane, + struct drm_plane_state *pstate) +{ + const struct drm_atomic_helper_funcs *funcs = + plane->dev->driver->atomic_helpers; + return funcs->check_plane_state(plane, pstate); +} + +static inline int +drm_atomic_commit_plane_state(struct drm_plane *plane, + struct drm_plane_state *pstate) +{ + const struct drm_atomic_helper_funcs *funcs = + plane->dev->driver->atomic_helpers; + return funcs->commit_plane_state(plane, pstate); +} + /** * struct drm_atomic_helper_state - the state object used by atomic helpers */ @@ -82,6 +115,8 @@ struct drm_atomic_helper_state { struct kref refcount; struct drm_device *dev; uint32_t flags; + struct drm_plane **planes; + struct drm_plane_state **pstates; }; static inline void diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 8831562..ee46a6a 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -675,6 +675,45 @@ struct drm_plane_funcs { }; /** + * drm_plane_state - mutable plane state + * @new_fb: has the fb been changed + * @crtc: currently bound CRTC + * @fb: currently bound fb + * @crtc_x: left position of visible portion of plane on crtc + * @crtc_y: upper position of visible portion of plane on crtc + * @crtc_w: width of visible portion of plane on crtc + * @crtc_h: height of visible portion of plane on crtc + * @src_x: left position of visible portion of plane within + * plane (in 16.16) + * @src_y: upper position of visible portion of plane within + * plane (in 16.16) + * @src_w: width of visible portion of plane (in 16.16) + * @src_h: height of visible portion of plane (in 16.16) + * @propvals: property values + * @state: current global/toplevel state object (for atomic) while an + * update is in progress, NULL otherwise. + */ +struct drm_plane_state { + bool new_fb : 1; + struct drm_crtc *crtc; + struct drm_framebuffer *fb; + + /* Signed dest location allows it to be partially off screen */ + int32_t crtc_x, crtc_y; + uint32_t crtc_w, crtc_h; + + /* Source values are 16.16 fixed point */ + uint32_t src_x, src_y; + uint32_t src_h, src_w; + + bool enabled; + + struct drm_object_property_values propvals; + + void *state; +}; + +/** * drm_plane - central DRM plane control structure * @dev: DRM device this plane belongs to * @head: for list management @@ -682,8 +721,8 @@ struct drm_plane_funcs { * @possible_crtcs: pipes this plane can be bound to * @format_types: array of formats supported by this plane * @format_count: number of formats supported - * @crtc: currently bound CRTC - * @fb: currently bound fb + * @id: plane number, 0..n + * @state: the mutable state * @funcs: helper functions * @properties: property tracking for this plane */ @@ -697,13 +736,17 @@ struct drm_plane { uint32_t *format_types; uint32_t format_count; - struct drm_crtc *crtc; - struct drm_framebuffer *fb; + int id; + + /* + * State that can be updated from userspace, and atomically + * commited or rolled back: + */ + struct drm_plane_state *state; const struct drm_plane_funcs *funcs; struct drm_object_properties properties; - struct drm_object_property_values propvals; }; /** @@ -884,8 +927,20 @@ struct drm_mode_config { bool poll_running; struct delayed_work output_poll_work; - /* pointers to standard properties */ + /* just so blob properties can always be in a list: */ struct list_head property_blob_list; + + /* pointers to standard properties */ + struct drm_property *prop_src_x; + struct drm_property *prop_src_y; + struct drm_property *prop_src_w; + struct drm_property *prop_src_h; + struct drm_property *prop_crtc_x; + struct drm_property *prop_crtc_y; + struct drm_property *prop_crtc_w; + struct drm_property *prop_crtc_h; + struct drm_property *prop_fb_id; + struct drm_property *prop_crtc_id; struct drm_property *edid_property; struct drm_property *dpms_property; @@ -969,7 +1024,15 @@ extern int drm_plane_init(struct drm_device *dev, const uint32_t *formats, uint32_t format_count, bool priv); extern void drm_plane_cleanup(struct drm_plane *plane); -extern void drm_plane_force_disable(struct drm_plane *plane); +extern void drm_plane_force_disable(struct drm_plane *plane, void *state); +extern int drm_plane_check_state(struct drm_plane *plane, + struct drm_plane_state *state); +extern void drm_plane_commit_state(struct drm_plane *plane, + struct drm_plane_state *state); +extern int drm_plane_set_property(struct drm_plane *plane, + struct drm_plane_state *state, + struct drm_property *property, + uint64_t value, void *blob_data); extern void drm_encoder_cleanup(struct drm_encoder *encoder); @@ -1023,6 +1086,17 @@ extern int drm_object_property_set_value(struct drm_mode_object *obj, extern int drm_object_property_get_value(struct drm_mode_object *obj, struct drm_property *property, uint64_t *value); + +int drm_mode_connector_set_obj_prop(struct drm_connector *connector, + void *state, struct drm_property *property, + uint64_t value, void *blob_data); +int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc, + void *state, struct drm_property *property, + uint64_t value, void *blob_data); +int drm_mode_plane_set_obj_prop(struct drm_plane *plane, + void *state, struct drm_property *property, + uint64_t value, void *blob_data); + extern int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, const struct drm_framebuffer_funcs *funcs);