diff mbox

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

Message ID 1385390858-4412-10-git-send-email-robdclark@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Rob Clark Nov. 25, 2013, 2:47 p.m. UTC
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         | 156 ++++++++++-
 drivers/gpu/drm/drm_crtc.c                  | 397 +++++++++++++++++++---------
 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   |  15 +-
 drivers/gpu/drm/i915/intel_sprite.c         |  21 +-
 drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   2 +-
 drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |  19 +-
 drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   8 +-
 drivers/gpu/drm/omapdrm/omap_crtc.c         |   4 +-
 drivers/gpu/drm/omapdrm/omap_drv.c          |   2 +-
 drivers/gpu/drm/omapdrm/omap_plane.c        |  33 ++-
 drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   8 +-
 drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |   2 +-
 drivers/gpu/drm/shmobile/shmob_drm_plane.c  |   6 +-
 drivers/gpu/drm/tegra/dc.c                  |  16 +-
 include/drm/drm_atomic_helper.h             |  39 ++-
 include/drm/drm_crtc.h                      |  88 +++++-
 19 files changed, 654 insertions(+), 189 deletions(-)

Comments

Sean Paul Jan. 28, 2014, 9:52 p.m. UTC | #1
On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@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).
> ---
>  drivers/gpu/drm/drm_atomic_helper.c         | 156 ++++++++++-
>  drivers/gpu/drm/drm_crtc.c                  | 397 +++++++++++++++++++---------
>  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   |  15 +-
>  drivers/gpu/drm/i915/intel_sprite.c         |  21 +-
>  drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   2 +-
>  drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |  19 +-
>  drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   8 +-
>  drivers/gpu/drm/omapdrm/omap_crtc.c         |   4 +-
>  drivers/gpu/drm/omapdrm/omap_drv.c          |   2 +-
>  drivers/gpu/drm/omapdrm/omap_plane.c        |  33 ++-
>  drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   8 +-
>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |   2 +-
>  drivers/gpu/drm/shmobile/shmob_drm_plane.c  |   6 +-
>  drivers/gpu/drm/tegra/dc.c                  |  16 +-
>  include/drm/drm_atomic_helper.h             |  39 ++-
>  include/drm/drm_crtc.h                      |  88 +++++-
>  19 files changed, 654 insertions(+), 189 deletions(-)
>

<snip>

> diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
> index 2e31fb8..d585a4c 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);
> @@ -225,13 +226,17 @@ static int exynos_plane_set_property(struct drm_plane *plane,
>         struct drm_device *dev = plane->dev;
>         struct exynos_plane *exynos_plane = to_exynos_plane(plane);
>         struct exynos_drm_private *dev_priv = dev->dev_private;
> +       struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);

Hi Rob,
This seems to cause a deadlock. drm_mode_obj_set_property_ioctl
performs a drm_modeset_lock_all first, then
drm_atomic_helper_get_plane_state tries to lock the crtc[s] again.

Trace:
(__schedule+0x4d8/0x71c) from [<c0512590>] (schedule+0x90/0x94)
(schedule+0x90/0x94) from [<c0512b1c>] (schedule_preempt_disabled+0x18/0x1c)
(schedule_preempt_disabled+0x18/0x1c) from [<c0510c78>]
(__ww_mutex_lock_slowpath+0x208/0x2cc)
(__ww_mutex_lock_slowpath+0x208/0x2cc) from [<c0510d8c>]
(__ww_mutex_lock+0x50/0xc4)
(__ww_mutex_lock+0x50/0xc4) from [<c028dfc0>] (modeset_lock_state+0x58/0x174)
(modeset_lock_state+0x58/0x174) from [<c028e0fc>] (drm_modeset_lock+0x20/0x30)
(drm_modeset_lock+0x20/0x30) from [<c028e148>]
(drm_modeset_lock_all_crtcs+0x3c/0x54)
(drm_modeset_lock_all_crtcs+0x3c/0x54) from [<c029c234>]
(drm_atomic_helper_get_plane_state+0x40/0xc4)
(drm_atomic_helper_get_plane_state+0x40/0xc4) from [<c02a2d58>]
(exynos_plane_set_property+0x3c/0xbc)
(exynos_plane_set_property+0x3c/0xbc) from [<c028dab4>]
(drm_mode_plane_set_obj_prop+0x3c/0x4c)
(drm_mode_plane_set_obj_prop+0x3c/0x4c) from [<c0290734>]
(drm_mode_set_obj_prop+0xac/0x128)
(drm_mode_set_obj_prop+0xac/0x128) from [<c0294814>]
(drm_mode_obj_set_property_ioctl+0xe4/0x15c)
(drm_mode_obj_set_property_ioctl+0xe4/0x15c) from [<c028468c>]
(drm_ioctl+0x2e0/0x40c)
(drm_ioctl+0x2e0/0x40c) from [<c0110c48>] (do_vfs_ioctl+0x508/0x5dc)

Sean


> +
> +       if (IS_ERR(pstate))
> +               return PTR_ERR(pstate);
>
>         if (property == dev_priv->plane_zpos_property) {
>                 exynos_plane->overlay.zpos = val;
>                 return 0;
>         }
>
> -       return -EINVAL;
> +       return drm_plane_set_property(plane, pstate, property, val, blob_data);
>  }
>
>  static struct drm_plane_funcs exynos_plane_funcs = {

<snip>

> --
> 1.8.4.2
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
Rob Clark Jan. 29, 2014, 1:44 p.m. UTC | #2
On Tue, Jan 28, 2014 at 4:52 PM, Sean Paul <seanpaul@chromium.org> wrote:
> On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@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).
>> ---
>>  drivers/gpu/drm/drm_atomic_helper.c         | 156 ++++++++++-
>>  drivers/gpu/drm/drm_crtc.c                  | 397 +++++++++++++++++++---------
>>  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   |  15 +-
>>  drivers/gpu/drm/i915/intel_sprite.c         |  21 +-
>>  drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   2 +-
>>  drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |  19 +-
>>  drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   8 +-
>>  drivers/gpu/drm/omapdrm/omap_crtc.c         |   4 +-
>>  drivers/gpu/drm/omapdrm/omap_drv.c          |   2 +-
>>  drivers/gpu/drm/omapdrm/omap_plane.c        |  33 ++-
>>  drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   8 +-
>>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |   2 +-
>>  drivers/gpu/drm/shmobile/shmob_drm_plane.c  |   6 +-
>>  drivers/gpu/drm/tegra/dc.c                  |  16 +-
>>  include/drm/drm_atomic_helper.h             |  39 ++-
>>  include/drm/drm_crtc.h                      |  88 +++++-
>>  19 files changed, 654 insertions(+), 189 deletions(-)
>>
>
> <snip>
>
>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
>> index 2e31fb8..d585a4c 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);
>> @@ -225,13 +226,17 @@ static int exynos_plane_set_property(struct drm_plane *plane,
>>         struct drm_device *dev = plane->dev;
>>         struct exynos_plane *exynos_plane = to_exynos_plane(plane);
>>         struct exynos_drm_private *dev_priv = dev->dev_private;
>> +       struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
>
> Hi Rob,
> This seems to cause a deadlock. drm_mode_obj_set_property_ioctl
> performs a drm_modeset_lock_all first, then
> drm_atomic_helper_get_plane_state tries to lock the crtc[s] again.

oh, whoops, I guess neither modetest or glplane test does a
set-property ioctl.  I think we simply need to drop the locking at the
top level ioctl fxn, and let the atomic locking take care of things.

BR,
-R

> Trace:
> (__schedule+0x4d8/0x71c) from [<c0512590>] (schedule+0x90/0x94)
> (schedule+0x90/0x94) from [<c0512b1c>] (schedule_preempt_disabled+0x18/0x1c)
> (schedule_preempt_disabled+0x18/0x1c) from [<c0510c78>]
> (__ww_mutex_lock_slowpath+0x208/0x2cc)
> (__ww_mutex_lock_slowpath+0x208/0x2cc) from [<c0510d8c>]
> (__ww_mutex_lock+0x50/0xc4)
> (__ww_mutex_lock+0x50/0xc4) from [<c028dfc0>] (modeset_lock_state+0x58/0x174)
> (modeset_lock_state+0x58/0x174) from [<c028e0fc>] (drm_modeset_lock+0x20/0x30)
> (drm_modeset_lock+0x20/0x30) from [<c028e148>]
> (drm_modeset_lock_all_crtcs+0x3c/0x54)
> (drm_modeset_lock_all_crtcs+0x3c/0x54) from [<c029c234>]
> (drm_atomic_helper_get_plane_state+0x40/0xc4)
> (drm_atomic_helper_get_plane_state+0x40/0xc4) from [<c02a2d58>]
> (exynos_plane_set_property+0x3c/0xbc)
> (exynos_plane_set_property+0x3c/0xbc) from [<c028dab4>]
> (drm_mode_plane_set_obj_prop+0x3c/0x4c)
> (drm_mode_plane_set_obj_prop+0x3c/0x4c) from [<c0290734>]
> (drm_mode_set_obj_prop+0xac/0x128)
> (drm_mode_set_obj_prop+0xac/0x128) from [<c0294814>]
> (drm_mode_obj_set_property_ioctl+0xe4/0x15c)
> (drm_mode_obj_set_property_ioctl+0xe4/0x15c) from [<c028468c>]
> (drm_ioctl+0x2e0/0x40c)
> (drm_ioctl+0x2e0/0x40c) from [<c0110c48>] (do_vfs_ioctl+0x508/0x5dc)
>
> Sean
>
>
>> +
>> +       if (IS_ERR(pstate))
>> +               return PTR_ERR(pstate);
>>
>>         if (property == dev_priv->plane_zpos_property) {
>>                 exynos_plane->overlay.zpos = val;
>>                 return 0;
>>         }
>>
>> -       return -EINVAL;
>> +       return drm_plane_set_property(plane, pstate, property, val, blob_data);
>>  }
>>
>>  static struct drm_plane_funcs exynos_plane_funcs = {
>
> <snip>
>
>> --
>> 1.8.4.2
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
Sean Paul Feb. 7, 2014, 2:53 a.m. UTC | #3
On Wed, Jan 29, 2014 at 8:44 AM, Rob Clark <robdclark@gmail.com> wrote:
> On Tue, Jan 28, 2014 at 4:52 PM, Sean Paul <seanpaul@chromium.org> wrote:
>> On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@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).
>>> ---
>>>  drivers/gpu/drm/drm_atomic_helper.c         | 156 ++++++++++-
>>>  drivers/gpu/drm/drm_crtc.c                  | 397 +++++++++++++++++++---------
>>>  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   |  15 +-
>>>  drivers/gpu/drm/i915/intel_sprite.c         |  21 +-
>>>  drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   2 +-
>>>  drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |  19 +-
>>>  drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   8 +-
>>>  drivers/gpu/drm/omapdrm/omap_crtc.c         |   4 +-
>>>  drivers/gpu/drm/omapdrm/omap_drv.c          |   2 +-
>>>  drivers/gpu/drm/omapdrm/omap_plane.c        |  33 ++-
>>>  drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   8 +-
>>>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |   2 +-
>>>  drivers/gpu/drm/shmobile/shmob_drm_plane.c  |   6 +-
>>>  drivers/gpu/drm/tegra/dc.c                  |  16 +-
>>>  include/drm/drm_atomic_helper.h             |  39 ++-
>>>  include/drm/drm_crtc.h                      |  88 +++++-
>>>  19 files changed, 654 insertions(+), 189 deletions(-)
>>>
>>
>> <snip>
>>
>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
>>> index 2e31fb8..d585a4c 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);
>>> @@ -225,13 +226,17 @@ static int exynos_plane_set_property(struct drm_plane *plane,
>>>         struct drm_device *dev = plane->dev;
>>>         struct exynos_plane *exynos_plane = to_exynos_plane(plane);
>>>         struct exynos_drm_private *dev_priv = dev->dev_private;
>>> +       struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
>>
>> Hi Rob,
>> This seems to cause a deadlock. drm_mode_obj_set_property_ioctl
>> performs a drm_modeset_lock_all first, then
>> drm_atomic_helper_get_plane_state tries to lock the crtc[s] again.
>
> oh, whoops, I guess neither modetest or glplane test does a
> set-property ioctl.  I think we simply need to drop the locking at the
> top level ioctl fxn, and let the atomic locking take care of things.
>

Seems like there's another one in the drm_fb_helper_restore_fbdev_mode path.

(modeset_lock_state+0x58/0x174) from [<c028e300>] (drm_modeset_lock+0x20/0x30)
(drm_modeset_lock+0x20/0x30) from [<c028e34c>]
(drm_modeset_lock_all_crtcs+0x3c/0x54)
(drm_modeset_lock_all_crtcs+0x3c/0x54) from [<c029b54c>]
(drm_atomic_helper_get_plane_state+0x40/0xc4)
(drm_atomic_helper_get_plane_state+0x40/0xc4) from [<c02a2008>]
(exynos_plane_set_property+0x3c/0xbc)
(exynos_plane_set_property+0x3c/0xbc) from [<c028dcb8>]
(drm_mode_plane_set_obj_prop+0x3c/0x4c)
(drm_mode_plane_set_obj_prop+0x3c/0x4c) from [<c028dd08>]
(drm_plane_force_disable+0x40/0x60)
(drm_fb_helper_restore_fbdev_mode+0x80/0x158) from [<c029ee78>]
(exynos_drm_fbdev_restore_mode+0x54/0x6c)
(exynos_drm_fbdev_restore_mode+0x54/0x6c) from [<c029bc28>]
(exynos_drm_lastclose+0x34/0x44)
(exynos_drm_lastclose+0x34/0x44) from [<c0284bb0>] (drm_lastclose+0x44/0x158)
(drm_lastclose+0x44/0x158) from [<c02857c0>] (drm_release+0x4c4/0x51c)
(drm_release+0x4c4/0x51c) from [<c0101410>] (__fput+0x108/0x208)


Sean


> BR,
> -R
>
>> Trace:
>> (__schedule+0x4d8/0x71c) from [<c0512590>] (schedule+0x90/0x94)
>> (schedule+0x90/0x94) from [<c0512b1c>] (schedule_preempt_disabled+0x18/0x1c)
>> (schedule_preempt_disabled+0x18/0x1c) from [<c0510c78>]
>> (__ww_mutex_lock_slowpath+0x208/0x2cc)
>> (__ww_mutex_lock_slowpath+0x208/0x2cc) from [<c0510d8c>]
>> (__ww_mutex_lock+0x50/0xc4)
>> (__ww_mutex_lock+0x50/0xc4) from [<c028dfc0>] (modeset_lock_state+0x58/0x174)
>> (modeset_lock_state+0x58/0x174) from [<c028e0fc>] (drm_modeset_lock+0x20/0x30)
>> (drm_modeset_lock+0x20/0x30) from [<c028e148>]
>> (drm_modeset_lock_all_crtcs+0x3c/0x54)
>> (drm_modeset_lock_all_crtcs+0x3c/0x54) from [<c029c234>]
>> (drm_atomic_helper_get_plane_state+0x40/0xc4)
>> (drm_atomic_helper_get_plane_state+0x40/0xc4) from [<c02a2d58>]
>> (exynos_plane_set_property+0x3c/0xbc)
>> (exynos_plane_set_property+0x3c/0xbc) from [<c028dab4>]
>> (drm_mode_plane_set_obj_prop+0x3c/0x4c)
>> (drm_mode_plane_set_obj_prop+0x3c/0x4c) from [<c0290734>]
>> (drm_mode_set_obj_prop+0xac/0x128)
>> (drm_mode_set_obj_prop+0xac/0x128) from [<c0294814>]
>> (drm_mode_obj_set_property_ioctl+0xe4/0x15c)
>> (drm_mode_obj_set_property_ioctl+0xe4/0x15c) from [<c028468c>]
>> (drm_ioctl+0x2e0/0x40c)
>> (drm_ioctl+0x2e0/0x40c) from [<c0110c48>] (do_vfs_ioctl+0x508/0x5dc)
>>
>> Sean
>>
>>
>>> +
>>> +       if (IS_ERR(pstate))
>>> +               return PTR_ERR(pstate);
>>>
>>>         if (property == dev_priv->plane_zpos_property) {
>>>                 exynos_plane->overlay.zpos = val;
>>>                 return 0;
>>>         }
>>>
>>> -       return -EINVAL;
>>> +       return drm_plane_set_property(plane, pstate, property, val, blob_data);
>>>  }
>>>
>>>  static struct drm_plane_funcs exynos_plane_funcs = {
>>
>> <snip>
>>
>>> --
>>> 1.8.4.2
>>>
>>> _______________________________________________
>>> dri-devel mailing list
>>> dri-devel@lists.freedesktop.org
>>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
Rob Clark Feb. 7, 2014, 12:28 p.m. UTC | #4
On Thu, Feb 6, 2014 at 9:53 PM, Sean Paul <seanpaul@chromium.org> wrote:
> On Wed, Jan 29, 2014 at 8:44 AM, Rob Clark <robdclark@gmail.com> wrote:
>> On Tue, Jan 28, 2014 at 4:52 PM, Sean Paul <seanpaul@chromium.org> wrote:
>>> On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@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).
>>>> ---
>>>>  drivers/gpu/drm/drm_atomic_helper.c         | 156 ++++++++++-
>>>>  drivers/gpu/drm/drm_crtc.c                  | 397 +++++++++++++++++++---------
>>>>  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   |  15 +-
>>>>  drivers/gpu/drm/i915/intel_sprite.c         |  21 +-
>>>>  drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   2 +-
>>>>  drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |  19 +-
>>>>  drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   8 +-
>>>>  drivers/gpu/drm/omapdrm/omap_crtc.c         |   4 +-
>>>>  drivers/gpu/drm/omapdrm/omap_drv.c          |   2 +-
>>>>  drivers/gpu/drm/omapdrm/omap_plane.c        |  33 ++-
>>>>  drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   8 +-
>>>>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |   2 +-
>>>>  drivers/gpu/drm/shmobile/shmob_drm_plane.c  |   6 +-
>>>>  drivers/gpu/drm/tegra/dc.c                  |  16 +-
>>>>  include/drm/drm_atomic_helper.h             |  39 ++-
>>>>  include/drm/drm_crtc.h                      |  88 +++++-
>>>>  19 files changed, 654 insertions(+), 189 deletions(-)
>>>>
>>>
>>> <snip>
>>>
>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
>>>> index 2e31fb8..d585a4c 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);
>>>> @@ -225,13 +226,17 @@ static int exynos_plane_set_property(struct drm_plane *plane,
>>>>         struct drm_device *dev = plane->dev;
>>>>         struct exynos_plane *exynos_plane = to_exynos_plane(plane);
>>>>         struct exynos_drm_private *dev_priv = dev->dev_private;
>>>> +       struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
>>>
>>> Hi Rob,
>>> This seems to cause a deadlock. drm_mode_obj_set_property_ioctl
>>> performs a drm_modeset_lock_all first, then
>>> drm_atomic_helper_get_plane_state tries to lock the crtc[s] again.
>>
>> oh, whoops, I guess neither modetest or glplane test does a
>> set-property ioctl.  I think we simply need to drop the locking at the
>> top level ioctl fxn, and let the atomic locking take care of things.
>>
>
> Seems like there's another one in the drm_fb_helper_restore_fbdev_mode path.
>
> (modeset_lock_state+0x58/0x174) from [<c028e300>] (drm_modeset_lock+0x20/0x30)
> (drm_modeset_lock+0x20/0x30) from [<c028e34c>]
> (drm_modeset_lock_all_crtcs+0x3c/0x54)
> (drm_modeset_lock_all_crtcs+0x3c/0x54) from [<c029b54c>]
> (drm_atomic_helper_get_plane_state+0x40/0xc4)
> (drm_atomic_helper_get_plane_state+0x40/0xc4) from [<c02a2008>]
> (exynos_plane_set_property+0x3c/0xbc)
> (exynos_plane_set_property+0x3c/0xbc) from [<c028dcb8>]
> (drm_mode_plane_set_obj_prop+0x3c/0x4c)
> (drm_mode_plane_set_obj_prop+0x3c/0x4c) from [<c028dd08>]
> (drm_plane_force_disable+0x40/0x60)
> (drm_fb_helper_restore_fbdev_mode+0x80/0x158) from [<c029ee78>]
> (exynos_drm_fbdev_restore_mode+0x54/0x6c)
> (exynos_drm_fbdev_restore_mode+0x54/0x6c) from [<c029bc28>]
> (exynos_drm_lastclose+0x34/0x44)
> (exynos_drm_lastclose+0x34/0x44) from [<c0284bb0>] (drm_lastclose+0x44/0x158)
> (drm_lastclose+0x44/0x158) from [<c02857c0>] (drm_release+0x4c4/0x51c)
> (drm_release+0x4c4/0x51c) from [<c0101410>] (__fput+0x108/0x208)
>

In theory the drm_modeset_{lock,unlock}_all() stuff should be removed
from exynos_drm_fbdev_restore_mode().. perhaps I missed updating
exynos?

BR,
-R

>
> Sean
>
>
>> BR,
>> -R
>>
>>> Trace:
>>> (__schedule+0x4d8/0x71c) from [<c0512590>] (schedule+0x90/0x94)
>>> (schedule+0x90/0x94) from [<c0512b1c>] (schedule_preempt_disabled+0x18/0x1c)
>>> (schedule_preempt_disabled+0x18/0x1c) from [<c0510c78>]
>>> (__ww_mutex_lock_slowpath+0x208/0x2cc)
>>> (__ww_mutex_lock_slowpath+0x208/0x2cc) from [<c0510d8c>]
>>> (__ww_mutex_lock+0x50/0xc4)
>>> (__ww_mutex_lock+0x50/0xc4) from [<c028dfc0>] (modeset_lock_state+0x58/0x174)
>>> (modeset_lock_state+0x58/0x174) from [<c028e0fc>] (drm_modeset_lock+0x20/0x30)
>>> (drm_modeset_lock+0x20/0x30) from [<c028e148>]
>>> (drm_modeset_lock_all_crtcs+0x3c/0x54)
>>> (drm_modeset_lock_all_crtcs+0x3c/0x54) from [<c029c234>]
>>> (drm_atomic_helper_get_plane_state+0x40/0xc4)
>>> (drm_atomic_helper_get_plane_state+0x40/0xc4) from [<c02a2d58>]
>>> (exynos_plane_set_property+0x3c/0xbc)
>>> (exynos_plane_set_property+0x3c/0xbc) from [<c028dab4>]
>>> (drm_mode_plane_set_obj_prop+0x3c/0x4c)
>>> (drm_mode_plane_set_obj_prop+0x3c/0x4c) from [<c0290734>]
>>> (drm_mode_set_obj_prop+0xac/0x128)
>>> (drm_mode_set_obj_prop+0xac/0x128) from [<c0294814>]
>>> (drm_mode_obj_set_property_ioctl+0xe4/0x15c)
>>> (drm_mode_obj_set_property_ioctl+0xe4/0x15c) from [<c028468c>]
>>> (drm_ioctl+0x2e0/0x40c)
>>> (drm_ioctl+0x2e0/0x40c) from [<c0110c48>] (do_vfs_ioctl+0x508/0x5dc)
>>>
>>> Sean
>>>
>>>
>>>> +
>>>> +       if (IS_ERR(pstate))
>>>> +               return PTR_ERR(pstate);
>>>>
>>>>         if (property == dev_priv->plane_zpos_property) {
>>>>                 exynos_plane->overlay.zpos = val;
>>>>                 return 0;
>>>>         }
>>>>
>>>> -       return -EINVAL;
>>>> +       return drm_plane_set_property(plane, pstate, property, val, blob_data);
>>>>  }
>>>>
>>>>  static struct drm_plane_funcs exynos_plane_funcs = {
>>>
>>> <snip>
>>>
>>>> --
>>>> 1.8.4.2
>>>>
>>>> _______________________________________________
>>>> dri-devel mailing list
>>>> dri-devel@lists.freedesktop.org
>>>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
Sean Paul Feb. 10, 2014, 5:45 p.m. UTC | #5
On Fri, Feb 7, 2014 at 7:28 AM, Rob Clark <robdclark@gmail.com> wrote:
> On Thu, Feb 6, 2014 at 9:53 PM, Sean Paul <seanpaul@chromium.org> wrote:
>> On Wed, Jan 29, 2014 at 8:44 AM, Rob Clark <robdclark@gmail.com> wrote:
>>> On Tue, Jan 28, 2014 at 4:52 PM, Sean Paul <seanpaul@chromium.org> wrote:
>>>> On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@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).
>>>>> ---
>>>>>  drivers/gpu/drm/drm_atomic_helper.c         | 156 ++++++++++-
>>>>>  drivers/gpu/drm/drm_crtc.c                  | 397 +++++++++++++++++++---------
>>>>>  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   |  15 +-
>>>>>  drivers/gpu/drm/i915/intel_sprite.c         |  21 +-
>>>>>  drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   2 +-
>>>>>  drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |  19 +-
>>>>>  drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   8 +-
>>>>>  drivers/gpu/drm/omapdrm/omap_crtc.c         |   4 +-
>>>>>  drivers/gpu/drm/omapdrm/omap_drv.c          |   2 +-
>>>>>  drivers/gpu/drm/omapdrm/omap_plane.c        |  33 ++-
>>>>>  drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   8 +-
>>>>>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |   2 +-
>>>>>  drivers/gpu/drm/shmobile/shmob_drm_plane.c  |   6 +-
>>>>>  drivers/gpu/drm/tegra/dc.c                  |  16 +-
>>>>>  include/drm/drm_atomic_helper.h             |  39 ++-
>>>>>  include/drm/drm_crtc.h                      |  88 +++++-
>>>>>  19 files changed, 654 insertions(+), 189 deletions(-)
>>>>>
>>>>
>>>> <snip>
>>>>
>>>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
>>>>> index 2e31fb8..d585a4c 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);
>>>>> @@ -225,13 +226,17 @@ static int exynos_plane_set_property(struct drm_plane *plane,
>>>>>         struct drm_device *dev = plane->dev;
>>>>>         struct exynos_plane *exynos_plane = to_exynos_plane(plane);
>>>>>         struct exynos_drm_private *dev_priv = dev->dev_private;
>>>>> +       struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
>>>>
>>>> Hi Rob,
>>>> This seems to cause a deadlock. drm_mode_obj_set_property_ioctl
>>>> performs a drm_modeset_lock_all first, then
>>>> drm_atomic_helper_get_plane_state tries to lock the crtc[s] again.
>>>
>>> oh, whoops, I guess neither modetest or glplane test does a
>>> set-property ioctl.  I think we simply need to drop the locking at the
>>> top level ioctl fxn, and let the atomic locking take care of things.
>>>
>>
>> Seems like there's another one in the drm_fb_helper_restore_fbdev_mode path.
>>
>> (modeset_lock_state+0x58/0x174) from [<c028e300>] (drm_modeset_lock+0x20/0x30)
>> (drm_modeset_lock+0x20/0x30) from [<c028e34c>]
>> (drm_modeset_lock_all_crtcs+0x3c/0x54)
>> (drm_modeset_lock_all_crtcs+0x3c/0x54) from [<c029b54c>]
>> (drm_atomic_helper_get_plane_state+0x40/0xc4)
>> (drm_atomic_helper_get_plane_state+0x40/0xc4) from [<c02a2008>]
>> (exynos_plane_set_property+0x3c/0xbc)
>> (exynos_plane_set_property+0x3c/0xbc) from [<c028dcb8>]
>> (drm_mode_plane_set_obj_prop+0x3c/0x4c)
>> (drm_mode_plane_set_obj_prop+0x3c/0x4c) from [<c028dd08>]
>> (drm_plane_force_disable+0x40/0x60)
>> (drm_fb_helper_restore_fbdev_mode+0x80/0x158) from [<c029ee78>]
>> (exynos_drm_fbdev_restore_mode+0x54/0x6c)
>> (exynos_drm_fbdev_restore_mode+0x54/0x6c) from [<c029bc28>]
>> (exynos_drm_lastclose+0x34/0x44)
>> (exynos_drm_lastclose+0x34/0x44) from [<c0284bb0>] (drm_lastclose+0x44/0x158)
>> (drm_lastclose+0x44/0x158) from [<c02857c0>] (drm_release+0x4c4/0x51c)
>> (drm_release+0x4c4/0x51c) from [<c0101410>] (__fput+0x108/0x208)
>>
>
> In theory the drm_modeset_{lock,unlock}_all() stuff should be removed
> from exynos_drm_fbdev_restore_mode().. perhaps I missed updating
> exynos?
>

Yeah, seems like it. IIRC, this was added during Daniel's locking
changes, so maybe it was too new.

Sean



> BR,
> -R
>
>>
>> Sean
>>
>>
>>> BR,
>>> -R
>>>
>>>> Trace:
>>>> (__schedule+0x4d8/0x71c) from [<c0512590>] (schedule+0x90/0x94)
>>>> (schedule+0x90/0x94) from [<c0512b1c>] (schedule_preempt_disabled+0x18/0x1c)
>>>> (schedule_preempt_disabled+0x18/0x1c) from [<c0510c78>]
>>>> (__ww_mutex_lock_slowpath+0x208/0x2cc)
>>>> (__ww_mutex_lock_slowpath+0x208/0x2cc) from [<c0510d8c>]
>>>> (__ww_mutex_lock+0x50/0xc4)
>>>> (__ww_mutex_lock+0x50/0xc4) from [<c028dfc0>] (modeset_lock_state+0x58/0x174)
>>>> (modeset_lock_state+0x58/0x174) from [<c028e0fc>] (drm_modeset_lock+0x20/0x30)
>>>> (drm_modeset_lock+0x20/0x30) from [<c028e148>]
>>>> (drm_modeset_lock_all_crtcs+0x3c/0x54)
>>>> (drm_modeset_lock_all_crtcs+0x3c/0x54) from [<c029c234>]
>>>> (drm_atomic_helper_get_plane_state+0x40/0xc4)
>>>> (drm_atomic_helper_get_plane_state+0x40/0xc4) from [<c02a2d58>]
>>>> (exynos_plane_set_property+0x3c/0xbc)
>>>> (exynos_plane_set_property+0x3c/0xbc) from [<c028dab4>]
>>>> (drm_mode_plane_set_obj_prop+0x3c/0x4c)
>>>> (drm_mode_plane_set_obj_prop+0x3c/0x4c) from [<c0290734>]
>>>> (drm_mode_set_obj_prop+0xac/0x128)
>>>> (drm_mode_set_obj_prop+0xac/0x128) from [<c0294814>]
>>>> (drm_mode_obj_set_property_ioctl+0xe4/0x15c)
>>>> (drm_mode_obj_set_property_ioctl+0xe4/0x15c) from [<c028468c>]
>>>> (drm_ioctl+0x2e0/0x40c)
>>>> (drm_ioctl+0x2e0/0x40c) from [<c0110c48>] (do_vfs_ioctl+0x508/0x5dc)
>>>>
>>>> Sean
>>>>
>>>>
>>>>> +
>>>>> +       if (IS_ERR(pstate))
>>>>> +               return PTR_ERR(pstate);
>>>>>
>>>>>         if (property == dev_priv->plane_zpos_property) {
>>>>>                 exynos_plane->overlay.zpos = val;
>>>>>                 return 0;
>>>>>         }
>>>>>
>>>>> -       return -EINVAL;
>>>>> +       return drm_plane_set_property(plane, pstate, property, val, blob_data);
>>>>>  }
>>>>>
>>>>>  static struct drm_plane_funcs exynos_plane_funcs = {
>>>>
>>>> <snip>
>>>>
>>>>> --
>>>>> 1.8.4.2
>>>>>
>>>>> _______________________________________________
>>>>> dri-devel mailing list
>>>>> dri-devel@lists.freedesktop.org
>>>>> http://lists.freedesktop.org/mailman/listinfo/dri-devel
Sean Paul Feb. 26, 2014, 9:30 p.m. UTC | #6
On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@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.

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.

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>
Rob Clark Feb. 27, 2014, 12:18 a.m. UTC | #7
On Wed, Feb 26, 2014 at 4:30 PM, Sean Paul <seanpaul@chromium.org> wrote:
> On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@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>
Sean Paul March 18, 2014, 3:48 p.m. UTC | #8
On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@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).
> ---
>  drivers/gpu/drm/drm_atomic_helper.c         | 156 ++++++++++-
>  drivers/gpu/drm/drm_crtc.c                  | 397 +++++++++++++++++++---------
>  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   |  15 +-
>  drivers/gpu/drm/i915/intel_sprite.c         |  21 +-
>  drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   2 +-
>  drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |  19 +-
>  drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   8 +-
>  drivers/gpu/drm/omapdrm/omap_crtc.c         |   4 +-
>  drivers/gpu/drm/omapdrm/omap_drv.c          |   2 +-
>  drivers/gpu/drm/omapdrm/omap_plane.c        |  33 ++-
>  drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   8 +-
>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |   2 +-
>  drivers/gpu/drm/shmobile/shmob_drm_plane.c  |   6 +-
>  drivers/gpu/drm/tegra/dc.c                  |  16 +-
>  include/drm/drm_atomic_helper.h             |  39 ++-
>  include/drm/drm_crtc.h                      |  88 +++++-
>  19 files changed, 654 insertions(+), 189 deletions(-)
>

<snip>

> +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;
> +       int ret;
> +
> +       /* grab lock of current crtc.. if crtc is NULL then grab all: */
> +       if (plane->state->crtc)
> +               ret = drm_modeset_lock(&plane->state->crtc->mutex, state);
> +       else
> +               ret = drm_modeset_lock_all_crtcs(plane->dev, state);


Hi Rob,
There seems to be a recursive lock in the
drm_fb_helper_restore_fbdev_mode path. The code loops through all
planes and does a force disable. If 2 of the planes do not have a crtc
assigned, we'll try to lock all crtcs twice. Here's a stack trace:

[   14.040141] Call Trace:
[   14.040144]  [<ffffffff9669023a>] drm_modeset_lock+0x46/0x1b7
[   14.040147]  [<ffffffff96689eb6>] ? drm_err+0x6e/0x85
[   14.040149]  [<ffffffff96690425>] drm_modeset_lock_all_crtcs+0x7a/0xdf
[   14.040152]  [<ffffffff9669b6b2>] drm_atomic_helper_get_plane_state+0x34/0x99
[   14.040154]  [<ffffffff9669af9d>]
drm_atomic_helper_plane_set_property+0x30/0x56
[   14.040157]  [<ffffffff9668ee04>] drm_mode_plane_set_obj_prop+0x18/0x20
[   14.040159]  [<ffffffff9668ee34>] drm_plane_force_disable+0x28/0x46
[   14.040161]  [<ffffffff9667e1f7>] drm_fb_helper_restore_fbdev_mode+0x84/0x140
[   14.040163]  [<ffffffff966e0bea>] intel_fb_restore_mode+0x26/0x8c
[   14.040166]  [<ffffffff966a1f41>] i915_driver_lastclose+0x2c/0x3e
[   14.040169]  [<ffffffff96685399>] drm_lastclose+0x49/0x238
[   14.040171]  [<ffffffff96685d24>] drm_release+0x513/0x546
[   14.040174]  [<ffffffff964e8842>] __fput+0xfb/0x1d3
[   14.040178]  [<ffffffff964e8928>] ____fput+0xe/0x10
[   14.040180]  [<ffffffff9644bf49>] task_work_run+0x7d/0x94
[   14.040182]  [<ffffffff96401c8f>] do_notify_resume+0x57/0x5b
[   14.040184]  [<ffffffff968b5308>] int_signal+0x12/0x17


> +       if (ret)
> +               return ERR_PTR(ret);
> +
> +       pstate = a->pstates[plane->id];
> +
> +       if (!pstate) {
> +               pstate = kzalloc(sizeof(*pstate), GFP_KERNEL);
> +               if (!pstate)
> +                       return ERR_PTR(-ENOMEM);
> +               drm_atomic_helper_init_plane_state(plane, pstate, state);
> +               a->planes[plane->id] = plane;
> +               a->pstates[plane->id] = pstate;
> +       }
> +
> +       return pstate;
> +}
> +

<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);

I haven't tested this, but it seems it might be susceptible to the
same issue if the plane does not have an assigned crtc.

Sean

> +                       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;
> +}
Rob Clark March 18, 2014, 4:24 p.m. UTC | #9
On Tue, Mar 18, 2014 at 11:48 AM, Sean Paul <seanpaul@chromium.org> wrote:
> On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@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).
>> ---
>>  drivers/gpu/drm/drm_atomic_helper.c         | 156 ++++++++++-
>>  drivers/gpu/drm/drm_crtc.c                  | 397 +++++++++++++++++++---------
>>  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   |  15 +-
>>  drivers/gpu/drm/i915/intel_sprite.c         |  21 +-
>>  drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   2 +-
>>  drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |  19 +-
>>  drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   8 +-
>>  drivers/gpu/drm/omapdrm/omap_crtc.c         |   4 +-
>>  drivers/gpu/drm/omapdrm/omap_drv.c          |   2 +-
>>  drivers/gpu/drm/omapdrm/omap_plane.c        |  33 ++-
>>  drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   8 +-
>>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |   2 +-
>>  drivers/gpu/drm/shmobile/shmob_drm_plane.c  |   6 +-
>>  drivers/gpu/drm/tegra/dc.c                  |  16 +-
>>  include/drm/drm_atomic_helper.h             |  39 ++-
>>  include/drm/drm_crtc.h                      |  88 +++++-
>>  19 files changed, 654 insertions(+), 189 deletions(-)
>>
>
> <snip>
>
>> +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;
>> +       int ret;
>> +
>> +       /* grab lock of current crtc.. if crtc is NULL then grab all: */
>> +       if (plane->state->crtc)
>> +               ret = drm_modeset_lock(&plane->state->crtc->mutex, state);
>> +       else
>> +               ret = drm_modeset_lock_all_crtcs(plane->dev, state);
>
>
> Hi Rob,
> There seems to be a recursive lock in the
> drm_fb_helper_restore_fbdev_mode path. The code loops through all
> planes and does a force disable. If 2 of the planes do not have a crtc
> assigned, we'll try to lock all crtcs twice. Here's a stack trace:

so what is *supposed* to happen is that in both cases, the crtc's are
locked against the same ww_ctx.  So the second time will be a no-op.

It gets a bit tricky with the whole lock-handoff-to-worker feature
which drivers can choose to use, since when the locks are re-acquired
it is (out of necessity) with a different ww_ctx.

I did fix something around those lines with the recent updated atomic
series (rebased on primary-planes, fwiw).  And also added a WARN_ON()
to catch places which don't acquire their needed locks before the
commit phase.

I'm pretty sure this is the same thing you are hitting here

> [   14.040141] Call Trace:
> [   14.040144]  [<ffffffff9669023a>] drm_modeset_lock+0x46/0x1b7
> [   14.040147]  [<ffffffff96689eb6>] ? drm_err+0x6e/0x85
> [   14.040149]  [<ffffffff96690425>] drm_modeset_lock_all_crtcs+0x7a/0xdf
> [   14.040152]  [<ffffffff9669b6b2>] drm_atomic_helper_get_plane_state+0x34/0x99
> [   14.040154]  [<ffffffff9669af9d>]
> drm_atomic_helper_plane_set_property+0x30/0x56
> [   14.040157]  [<ffffffff9668ee04>] drm_mode_plane_set_obj_prop+0x18/0x20
> [   14.040159]  [<ffffffff9668ee34>] drm_plane_force_disable+0x28/0x46
> [   14.040161]  [<ffffffff9667e1f7>] drm_fb_helper_restore_fbdev_mode+0x84/0x140
> [   14.040163]  [<ffffffff966e0bea>] intel_fb_restore_mode+0x26/0x8c
> [   14.040166]  [<ffffffff966a1f41>] i915_driver_lastclose+0x2c/0x3e
> [   14.040169]  [<ffffffff96685399>] drm_lastclose+0x49/0x238
> [   14.040171]  [<ffffffff96685d24>] drm_release+0x513/0x546
> [   14.040174]  [<ffffffff964e8842>] __fput+0xfb/0x1d3
> [   14.040178]  [<ffffffff964e8928>] ____fput+0xe/0x10
> [   14.040180]  [<ffffffff9644bf49>] task_work_run+0x7d/0x94
> [   14.040182]  [<ffffffff96401c8f>] do_notify_resume+0x57/0x5b
> [   14.040184]  [<ffffffff968b5308>] int_signal+0x12/0x17
>
>
>> +       if (ret)
>> +               return ERR_PTR(ret);
>> +
>> +       pstate = a->pstates[plane->id];
>> +
>> +       if (!pstate) {
>> +               pstate = kzalloc(sizeof(*pstate), GFP_KERNEL);
>> +               if (!pstate)
>> +                       return ERR_PTR(-ENOMEM);
>> +               drm_atomic_helper_init_plane_state(plane, pstate, state);
>> +               a->planes[plane->id] = plane;
>> +               a->pstates[plane->id] = pstate;
>> +       }
>> +
>> +       return pstate;
>> +}
>> +
>
> <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);
>
> I haven't tested this, but it seems it might be susceptible to the
> same issue if the plane does not have an assigned crtc.

well, as long as the locks that will be needed in commit() are grabbed
prior to commit(), it should be all good.  But there was a case where
I needed to grab crtc locks earlier.

Eventually we should just have plane locks, which would reduce the
cases where we have to grab crtc locks.  But I was really trying to
not *completely* re-write kms in one go ;-)

BR,
-R

>
> Sean
>
>> +                       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;
>> +}
Sean Paul March 18, 2014, 5:33 p.m. UTC | #10
On Tue, Mar 18, 2014 at 12:24 PM, Rob Clark <robdclark@gmail.com> wrote:
> On Tue, Mar 18, 2014 at 11:48 AM, Sean Paul <seanpaul@chromium.org> wrote:
>> On Mon, Nov 25, 2013 at 9:47 AM, Rob Clark <robdclark@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).
>>> ---
>>>  drivers/gpu/drm/drm_atomic_helper.c         | 156 ++++++++++-
>>>  drivers/gpu/drm/drm_crtc.c                  | 397 +++++++++++++++++++---------
>>>  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   |  15 +-
>>>  drivers/gpu/drm/i915/intel_sprite.c         |  21 +-
>>>  drivers/gpu/drm/msm/mdp4/mdp4_crtc.c        |   2 +-
>>>  drivers/gpu/drm/msm/mdp4/mdp4_plane.c       |  19 +-
>>>  drivers/gpu/drm/nouveau/dispnv04/overlay.c  |   8 +-
>>>  drivers/gpu/drm/omapdrm/omap_crtc.c         |   4 +-
>>>  drivers/gpu/drm/omapdrm/omap_drv.c          |   2 +-
>>>  drivers/gpu/drm/omapdrm/omap_plane.c        |  33 ++-
>>>  drivers/gpu/drm/rcar-du/rcar_du_plane.c     |   8 +-
>>>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c   |   2 +-
>>>  drivers/gpu/drm/shmobile/shmob_drm_plane.c  |   6 +-
>>>  drivers/gpu/drm/tegra/dc.c                  |  16 +-
>>>  include/drm/drm_atomic_helper.h             |  39 ++-
>>>  include/drm/drm_crtc.h                      |  88 +++++-
>>>  19 files changed, 654 insertions(+), 189 deletions(-)
>>>
>>
>> <snip>
>>
>>> +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;
>>> +       int ret;
>>> +
>>> +       /* grab lock of current crtc.. if crtc is NULL then grab all: */
>>> +       if (plane->state->crtc)
>>> +               ret = drm_modeset_lock(&plane->state->crtc->mutex, state);
>>> +       else
>>> +               ret = drm_modeset_lock_all_crtcs(plane->dev, state);
>>
>>
>> Hi Rob,
>> There seems to be a recursive lock in the
>> drm_fb_helper_restore_fbdev_mode path. The code loops through all
>> planes and does a force disable. If 2 of the planes do not have a crtc
>> assigned, we'll try to lock all crtcs twice. Here's a stack trace:
>
> so what is *supposed* to happen is that in both cases, the crtc's are
> locked against the same ww_ctx.  So the second time will be a no-op.
>

Right, OK, I was slightly mistaken. It seems like what was happening
is that we were setting lock->atomic_pending the first time we grabbed
each lock and then blocking on atomic_pending in the second go-around.


> It gets a bit tricky with the whole lock-handoff-to-worker feature
> which drivers can choose to use, since when the locks are re-acquired
> it is (out of necessity) with a different ww_ctx.
>
> I did fix something around those lines with the recent updated atomic
> series (rebased on primary-planes, fwiw).  And also added a WARN_ON()
> to catch places which don't acquire their needed locks before the
> commit phase.
>

Indeed, this seems to be fixed in your cold-fusion branch since it
only acquires the locks once, when the state is allocated.

Sean



> I'm pretty sure this is the same thing you are hitting here
>
>> [   14.040141] Call Trace:
>> [   14.040144]  [<ffffffff9669023a>] drm_modeset_lock+0x46/0x1b7
>> [   14.040147]  [<ffffffff96689eb6>] ? drm_err+0x6e/0x85
>> [   14.040149]  [<ffffffff96690425>] drm_modeset_lock_all_crtcs+0x7a/0xdf
>> [   14.040152]  [<ffffffff9669b6b2>] drm_atomic_helper_get_plane_state+0x34/0x99
>> [   14.040154]  [<ffffffff9669af9d>]
>> drm_atomic_helper_plane_set_property+0x30/0x56
>> [   14.040157]  [<ffffffff9668ee04>] drm_mode_plane_set_obj_prop+0x18/0x20
>> [   14.040159]  [<ffffffff9668ee34>] drm_plane_force_disable+0x28/0x46
>> [   14.040161]  [<ffffffff9667e1f7>] drm_fb_helper_restore_fbdev_mode+0x84/0x140
>> [   14.040163]  [<ffffffff966e0bea>] intel_fb_restore_mode+0x26/0x8c
>> [   14.040166]  [<ffffffff966a1f41>] i915_driver_lastclose+0x2c/0x3e
>> [   14.040169]  [<ffffffff96685399>] drm_lastclose+0x49/0x238
>> [   14.040171]  [<ffffffff96685d24>] drm_release+0x513/0x546
>> [   14.040174]  [<ffffffff964e8842>] __fput+0xfb/0x1d3
>> [   14.040178]  [<ffffffff964e8928>] ____fput+0xe/0x10
>> [   14.040180]  [<ffffffff9644bf49>] task_work_run+0x7d/0x94
>> [   14.040182]  [<ffffffff96401c8f>] do_notify_resume+0x57/0x5b
>> [   14.040184]  [<ffffffff968b5308>] int_signal+0x12/0x17
>>
>>
>>> +       if (ret)
>>> +               return ERR_PTR(ret);
>>> +
>>> +       pstate = a->pstates[plane->id];
>>> +
>>> +       if (!pstate) {
>>> +               pstate = kzalloc(sizeof(*pstate), GFP_KERNEL);
>>> +               if (!pstate)
>>> +                       return ERR_PTR(-ENOMEM);
>>> +               drm_atomic_helper_init_plane_state(plane, pstate, state);
>>> +               a->planes[plane->id] = plane;
>>> +               a->pstates[plane->id] = pstate;
>>> +       }
>>> +
>>> +       return pstate;
>>> +}
>>> +
>>
>> <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);
>>
>> I haven't tested this, but it seems it might be susceptible to the
>> same issue if the plane does not have an assigned crtc.
>
> well, as long as the locks that will be needed in commit() are grabbed
> prior to commit(), it should be all good.  But there was a case where
> I needed to grab crtc locks earlier.
>
> Eventually we should just have plane locks, which would reduce the
> cases where we have to grab crtc locks.  But I was really trying to
> not *completely* re-write kms in one go ;-)
>
> BR,
> -R
>
>>
>> Sean
>>
>>> +                       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;
>>> +}
diff mbox

Patch

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 8b37cf1..14e0571 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);
 
@@ -57,6 +59,12 @@  void *drm_atomic_helper_begin(struct drm_device *dev, uint32_t flags)
 	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);
@@ -92,7 +100,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);
 
@@ -168,6 +188,18 @@  fail:
 static void commit_locks(struct drm_atomic_helper_state *a,
 		struct ww_acquire_ctx *ww_ctx)
 {
+	struct drm_device *dev = a->dev;
+	int nplanes = dev->mode_config.num_plane;
+	int i;
+
+	for (i = 0; i < nplanes; i++) {
+		struct drm_plane *plane = a->planes[i];
+		if (plane) {
+			plane->state->state = NULL;
+			drm_atomic_helper_destroy_plane_state(plane, a->pstates[i]);
+		}
+	}
+
 	/* and properly release them (clear in_atomic, remove from list): */
 	mutex_lock(&a->mutex);
 	while (!list_empty(&a->locked)) {
@@ -186,7 +218,17 @@  static void commit_locks(struct drm_atomic_helper_state *a,
 static int atomic_commit(struct drm_atomic_helper_state *a,
 		struct ww_acquire_ctx *ww_ctx)
 {
-	int ret = 0;
+	int nplanes = a->dev->mode_config.num_plane;
+	int i, ret = 0;
+
+	for (i = 0; i < nplanes; i++) {
+		struct drm_plane *plane = a->planes[i];
+		if (plane) {
+			ret = drm_atomic_commit_plane_state(plane, a->pstates[i]);
+			if (ret)
+				break;
+		}
+	}
 
 	commit_locks(a, ww_ctx);
 
@@ -263,7 +305,117 @@  void _drm_atomic_helper_state_free(struct kref *kref)
 }
 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)
+{
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
+	return drm_plane_set_property(plane, pstate, 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);
+
+void drm_atomic_helper_destroy_plane_state(struct drm_plane *plane,
+		struct drm_plane_state *state)
+{
+	kfree(state);
+}
+EXPORT_SYMBOL(drm_atomic_helper_destroy_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;
+	int ret;
+
+	/* grab lock of current crtc.. if crtc is NULL then grab all: */
+	if (plane->state->crtc)
+		ret = drm_modeset_lock(&plane->state->crtc->mutex, state);
+	else
+		ret = drm_modeset_lock_all_crtcs(plane->dev, state);
+	if (ret)
+		return ERR_PTR(ret);
+
+	pstate = a->pstates[plane->id];
+
+	if (!pstate) {
+		pstate = kzalloc(sizeof(*pstate), GFP_KERNEL);
+		if (!pstate)
+			return ERR_PTR(-ENOMEM);
+		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)
+{
+	struct drm_plane_state *pstate = a->pstates[plane->id];
+
+	/* clear transient state (only valid during atomic update): */
+	pstate->new_fb = false;
+
+	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_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;
+			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;
+}
 
 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 8180499..bb1a4fe 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -680,7 +680,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) {
@@ -695,9 +708,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);
 	}
 
@@ -993,8 +1015,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);
@@ -1002,7 +1028,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,
@@ -1024,11 +1050,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);
 
@@ -1060,6 +1098,121 @@  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);
+		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);
+
 /**
  * drm_plane_force_disable - Forcibly disable a plane
  * @plane: plane to disable
@@ -1069,20 +1222,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);
 
@@ -1132,21 +1280,78 @@  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;
+
+
+	prop = drm_property_create_range(dev, 0, "SRC_X", 0, UINT_MAX);
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.prop_src_x = prop;
+
+	prop = drm_property_create_range(dev, 0, "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_signed_range(dev, 0, "CRTC_X",
+			INT_MIN, INT_MAX);
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.prop_crtc_x = prop;
+
+	prop = drm_property_create_signed_range(dev, 0, "CRTC_Y",
+			INT_MIN, 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, 0, "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;
 }
@@ -1939,13 +2144,13 @@  int drm_mode_getplane(struct drm_device *dev, void *data,
 		goto out;
 	}
 
-	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;
 
@@ -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;
 }
 
@@ -3427,7 +3570,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)
 {
@@ -3450,8 +3593,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)
 {
@@ -3466,8 +3610,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)
 {
@@ -3476,12 +3621,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 1b6b6f4..5773468 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -286,12 +286,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..d585a4c 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);
@@ -225,13 +226,17 @@  static int exynos_plane_set_property(struct drm_plane *plane,
 	struct drm_device *dev = plane->dev;
 	struct exynos_plane *exynos_plane = to_exynos_plane(plane);
 	struct exynos_drm_private *dev_priv = dev->dev_private;
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
+
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
 
 	if (property == dev_priv->plane_zpos_property) {
 		exynos_plane->overlay.zpos = val;
 		return 0;
 	}
 
-	return -EINVAL;
+	return drm_plane_set_property(plane, pstate, property, val, blob_data);
 }
 
 static struct drm_plane_funcs exynos_plane_funcs = {
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index b9fabf8..0d1d34a 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -897,20 +897,21 @@  static int
 intel_disable_plane(struct drm_plane *plane)
 {
 	struct drm_device *dev = plane->dev;
+	struct drm_plane_state *state = plane->state;
 	struct intel_plane *intel_plane = to_intel_plane(plane);
 	struct intel_crtc *intel_crtc;
 
-	if (!plane->fb)
+	if (!state->fb)
 		return 0;
 
-	if (WARN_ON(!plane->crtc))
+	if (WARN_ON(!state->crtc))
 		return -EINVAL;
 
-	intel_crtc = to_intel_crtc(plane->crtc);
+	intel_crtc = to_intel_crtc(state->crtc);
 
 	if (intel_crtc->active) {
-		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) {
@@ -1000,11 +1001,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,
@@ -1013,7 +1015,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);
@@ -1023,6 +1027,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 80af12a..37a3fd2 100644
--- a/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/mdp4/mdp4_crtc.c
@@ -263,7 +263,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..037ac83 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,10 @@  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;
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
+	return drm_plane_set_property(plane, pstate, property, val, blob_data);
 }
 
 static const struct drm_plane_funcs mdp4_plane_funcs = {
@@ -117,7 +122,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/nouveau/dispnv04/overlay.c b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
index fc938f7..a6a4375 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/overlay.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/overlay.c
@@ -24,6 +24,7 @@ 
  */
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_fourcc.h>
 
@@ -213,6 +214,10 @@  nv10_set_property(struct drm_plane *plane, void *state,
 		  uint64_t value, void *blob_data)
 {
 	struct nouveau_plane *nv_plane = (struct nouveau_plane *)plane;
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
+
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
 
 	if (property == nv_plane->props.colorkey)
 		nv_plane->colorkey = value;
@@ -227,7 +232,8 @@  nv10_set_property(struct drm_plane *plane, void *state,
 	else if (property == nv_plane->props.iturbt_709)
 		nv_plane->iturbt_709 = value;
 	else
-		return -EINVAL;
+		return drm_plane_set_property(plane, pstate,
+				property, value, blob_data);
 
 	nv10_set_params(nv_plane);
 	return 0;
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
index 357342d..524a81a 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 1b09696..e1e794a 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..b85bd61 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);
 
@@ -332,8 +338,12 @@  int omap_plane_set_property(struct drm_plane *plane, void *state,
 {
 	struct omap_plane *omap_plane = to_omap_plane(plane);
 	struct omap_drm_private *priv = plane->dev->dev_private;
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
 	int ret = -EINVAL;
 
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
+
 	if (property == priv->rotation_prop) {
 		DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val);
 		omap_plane->win.rotation = val;
@@ -342,6 +352,9 @@  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, pstate, 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..4121a58 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>
 
@@ -403,6 +404,10 @@  static int rcar_du_plane_set_property(struct drm_plane *plane,
 {
 	struct rcar_du_plane *rplane = to_rcar_plane(plane);
 	struct rcar_du_group *rgrp = rplane->group;
+	struct drm_plane_state *pstate = drm_atomic_get_plane_state(plane, state);
+
+	if (IS_ERR(pstate))
+		return PTR_ERR(pstate);
 
 	if (property == rgrp->planes.alpha)
 		rcar_du_plane_set_alpha(rplane, value);
@@ -411,7 +416,8 @@  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, pstate,
+				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 b72ba99..9e86b99 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/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index ae1cb31..1794c6e 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -73,11 +73,12 @@  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 drm_plane_state *state = plane->state;
+	struct tegra_dc *dc = to_tegra_dc(state->crtc);
 	struct tegra_plane *p = to_tegra_plane(plane);
 	unsigned long value;
 
-	if (!plane->crtc)
+	if (!state->crtc)
 		return 0;
 
 	value = WINDOW_A_SELECT << p->index;
@@ -309,13 +310,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 d77f00e..4ca8360 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;
@@ -76,6 +78,39 @@  int drm_atomic_helper_commit(struct drm_device *dev, void *state);
 int drm_atomic_helper_commit_unlocked(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);
+void drm_atomic_helper_destroy_plane_state(struct drm_plane *plane,
+		struct drm_plane_state *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
  */
@@ -83,6 +118,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;
 
 	bool committed;
 
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 40eec19..9a4c16e 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -753,6 +753,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
@@ -760,8 +799,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
  */
@@ -775,13 +814,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;
 };
 
 /**
@@ -962,8 +1005,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;
 
@@ -1043,7 +1098,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);
 
@@ -1115,6 +1178,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);