[RFCv4 09/14] drm: convert plane to properties/state

Rob Clark robdclark at gmail.com
Wed Feb 26 16:18:39 PST 2014


On Wed, Feb 26, 2014 at 4:30 PM, Sean Paul <seanpaul at chromium.org> wrote:
> On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark at gmail.com> 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).
>
>
> Hi Rob,
> I've been tracking down a crash that came up on my exynos board when I
> applied this patch. I hope you can hold my hand a little bit and give
> some guidance.
>
> <snip>
>
>> +static int
>> +drm_atomic_helper_commit_plane_state(struct drm_plane *plane,
>> +               struct drm_plane_state *pstate)
>> +{
>> +       struct drm_framebuffer *old_fb = NULL, *fb = NULL;
>> +       int ret = 0;
>> +
>> +       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;
>
> I think this is slightly different than what we had before in
> setplane. In the previous code, we had:
>
> 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;
>         fb = NULL;
>         plane->crtc = crtc;
>         plane->fb = fb;
> }
>
> In this case, we'd unreference old_fb which is the previous plane->fb.
> AFAICT, update_plane did not set plane->fb to fb, so it would, in
> fact, be the old plane.

to be honest, I need to dig up that branch again and remember (and
rebase it while I'm at it)..

I don't think the driver should be changing state->fb (ie. the state
object should in theory, once constructed, be a sort of constant
thing).  So until swap_plane_state() plane->state->fb is *supposed* to
be the old fb.

For crtc, as there were so many places in code accessing crtc->fb (and
it was pretty intertwined in the crtc helpers), the way I left it as:
crtc->state->fb is what is requested by userspace, and crtc->fb is
what is currently used (which is updated to the new cstate->fb in the
course of modeset).

I don't remember if I left it the same way for plane (which isn't
referenced in nearly as many places, and which doesn't have a big
chunk of modeset helper to care about).

> In the new code, drm_mode_setplane calls drm_mode_plane_set_obj_prop
> on fb_id, which will update plane->state->fb. Then we come in here and
> unreference it as old_fb. I _believe_ we're taking one more reference
> than we need in this case.

well, it should be updating 'pstate', what will become the new
plane->state..  But not the current plane->state.

BR,
-R

> What I'm seeing in exynos is that when we setplane to an fb, we leave
> it with refcount == 1. If we disable that plane, we'll free the fb.
> Unfortunately, this leaves the fb in the fbs list and the next time we
> try to iterate that list Bad Things Happen.
>
> Does this make any sense?
>
> Sean
>
>> +                       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);
>> +       }
>> +
>> +
>> +       if (fb)
>> +               drm_framebuffer_unreference(fb);
>> +       if (old_fb)
>> +               drm_framebuffer_unreference(old_fb);
>> +
>> +       return ret;
>> +}
>>
>
> <snip>
>
>> +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);
>> +               struct drm_crtc *crtc = obj ? obj_to_crtc(obj) : NULL;
>> +               /* take the lock of the incoming crtc as well, moving
>> +                * plane between crtcs is synchronized on both incoming
>> +                * and outgoing crtc.
>> +                */
>> +               if (crtc) {
>> +                       int ret = drm_modeset_lock(&crtc->mutex, state->state);
>> +                       if (ret)
>> +                               return ret;
>> +               }
>> +               state->crtc = crtc;
>> +       } 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);
>> +
>
> <snip>
>
>> @@ -1987,20 +2192,19 @@ 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_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.
>> -        */
>> +retry:
>> +       state = dev->driver->atomic_begin(dev, 0);
>> +       if (IS_ERR(state))
>> +               return PTR_ERR(state);
>> +
>>         plane = drm_plane_find(dev, plane_req->plane_id);
>>         if (!plane) {
>>                 DRM_DEBUG_KMS("Unknown plane ID %d\n",
>> @@ -2008,98 +2212,37 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
>>                 return -ENOENT;
>>         }
>>
>> -       /* 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;
>> -       }
>> -
>> -       crtc = drm_crtc_find(dev, plane_req->crtc_id);
>> -       if (!crtc) {
>> -               DRM_DEBUG_KMS("Unknown crtc ID %d\n",
>> -                             plane_req->crtc_id);
>> -               ret = -ENOENT;
>> -               goto out;
>> -       }
>> -
>> -       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_plane_set_obj_prop(plane, state,
>> +                       config->prop_crtc_id, plane_req->crtc_id, NULL) ||
>> +               drm_mode_plane_set_obj_prop(plane, state,
>> +                       config->prop_fb_id, plane_req->fb_id, NULL) ||
>> +               drm_mode_plane_set_obj_prop(plane, state,
>> +                       config->prop_crtc_x, I642U64(plane_req->crtc_x), NULL) ||
>> +               drm_mode_plane_set_obj_prop(plane, state,
>> +                       config->prop_crtc_y, I642U64(plane_req->crtc_y), NULL) ||
>> +               drm_mode_plane_set_obj_prop(plane, state,
>> +                       config->prop_crtc_w, plane_req->crtc_w, NULL) ||
>> +               drm_mode_plane_set_obj_prop(plane, state,
>> +                       config->prop_crtc_h, plane_req->crtc_h, NULL) ||
>> +               drm_mode_plane_set_obj_prop(plane, state,
>> +                       config->prop_src_w, plane_req->src_w, NULL) ||
>> +               drm_mode_plane_set_obj_prop(plane, state,
>> +                       config->prop_src_h, plane_req->src_h, NULL) ||
>> +               drm_mode_plane_set_obj_prop(plane, state,
>> +                       config->prop_src_x, plane_req->src_x, NULL) ||
>> +               drm_mode_plane_set_obj_prop(plane, 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);
>> +       if (ret == -EDEADLK)
>> +               goto retry;
>>         return ret;
>>  }
>>
>
> <snip>


More information about the dri-devel mailing list