On Sat, May 24, 2014 at 02:30:21PM -0400, Rob Clark wrote:
> Break the mutable state of a crtc out into a separate structure
> and use atomic properties mechanism to set crtc attributes.  This
> makes it easier to have some helpers for crtc->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.
> 
> Signed-off-by: Rob Clark <robdclark at gmail.com>

Same comments about interface design as for the plane patch apply here.
One additional comment below.

> ---
>  drivers/gpu/drm/armada/armada_crtc.c       |  11 +-
>  drivers/gpu/drm/ast/ast_mode.c             |   1 +
>  drivers/gpu/drm/cirrus/cirrus_mode.c       |   1 +
>  drivers/gpu/drm/drm_atomic.c               | 231 ++++++++++-
>  drivers/gpu/drm/drm_crtc.c                 | 598 
> ++++++++++++++++++-----------
>  drivers/gpu/drm/drm_fb_helper.c            |   2 +-
>  drivers/gpu/drm/exynos/exynos_drm_crtc.c   |   7 +-
>  drivers/gpu/drm/gma500/cdv_intel_display.c |   1 +
>  drivers/gpu/drm/gma500/psb_intel_display.c |   1 +
>  drivers/gpu/drm/i915/intel_display.c       |   1 +
>  drivers/gpu/drm/mgag200/mgag200_mode.c     |   1 +
>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c   |   6 +-
>  drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c   |   6 +-
>  drivers/gpu/drm/nouveau/dispnv04/crtc.c    |   1 +
>  drivers/gpu/drm/nouveau/nv50_display.c     |   1 +
>  drivers/gpu/drm/omapdrm/omap_crtc.c        |  12 +-
>  drivers/gpu/drm/omapdrm/omap_drv.c         |   2 +-
>  drivers/gpu/drm/qxl/qxl_display.c          |   2 +
>  drivers/gpu/drm/radeon/radeon_display.c    |   2 +
>  drivers/gpu/drm/rcar-du/rcar_du_crtc.c     |   2 +
>  drivers/gpu/drm/shmobile/shmob_drm_crtc.c  |   2 +
>  drivers/gpu/drm/tilcdc/tilcdc_crtc.c       |   1 +
>  drivers/gpu/drm/udl/udl_modeset.c          |   2 +
>  drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c        |   1 +
>  drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c       |   1 +
>  include/drm/drm_atomic.h                   |  29 ++
>  include/drm/drm_crtc.h                     | 103 ++++-
>  27 files changed, 785 insertions(+), 243 deletions(-)
> 
> diff --git a/drivers/gpu/drm/armada/armada_crtc.c 
> b/drivers/gpu/drm/armada/armada_crtc.c
> index 7d3c649..6237af4 100644
> --- a/drivers/gpu/drm/armada/armada_crtc.c
> +++ b/drivers/gpu/drm/armada/armada_crtc.c
> @@ -9,6 +9,7 @@
>  #include <linux/clk.h>
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic.h>
>  #include "armada_crtc.h"
>  #include "armada_drm.h"
>  #include "armada_fb.h"
> @@ -966,7 +967,12 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc,
>  {
>       struct armada_private *priv = crtc->dev->dev_private;
>       struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
> +     struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
>       bool update_csc = false;
> +     int ret = 0;
> +
> +     if (IS_ERR(cstate))
> +             return PTR_ERR(cstate);
>  
>       if (property == priv->csc_yuv_prop) {
>               dcrtc->csc_yuv_mode = val;
> @@ -974,6 +980,9 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc,
>       } else if (property == priv->csc_rgb_prop) {
>               dcrtc->csc_rgb_mode = val;
>               update_csc = true;
> +     } else {
> +             ret = drm_crtc_set_property(crtc, cstate, property,
> +                             val, blob_data);
>       }
>  
>       if (update_csc) {
> @@ -984,7 +993,7 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc,
>               writel_relaxed(val, dcrtc->base + LCD_SPU_IOPAD_CONTROL);
>       }
>  
> -     return 0;
> +     return ret;
>  }
>  
>  static struct drm_crtc_funcs armada_crtc_funcs = {
> diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c
> index 114aee9..c08e0e1 100644
> --- a/drivers/gpu/drm/ast/ast_mode.c
> +++ b/drivers/gpu/drm/ast/ast_mode.c
> @@ -632,6 +632,7 @@ static const struct drm_crtc_funcs ast_crtc_funcs = {
>       .cursor_move = ast_cursor_move,
>       .reset = ast_crtc_reset,
>       .set_config = drm_crtc_helper_set_config,
> +     .set_property = drm_atomic_crtc_set_property,
>       .gamma_set = ast_crtc_gamma_set,
>       .destroy = ast_crtc_destroy,
>  };
> diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c 
> b/drivers/gpu/drm/cirrus/cirrus_mode.c
> index 49332c5..3c4428c 100644
> --- a/drivers/gpu/drm/cirrus/cirrus_mode.c
> +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c
> @@ -366,6 +366,7 @@ static void cirrus_crtc_destroy(struct drm_crtc *crtc)
>  static const struct drm_crtc_funcs cirrus_crtc_funcs = {
>       .gamma_set = cirrus_crtc_gamma_set,
>       .set_config = drm_crtc_helper_set_config,
> +     .set_property = drm_atomic_crtc_set_property,
>       .destroy = cirrus_crtc_destroy,
>  };
>  
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index 403ffc5..863a0fe 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -48,11 +48,13 @@ struct drm_atomic_state *drm_atomic_begin(struct 
> drm_device *dev,
>  {
>       struct drm_atomic_state *state;
>       int nplanes = dev->mode_config.num_total_plane;
> +     int ncrtcs  = dev->mode_config.num_crtc;
>       int sz;
>       void *ptr;
>  
>       sz = sizeof(*state);
>       sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes;
> +     sz += (sizeof(state->crtcs) + sizeof(state->cstates)) * ncrtcs;
>  
>       ptr = kzalloc(sz, GFP_KERNEL);
>  
> @@ -74,6 +76,12 @@ struct drm_atomic_state *drm_atomic_begin(struct 
> drm_device *dev,
>       state->pstates = ptr;
>       ptr = &state->pstates[nplanes];
>  
> +     state->crtcs = ptr;
> +     ptr = &state->crtcs[ncrtcs];
> +
> +     state->cstates = ptr;
> +     ptr = &state->cstates[ncrtcs];
> +
>       return state;
>  }
>  EXPORT_SYMBOL(drm_atomic_begin);
> @@ -92,7 +100,18 @@ int drm_atomic_set_event(struct drm_device *dev,
>               struct drm_atomic_state *state, struct drm_mode_object *obj,
>               struct drm_pending_vblank_event *event)
>  {
> -     return -EINVAL;  /* for now */
> +     switch (obj->type) {
> +     case DRM_MODE_OBJECT_CRTC: {
> +             struct drm_crtc_state *cstate =
> +                     drm_atomic_get_crtc_state(obj_to_crtc(obj), state);
> +             if (IS_ERR(cstate))
> +                     return PTR_ERR(cstate);
> +             cstate->event = event;
> +             return 0;
> +     }
> +     default:
> +             return -EINVAL;
> +     }

Hm, I think if we only want completion events on crtcs (which I agree on)
then we should make the set_event interface more specific by passing
struct drm_crtc * and only call it for crtcs.

>  }
>  EXPORT_SYMBOL(drm_atomic_set_event);
>  
> @@ -111,6 +130,7 @@ int drm_atomic_check(struct drm_device *dev, struct 
> drm_atomic_state *state)
>  {
>       struct drm_atomic_state *a = state;
>       int nplanes = dev->mode_config.num_total_plane;
> +     int ncrtcs = dev->mode_config.num_crtc;
>       int i, ret = 0;
>  
>       for (i = 0; i < nplanes; i++) {
> @@ -120,6 +140,13 @@ int drm_atomic_check(struct drm_device *dev, struct 
> drm_atomic_state *state)
>                               break;
>               }
>       }
> +     for (i = 0; i < ncrtcs; i++) {
> +             if (a->crtcs[i]) {
> +                     ret = drm_atomic_check_crtc_state(a->crtcs[i], 
> a->cstates[i]);
> +                     if (ret)
> +                             break;
> +             }
> +     }
>  
>       a->acquire_ctx.frozen = true;
>  
> @@ -203,6 +230,7 @@ static void commit_locks(struct drm_atomic_state *a,
>  {
>       struct drm_device *dev = a->dev;
>       int nplanes = dev->mode_config.num_total_plane;
> +     int ncrtcs = dev->mode_config.num_crtc;
>       int i;
>  
>       for (i = 0; i < nplanes; i++) {
> @@ -213,6 +241,14 @@ static void commit_locks(struct drm_atomic_state *a,
>               }
>       }
>  
> +     for (i = 0; i < ncrtcs; i++) {
> +             struct drm_crtc *crtc = a->crtcs[i];
> +             if (crtc) {
> +                     crtc->state->state = NULL;
> +                     drm_crtc_destroy_state(crtc, a->cstates[i]);
> +             }
> +     }
> +
>       /* and properly release them (clear in_atomic, remove from list): */
>       drm_modeset_drop_locks(&a->acquire_ctx);
>       ww_acquire_fini(ww_ctx);
> @@ -223,8 +259,18 @@ static int atomic_commit(struct drm_atomic_state *a,
>               struct ww_acquire_ctx *ww_ctx)
>  {
>       int nplanes = a->dev->mode_config.num_total_plane;
> +     int ncrtcs = a->dev->mode_config.num_crtc;
>       int i, ret = 0;
>  
> +     for (i = 0; i < ncrtcs; i++) {
> +             struct drm_crtc *crtc = a->crtcs[i];
> +             if (crtc) {
> +                     ret = drm_atomic_commit_crtc_state(crtc, a->cstates[i]);
> +                     if (ret)
> +                             break;
> +             }
> +     }
> +
>       for (i = 0; i < nplanes; i++) {
>               struct drm_plane *plane = a->planes[i];
>               if (plane) {
> @@ -403,6 +449,7 @@ static int
>  commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
>  {
>       struct drm_atomic_state *a = pstate->state;
> +     struct drm_crtc_state *cstate = NULL;
>       struct drm_framebuffer *old_fb = plane->fb;
>       struct drm_framebuffer *fb = pstate->fb;
>       bool enabled = pstate->crtc && fb;
> @@ -425,8 +472,11 @@ commit_plane_state(struct drm_plane *plane, struct 
> drm_plane_state *pstate)
>               }
>       } else {
>               struct drm_crtc *crtc = pstate->crtc;
> +             cstate = drm_atomic_get_crtc_state(crtc, pstate->state);
>               if (pstate->update_plane ||
>                               (pstate->new_fb && !can_flip(plane, pstate))) {
> +/* TODO pass event to update_plane().. */
> +WARN_ON(cstate->event);
>                       ret = plane->funcs->update_plane(plane, crtc, 
> pstate->fb,
>                                       pstate->crtc_x, pstate->crtc_y,
>                                       pstate->crtc_w, pstate->crtc_h,
> @@ -443,7 +493,7 @@ commit_plane_state(struct drm_plane *plane, struct 
> drm_plane_state *pstate)
>                       }
>  
>               } else if (pstate->new_fb) {
> -                     ret = crtc->funcs->page_flip(crtc, fb, NULL, a->flags);
> +                     ret = crtc->funcs->page_flip(crtc, fb, cstate->event, 
> a->flags);
>                       if (ret == 0) {
>                               /*
>                                * Warn if the driver hasn't properly updated 
> the plane->fb
> @@ -473,9 +523,10 @@ commit_plane_state(struct drm_plane *plane, struct 
> drm_plane_state *pstate)
>                * original code.
>                */
>               swap_plane_state(plane, pstate->state);
> +             if (cstate)
> +                     cstate->event = NULL;
>       }
>  
> -
>       if (fb)
>               drm_framebuffer_unreference(fb);
>       if (old_fb)
> @@ -484,8 +535,182 @@ commit_plane_state(struct drm_plane *plane, struct 
> drm_plane_state *pstate)
>       return ret;
>  }
>  
> +int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
> +             struct drm_atomic_state *state, struct drm_property *property,
> +             uint64_t val, void *blob_data)
> +{
> +     struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
> +     if (IS_ERR(cstate))
> +             return PTR_ERR(cstate);
> +     return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
> +}
> +EXPORT_SYMBOL(drm_atomic_crtc_set_property);
> +
> +static void init_crtc_state(struct drm_crtc *crtc,
> +             struct drm_crtc_state *cstate, struct drm_atomic_state *state)
> +{
> +     /* snapshot current state: */
> +     *cstate = *crtc->state;
> +     cstate->state = state;
> +
> +     if (cstate->connector_ids) {
> +             int sz = cstate->num_connector_ids * 
> sizeof(cstate->connector_ids[0]);
> +             cstate->connector_ids = kmemdup(cstate->connector_ids, sz, 
> GFP_KERNEL);
> +     }
> +
> +     /* this should never happen.. but make sure! */
> +     WARN_ON(cstate->event);
> +     cstate->event = NULL;
> +}
> +
> +struct drm_crtc_state *
> +drm_atomic_get_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a)
> +{
> +     struct drm_crtc_state *cstate;
> +     int ret;
> +
> +     cstate = a->cstates[crtc->id];
> +
> +     if (!cstate) {
> +             ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx);
> +             if (ret)
> +                     return ERR_PTR(ret);
> +
> +             cstate = drm_crtc_create_state(crtc);
> +             if (!cstate)
> +                     return ERR_PTR(-ENOMEM);
> +             init_crtc_state(crtc, cstate, a);
> +             a->crtcs[crtc->id] = crtc;
> +             a->cstates[crtc->id] = cstate;
> +
> +             /* we'll need it later, so make sure we have state
> +              * for primary plane too:
> +              */
> +             drm_atomic_get_plane_state(crtc->primary, a);

I haven't figured out why. With primary planes I don't really see a need
for this. If we need it to implement the legacy setcrtc interface, then
that should be done there, not here.

> +     }
> +     return cstate;
> +}
> +EXPORT_SYMBOL(drm_atomic_get_crtc_state);
> +
> +static void
> +swap_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a)
> +{
> +     struct drm_crtc_state *cstate = a->cstates[crtc->id];
> +     struct drm_device *dev = crtc->dev;
> +     struct drm_pending_vblank_event *event = cstate->event;
> +
> +     if (event) {
> +             /* hrm, need to sort out a better way to send events for
> +              * other-than-pageflip.. but modeset is not async, so:
> +              */
> +             unsigned long flags;
> +             spin_lock_irqsave(&dev->event_lock, flags);
> +             drm_send_vblank_event(dev, crtc->id, event);
> +             cstate->event = NULL;
> +             spin_unlock_irqrestore(&dev->event_lock, flags);
> +     }
> +
> +     /* clear transient state (only valid during atomic update): */
> +     cstate->set_config = false;
> +     cstate->connectors_change = false;
> +
> +     swap(crtc->state, a->cstates[crtc->id]);
> +     crtc->base.propvals = &crtc->state->propvals;
> +}
> +
> +static struct drm_connector **get_connector_set(struct drm_device *dev,
> +             uint32_t *connector_ids, uint32_t num_connector_ids)
> +{
> +     struct drm_connector **connector_set = NULL;
> +     int i;
> +
> +     connector_set = kmalloc(num_connector_ids *
> +                     sizeof(struct drm_connector *),
> +                     GFP_KERNEL);
> +     if (!connector_set)
> +             return NULL;
> +
> +     for (i = 0; i < num_connector_ids; i++)
> +             connector_set[i] = drm_connector_find(dev, connector_ids[i]);
> +
> +     return connector_set;
> +}
> +
> +static int set_config(struct drm_crtc *crtc, struct drm_crtc_state *cstate)
> +{
> +     struct drm_device *dev = crtc->dev;
> +     struct drm_plane_state *pstate =
> +                     drm_atomic_get_plane_state(crtc->primary, 
> cstate->state);
> +     struct drm_framebuffer *fb = pstate->fb;
> +     struct drm_connector **connector_set = get_connector_set(crtc->dev,
> +                     cstate->connector_ids, cstate->num_connector_ids);
> +     struct drm_display_mode *mode = drm_crtc_get_mode(crtc, cstate);
> +     struct drm_mode_set set = {
> +                     .crtc = crtc,
> +                     .x = pstate->src_x >> 16,
> +                     .y = pstate->src_y >> 16,
> +                     .mode = mode,
> +                     .num_connectors = cstate->num_connector_ids,
> +                     .connectors = connector_set,
> +                     .fb = fb,
> +     };
> +     int ret;
> +
> +     if (IS_ERR(mode)) {
> +             ret = PTR_ERR(mode);
> +             return ret;
> +     }
> +
> +     if (fb)
> +             drm_framebuffer_reference(fb);
> +
> +     ret = drm_mode_set_config_internal(&set);
> +     if (!ret) {
> +             swap_crtc_state(crtc, cstate->state);
> +             pstate->new_fb = pstate->update_plane = false;
> +     }
> +
> +     if (fb)
> +             drm_framebuffer_unreference(fb);
> +
> +     kfree(connector_set);
> +     if (mode)
> +             drm_mode_destroy(dev, mode);
> +     return ret;
> +}
> +
> +static int
> +commit_crtc_state(struct drm_crtc *crtc,
> +             struct drm_crtc_state *cstate)
> +{
> +     struct drm_plane_state *pstate =
> +                     drm_atomic_get_plane_state(crtc->primary, 
> cstate->state);
> +     int ret = -EINVAL;
> +
> +     if (cstate->set_config)
> +             return set_config(crtc, cstate);
> +
> +     if (!pstate->fb) {
> +             /* disable */
> +             struct drm_mode_set set = {
> +                             .crtc = crtc,
> +                             .fb = NULL,
> +             };
> +
> +             ret = drm_mode_set_config_internal(&set);
> +             if (!ret) {
> +                     swap_crtc_state(crtc, cstate->state);
> +             }
> +     }
> +
> +     return ret;
> +}
> +
>  const struct drm_atomic_funcs drm_atomic_funcs = {
>               .check_plane_state  = drm_plane_check_state,
>               .commit_plane_state = commit_plane_state,
> +
> +             .check_crtc_state   = drm_crtc_check_state,
> +             .commit_crtc_state  = commit_crtc_state,
>  };
>  EXPORT_SYMBOL(drm_atomic_funcs);
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index b556a31..e14d517 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -689,10 +689,7 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup);
>  void drm_framebuffer_remove(struct drm_framebuffer *fb)
>  {
>       struct drm_device *dev = fb->dev;
> -     struct drm_crtc *crtc;
>       struct drm_plane *plane;
> -     struct drm_mode_set set;
> -     int ret;
>  
>       WARN_ON(!list_empty(&fb->filp_head));
>  
> @@ -712,7 +709,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
>        * in this manner.
>        */
>       if (atomic_read(&fb->refcount.refcount) > 1) {
> -             void *state;
> +             struct drm_atomic_state *state;
>  
>               state = dev->driver->atomic_begin(dev, 0);
>               if (IS_ERR(state)) {
> @@ -720,24 +717,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
>                       return;
>               }
>  
> -             /* TODO once CRTC is converted to state/properties, we can push 
> the
> -              * locking down into drm_atomic_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->primary->fb == fb) {
> -                             /* should turn off the crtc */
> -                             memset(&set, 0, sizeof(struct drm_mode_set));
> -                             set.crtc = crtc;
> -                             set.fb = NULL;
> -                             ret = drm_mode_set_config_internal(&set);
> -                             if (ret)
> -                                     DRM_ERROR("failed to reset crtc %p when 
> fb was deleted\n", crtc);
> -                     }
> -             }
> -
> +             /* remove from any plane */
>               list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
>                       if (plane->fb == fb)
>                               drm_plane_force_disable(plane, state);
> @@ -750,8 +730,6 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
>                       dev->driver->atomic_commit(dev, state);
>  
>               dev->driver->atomic_end(dev, state);
> -
> -             drm_modeset_unlock_all(dev);
>       }
>  
>       drm_framebuffer_unreference(fb);
> @@ -782,9 +760,13 @@ int drm_crtc_init_with_planes(struct drm_device *dev, 
> struct drm_crtc *crtc,
>       struct drm_mode_config *config = &dev->mode_config;
>       int ret;
>  
> +     /* this is now required: */
> +     WARN_ON(!funcs->set_property);
> +
>       crtc->dev = dev;
>       crtc->funcs = funcs;
> -     crtc->invert_dimensions = false;
> +     crtc->state = drm_crtc_create_state(crtc);
> +     crtc->state->invert_dimensions = false;
>  
>       drm_modeset_lock_all(dev);
>       drm_modeset_lock_init(&crtc->mutex);
> @@ -796,14 +778,17 @@ int drm_crtc_init_with_planes(struct drm_device *dev, 
> struct drm_crtc *crtc,
>               goto out;
>  
>       crtc->base.properties = &crtc->properties;
> -     crtc->base.propvals = &crtc->propvals;
> +     crtc->base.propvals = &crtc->state->propvals;
>  
>       list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
>       dev->mode_config.num_crtc++;
>  
>       crtc->primary = primary;
>       if (primary)
> -             primary->possible_crtcs = 1 << drm_crtc_index(crtc);
> +             primary->possible_crtcs = 1 << crtc->id;
> +
> +     drm_object_attach_property(&crtc->base, config->prop_mode, 0);
> +     drm_object_attach_property(&crtc->base, config->prop_connector_ids, 0);
>  
>   out:
>       drm_modeset_unlock_all(dev);
> @@ -832,31 +817,245 @@ void drm_crtc_cleanup(struct drm_crtc *crtc)
>       drm_mode_object_put(dev, &crtc->base);
>       list_del(&crtc->head);
>       dev->mode_config.num_crtc--;
> +
> +     drm_crtc_destroy_state(crtc, crtc->state);
>  }
>  EXPORT_SYMBOL(drm_crtc_cleanup);
>  
> -/**
> - * drm_crtc_index - find the index of a registered CRTC
> - * @crtc: CRTC to find index for
> - *
> - * Given a registered CRTC, return the index of that CRTC within a DRM
> - * device's list of CRTCs.
> - */
> -unsigned int drm_crtc_index(struct drm_crtc *crtc)
> +/* get display-mode from user-mode */
> +struct drm_display_mode *drm_crtc_get_mode(struct drm_crtc *crtc,
> +             struct drm_crtc_state *cstate)
>  {
> -     unsigned int index = 0;
> -     struct drm_crtc *tmp;
> +     struct drm_display_mode *mode = NULL;
> +     if (cstate->mode_valid) {
> +             struct drm_device *dev = crtc->dev;
> +             int ret;
>  
> -     list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
> -             if (tmp == crtc)
> -                     return index;
> +             mode = drm_mode_create(dev);
> +             if (!mode)
> +                     return ERR_PTR(-ENOMEM);
> +
> +             ret = drm_crtc_convert_umode(mode, &cstate->mode);
> +             if (ret) {
> +                     DRM_DEBUG_KMS("Invalid mode\n");
> +                     drm_mode_destroy(dev, mode);
> +                     return ERR_PTR(ret);
> +             }
>  
> -             index++;
> +             drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
>       }
> +     return mode;
> +}
>  
> -     BUG();
> +static int connector_idx(struct drm_crtc_state *state,
> +             uint32_t connector_id)
> +{
> +     int i;
> +     for (i = 0; i < state->num_connector_ids; i++)
> +             if (state->connector_ids[i] == connector_id)
> +                     return i;
> +     return -1;
> +}
> +
> +static int remove_connector(struct drm_crtc *ocrtc,
> +             struct drm_crtc_state *ostate, struct drm_atomic_state *state,
> +             int idx)
> +{
> +     struct drm_mode_config *config = &ocrtc->dev->mode_config;
> +     uint32_t *new_connector_ids;
> +     int a, b;
> +
> +     /* before deletion point: */
> +     a = idx * sizeof(ostate->connector_ids[0]);
> +
> +     /* after deletion point: */
> +     b = (ostate->num_connector_ids - 1 - idx) *
> +                     sizeof(ostate->connector_ids[0]);
> +
> +     new_connector_ids = kmalloc(a+b, GFP_KERNEL);
> +     if (!new_connector_ids)
> +             return -ENOMEM;
> +
> +     memcpy(new_connector_ids, ostate->connector_ids, a);
> +     memcpy(&new_connector_ids[idx],
> +                     &ostate->connector_ids[idx + 1], b);
> +
> +     return drm_mode_crtc_set_obj_prop(ocrtc, state,
> +             config->prop_connector_ids, a + b,
> +             new_connector_ids);
>  }
> -EXPORT_SYMBOL(drm_crtc_index);
> +
> +static int check_connectors(struct drm_crtc *crtc,
> +             struct drm_atomic_state *state, bool fix,
> +             uint32_t *connector_ids, uint32_t num_connector_ids)
> +{
> +     struct drm_mode_config *config = &crtc->dev->mode_config;
> +     struct drm_crtc *ocrtc; /* other connector */
> +
> +     list_for_each_entry(ocrtc, &config->crtc_list, head) {
> +             struct drm_crtc_state *ostate; /* other state */
> +             unsigned i;
> +
> +             if (ocrtc == crtc)
> +                     continue;
> +
> +             ostate = drm_atomic_get_crtc_state(crtc, state);
> +             if (IS_ERR(ostate))
> +                     return PTR_ERR(ostate);
> +
> +             for (i = 0; i < num_connector_ids; i++) {
> +                     struct drm_connector *connector;
> +                     uint32_t cid = connector_ids[i];
> +                     int idx;
> +
> +retry:
> +                     idx = connector_idx(ostate, cid);
> +                     if (idx < 0)
> +                             continue;
> +
> +                     if (fix) {
> +                             int ret = remove_connector(ocrtc,
> +                                             ostate, state, idx);
> +                             if (ret)
> +                                     return ret;
> +                             goto retry;
> +                     }
> +
> +                     connector = drm_connector_find(crtc->dev, cid);
> +                     DRM_DEBUG_KMS("[CONNECTOR:%d:%s] already in use\n",
> +                                     connector->base.id,
> +                                     drm_get_connector_name(connector));
> +                     return -EINVAL;
> +             }
> +     }
> +
> +     return 0;
> +}
> +
> +int drm_crtc_check_state(struct drm_crtc *crtc,
> +             struct drm_crtc_state *state)
> +{
> +     struct drm_plane *primary = crtc->primary;
> +     struct drm_plane_state *pstate =
> +                     drm_atomic_get_plane_state(primary, state->state);
> +     struct drm_framebuffer *fb = pstate->fb;
> +     int hdisplay, vdisplay;
> +     struct drm_display_mode *mode = drm_crtc_get_mode(crtc, state);
> +     unsigned x, y;
> +
> +     if (IS_ERR(mode))
> +             return PTR_ERR(mode);
> +
> +     /* disabling the crtc is allowed: */
> +     if (!(fb && state->mode_valid))
> +             return 0;
> +
> +     hdisplay = state->mode.hdisplay;
> +     vdisplay = state->mode.vdisplay;
> +
> +     if (mode && drm_mode_is_stereo(mode)) {
> +             struct drm_display_mode adjusted = *mode;
> +
> +             drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE);
> +             hdisplay = adjusted.crtc_hdisplay;
> +             vdisplay = adjusted.crtc_vdisplay;
> +     }
> +
> +     if (state->invert_dimensions)
> +             swap(hdisplay, vdisplay);
> +
> +     x = pstate->src_x >> 16;
> +     y = pstate->src_y >> 16;
> +
> +     if (hdisplay > fb->width ||
> +         vdisplay > fb->height ||
> +         x > fb->width - hdisplay ||
> +         y > fb->height - vdisplay) {
> +             DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport 
> %ux%u+%d+%d%s.\n",
> +                           fb->width, fb->height, hdisplay, vdisplay,
> +                           x, y, state->invert_dimensions ? " (inverted)" : 
> "");
> +             return -ENOSPC;
> +     }
> +
> +     if (crtc->enabled && !state->set_config) {
> +             if (primary->state->fb->pixel_format != fb->pixel_format) {
> +                     DRM_DEBUG_KMS("Page flip is not allowed to "
> +                                     "change frame buffer format.\n");
> +                     return -EINVAL;
> +             }
> +     }
> +
> +     if (state->num_connector_ids == 0) {
> +             DRM_DEBUG_KMS("Count connectors is 0 but mode set\n");
> +             return -EINVAL;
> +     }
> +
> +     if (state->connectors_change) {
> +             int ret = check_connectors(crtc, state->state, false,
> +                             state->connector_ids, state->num_connector_ids);
> +             if (ret)
> +                     return ret;
> +     }
> +
> +     if (mode)
> +             drm_mode_destroy(crtc->dev, mode);
> +
> +     return 0;
> +}
> +EXPORT_SYMBOL(drm_crtc_check_state);
> +
> +void drm_crtc_commit_state(struct drm_crtc *crtc,
> +             struct drm_crtc_state *state)
> +{
> +     crtc->state = state;
> +     crtc->base.propvals = &state->propvals;
> +}
> +EXPORT_SYMBOL(drm_crtc_commit_state);
> +
> +int drm_crtc_set_property(struct drm_crtc *crtc,
> +             struct drm_crtc_state *state,
> +             struct drm_property *property,
> +             uint64_t value, void *blob_data)
> +{
> +     struct drm_device *dev = crtc->dev;
> +     struct drm_mode_config *config = &dev->mode_config;
> +
> +     /* grab primary plane state now, to ensure locks are held, etc. */
> +     drm_atomic_get_plane_state(crtc->primary, state->state);
> +
> +     drm_object_property_set_value(&crtc->base,
> +                     &state->propvals, property, value, blob_data);
> +
> +     if (property == config->prop_mode) {
> +             if (!blob_data) {
> +                     memset(&state->mode, 0, sizeof(state->mode));
> +                     state->mode_valid = false;
> +             } else {
> +                     /* check size: */
> +                     if (value < sizeof(struct drm_mode_modeinfo))
> +                             return -EINVAL;
> +                     state->mode = *(struct drm_mode_modeinfo *)blob_data;
> +                     state->mode_valid = true;
> +             }
> +             state->set_config = true;
> +     } else if (property == config->prop_connector_ids) {
> +             /* if connector-id's changing, we need to have all the locks: */
> +             struct drm_atomic_state *a = state->state;
> +             int ret = drm_modeset_lock_all_crtcs(crtc->dev, 
> &a->acquire_ctx);
> +             if (ret)
> +                     return ret;
> +             state->connectors_change = true;
> +             state->num_connector_ids = value / 
> sizeof(state->connector_ids[0]);
> +             kfree(state->connector_ids);
> +             state->connector_ids = blob_data;
> +             state->set_config = true;
> +     } else {
> +             return -EINVAL;
> +     }
> +
> +     return 0;
> +}
> +EXPORT_SYMBOL(drm_crtc_set_property);
>  
>  /*
>   * drm_mode_remove - remove and free a mode
> @@ -1239,6 +1438,10 @@ int drm_plane_check_state(struct drm_plane *plane,
>       if (!fb)
>               return 0;
>  
> +     /* we'll need this later during commit: */
> +     if (state->crtc)
> +             drm_atomic_get_crtc_state(state->crtc, state->state);
> +
>       fb_width = fb->width << 16;
>       fb_height = fb->height << 16;
>  
> @@ -1465,6 +1668,16 @@ static int 
> drm_mode_create_standard_connector_properties(struct drm_device *dev)
>               return -ENOMEM;
>       dev->mode_config.prop_crtc_id = prop;
>  
> +     prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CONNECTOR_IDS", 0);
> +     if (!prop)
> +             return -ENOMEM;
> +     dev->mode_config.prop_connector_ids = prop;
> +
> +     prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "MODE", 0);
> +     if (!prop)
> +             return -ENOMEM;
> +     dev->mode_config.prop_mode = prop;
> +
>       return 0;
>  }
>  
> @@ -1754,7 +1967,7 @@ static void drm_crtc_convert_to_umode(struct 
> drm_mode_modeinfo *out,
>   * Returns:
>   * Zero on success, errno on failure.
>   */
> -static int drm_crtc_convert_umode(struct drm_display_mode *out,
> +int drm_crtc_convert_umode(struct drm_display_mode *out,
>                                 const struct drm_mode_modeinfo *in)
>  {
>       if (in->clock > INT_MAX || in->vrefresh > INT_MAX)
> @@ -2001,8 +2214,8 @@ int drm_mode_getcrtc(struct drm_device *dev,
>               goto out;
>       }
>  
> -     crtc_resp->x = crtc->x;
> -     crtc_resp->y = crtc->y;
> +     crtc_resp->x = crtc->primary->state->src_x >> 16;
> +     crtc_resp->y = crtc->primary->state->src_y >> 16;
>       crtc_resp->gamma_size = crtc->gamma_size;
>       if (crtc->primary->fb)
>               crtc_resp->fb_id = crtc->primary->fb->base.id;
> @@ -2495,7 +2708,7 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc,
>               vdisplay = adjusted.crtc_vdisplay;
>       }
>  
> -     if (crtc->invert_dimensions)
> +     if (crtc->state->invert_dimensions)
>               swap(hdisplay, vdisplay);
>  
>       if (hdisplay > fb->width ||
> @@ -2504,7 +2717,7 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc,
>           y > fb->height - vdisplay) {
>               DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport 
> %ux%u+%d+%d%s.\n",
>                             fb->width, fb->height, hdisplay, vdisplay, x, y,
> -                           crtc->invert_dimensions ? " (inverted)" : "");
> +                           crtc->state->invert_dimensions ? " (inverted)" : 
> "");
>               return -ENOSPC;
>       }
>  
> @@ -2531,22 +2744,15 @@ int drm_mode_setcrtc(struct drm_device *dev, void 
> *data,
>       struct drm_mode_config *config = &dev->mode_config;
>       struct drm_mode_crtc *crtc_req = data;
>       struct drm_crtc *crtc;
> -     struct drm_connector **connector_set = NULL, *connector;
> -     struct drm_framebuffer *fb = NULL;
> -     struct drm_display_mode *mode = NULL;
> -     struct drm_mode_set set;
> -     uint32_t __user *set_connectors_ptr;
> +     uint32_t fb_id = -1;
> +     uint32_t *connector_ids = NULL;
> +     struct drm_atomic_state *state = NULL;
>       int ret;
>       int i;
>  
>       if (!drm_core_check_feature(dev, DRIVER_MODESET))
>               return -EINVAL;
>  
> -     /* For some reason crtc x/y offsets are signed internally. */
> -     if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX)
> -             return -ERANGE;
> -
> -     drm_modeset_lock_all(dev);
>       crtc = drm_crtc_find(dev, crtc_req->crtc_id);
>       if (!crtc) {
>               DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
> @@ -2564,55 +2770,15 @@ int drm_mode_setcrtc(struct drm_device *dev, void 
> *data,
>                               ret = -EINVAL;
>                               goto out;
>                       }
> -                     fb = crtc->primary->fb;
> -                     /* Make refcounting symmetric with the lookup path. */
> -                     drm_framebuffer_reference(fb);
> +                     fb_id = crtc->primary->fb->base.id;
>               } else {
> -                     fb = drm_framebuffer_lookup(dev, crtc_req->fb_id);
> -                     if (!fb) {
> -                             DRM_DEBUG_KMS("Unknown FB ID%d\n",
> -                                             crtc_req->fb_id);
> -                             ret = -ENOENT;
> -                             goto out;
> -                     }
> -             }
> -
> -             mode = drm_mode_create(dev);
> -             if (!mode) {
> -                     ret = -ENOMEM;
> -                     goto out;
> +                     fb_id = crtc_req->fb_id;
>               }
> -
> -             ret = drm_crtc_convert_umode(mode, &crtc_req->mode);
> -             if (ret) {
> -                     DRM_DEBUG_KMS("Invalid mode\n");
> -                     goto out;
> -             }
> -
> -             drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
> -
> -             ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y,
> -                                           mode, fb);
> -             if (ret)
> -                     goto out;
> -
> -     }
> -
> -     if (crtc_req->count_connectors == 0 && mode) {
> -             DRM_DEBUG_KMS("Count connectors is 0 but mode set\n");
> -             ret = -EINVAL;
> -             goto out;
> -     }
> -
> -     if (crtc_req->count_connectors > 0 && (!mode || !fb)) {
> -             DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n",
> -                       crtc_req->count_connectors);
> -             ret = -EINVAL;
> -             goto out;
>       }
>  
>       if (crtc_req->count_connectors > 0) {
> -             u32 out_id;
> +             uint32_t __user *set_connectors_ptr =
> +                             (uint32_t __user *)(unsigned 
> long)crtc_req->set_connectors_ptr;
>  
>               /* Avoid unbounded kernel memory allocation */
>               if (crtc_req->count_connectors > config->num_connector) {
> @@ -2620,52 +2786,65 @@ int drm_mode_setcrtc(struct drm_device *dev, void 
> *data,
>                       goto out;
>               }
>  
> -             connector_set = kmalloc(crtc_req->count_connectors *
> -                                     sizeof(struct drm_connector *),
> +             connector_ids = kmalloc(crtc_req->count_connectors *
> +                                     sizeof(connector_ids[0]),
>                                       GFP_KERNEL);
> -             if (!connector_set) {
> +             if (!connector_ids) {
>                       ret = -ENOMEM;
>                       goto out;
>               }
>  
>               for (i = 0; i < crtc_req->count_connectors; i++) {
> -                     set_connectors_ptr = (uint32_t __user *)(unsigned 
> long)crtc_req->set_connectors_ptr;
> +                     u32 out_id;
> +
>                       if (get_user(out_id, &set_connectors_ptr[i])) {
>                               ret = -EFAULT;
>                               goto out;
>                       }
> -
> -                     connector = drm_connector_find(dev, out_id);
> -                     if (!connector) {
> -                             DRM_DEBUG_KMS("Connector id %d unknown\n",
> -                                             out_id);
> -                             ret = -ENOENT;
> -                             goto out;
> -                     }
> -                     DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
> -                                     connector->base.id,
> -                                     drm_get_connector_name(connector));
> -
> -                     connector_set[i] = connector;
> +                     connector_ids[i] = out_id;
>               }
>       }
>  
> -     set.crtc = crtc;
> -     set.x = crtc_req->x;
> -     set.y = crtc_req->y;
> -     set.mode = mode;
> -     set.connectors = connector_set;
> -     set.num_connectors = crtc_req->count_connectors;
> -     set.fb = fb;
> -     ret = drm_mode_set_config_internal(&set);
> +retry:
> +     state = dev->driver->atomic_begin(dev, 0);
> +     if (IS_ERR(state))
> +             return PTR_ERR(state);
>  
> -out:
> -     if (fb)
> -             drm_framebuffer_unreference(fb);
> +     /* If connectors change, we need to check if we need to steal one
> +      * from another CRTC..  setcrtc makes this implicit, but atomic
> +      * treats it as an error so we need to handle here:
> +      */
> +     ret = check_connectors(crtc, state, true,
> +             connector_ids, crtc_req->count_connectors);
> +     if (ret)
> +             goto out;
>  
> -     kfree(connector_set);
> -     drm_mode_destroy(dev, mode);
> -     drm_modeset_unlock_all(dev);
> +     ret =
> +             drm_mode_crtc_set_obj_prop(crtc, state,
> +                     config->prop_mode, sizeof(crtc_req->mode), 
> &crtc_req->mode) ||
> +             drm_mode_crtc_set_obj_prop(crtc, state,
> +                     config->prop_connector_ids,
> +                     crtc_req->count_connectors * sizeof(connector_ids[0]),
> +                     connector_ids) ||
> +             drm_mode_plane_set_obj_prop(crtc->primary, state,
> +                     config->prop_crtc_id, crtc->base.id, NULL) ||
> +             drm_mode_plane_set_obj_prop(crtc->primary, state,
> +                     config->prop_fb_id, fb_id, NULL) ||
> +             drm_mode_plane_set_obj_prop(crtc->primary, state,
> +                     config->prop_src_x, crtc_req->x << 16, NULL) ||
> +             drm_mode_plane_set_obj_prop(crtc->primary, state,
> +                     config->prop_src_y, crtc_req->y << 16, NULL) ||
> +             dev->driver->atomic_check(dev, state);
> +     if (ret)
> +             goto out;
> +
> +     ret = dev->driver->atomic_commit(dev, state);
> +
> +out:
> +     if (state)
> +             dev->driver->atomic_end(dev, state);
> +     if (ret == -EDEADLK)
> +             goto retry;
>       return ret;
>  }
>  
> @@ -4028,9 +4207,6 @@ int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
>       if (crtc->funcs->set_property)
>               ret = crtc->funcs->set_property(crtc, state, property,
>                               value, blob_data);
> -     if (!ret)
> -             drm_object_property_set_value(&crtc->base, &crtc->propvals,
> -                             property, value, NULL);
>  
>       return ret;
>  }
> @@ -4424,6 +4600,51 @@ out:
>       return ret;
>  }
>  
> +static struct drm_pending_vblank_event *create_vblank_event(
> +             struct drm_device *dev, struct drm_file *file_priv, uint64_t 
> user_data)
> +{
> +     struct drm_pending_vblank_event *e = NULL;
> +     unsigned long flags;
> +
> +     spin_lock_irqsave(&dev->event_lock, flags);
> +     if (file_priv->event_space < sizeof e->event) {
> +             spin_unlock_irqrestore(&dev->event_lock, flags);
> +             goto out;
> +     }
> +     file_priv->event_space -= sizeof e->event;
> +     spin_unlock_irqrestore(&dev->event_lock, flags);
> +
> +     e = kzalloc(sizeof *e, GFP_KERNEL);
> +     if (e == NULL) {
> +             spin_lock_irqsave(&dev->event_lock, flags);
> +             file_priv->event_space += sizeof e->event;
> +             spin_unlock_irqrestore(&dev->event_lock, flags);
> +             goto out;
> +     }
> +
> +     e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
> +     e->event.base.length = sizeof e->event;
> +     e->event.user_data = user_data;
> +     e->base.event = &e->event.base;
> +     e->base.file_priv = file_priv;
> +     e->base.destroy =
> +             (void (*) (struct drm_pending_event *)) kfree;
> +
> +out:
> +     return e;
> +}
> +
> +static void destroy_vblank_event(struct drm_device *dev,
> +             struct drm_file *file_priv, struct drm_pending_vblank_event *e)
> +{
> +     unsigned long flags;
> +
> +     spin_lock_irqsave(&dev->event_lock, flags);
> +     file_priv->event_space += sizeof e->event;
> +     spin_unlock_irqrestore(&dev->event_lock, flags);
> +     kfree(e);
> +}
> +
>  /**
>   * drm_mode_page_flip_ioctl - schedule an asynchronous fb update
>   * @dev: DRM device
> @@ -4446,10 +4667,10 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
>                            void *data, struct drm_file *file_priv)
>  {
>       struct drm_mode_crtc_page_flip *page_flip = data;
> +     struct drm_mode_config *config = &dev->mode_config;
>       struct drm_crtc *crtc;
> -     struct drm_framebuffer *fb = NULL, *old_fb = NULL;
>       struct drm_pending_vblank_event *e = NULL;
> -     unsigned long flags;
> +     struct drm_atomic_state *state;
>       int ret = -EINVAL;
>  
>       if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS ||
> @@ -4463,92 +4684,41 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
>       if (!crtc)
>               return -ENOENT;
>  
> -     drm_modeset_lock(&crtc->mutex, NULL);
> -     if (crtc->primary->fb == NULL) {
> -             /* The framebuffer is currently unbound, presumably
> -              * due to a hotplug event, that userspace has not
> -              * yet discovered.
> -              */
> -             ret = -EBUSY;
> -             goto out;
> -     }
> -
> -     if (crtc->funcs->page_flip == NULL)
> -             goto out;
> -
> -     fb = drm_framebuffer_lookup(dev, page_flip->fb_id);
> -     if (!fb) {
> -             ret = -ENOENT;
> -             goto out;
> -     }
> -
> -     ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb);
> -     if (ret)
> -             goto out;
> -
> -     if (crtc->primary->fb->pixel_format != fb->pixel_format) {
> -             DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer 
> format.\n");
> -             ret = -EINVAL;
> -             goto out;
> -     }
> +retry:
> +     state = dev->driver->atomic_begin(dev,
> +                     page_flip->flags | DRM_MODE_ATOMIC_NONBLOCK);
> +     if (IS_ERR(state))
> +             return PTR_ERR(state);
>  
>       if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
> -             ret = -ENOMEM;
> -             spin_lock_irqsave(&dev->event_lock, flags);
> -             if (file_priv->event_space < sizeof e->event) {
> -                     spin_unlock_irqrestore(&dev->event_lock, flags);
> +             e = create_vblank_event(dev, file_priv, page_flip->user_data);
> +             if (!e) {
> +                     ret = -ENOMEM;
>                       goto out;
>               }
> -             file_priv->event_space -= sizeof e->event;
> -             spin_unlock_irqrestore(&dev->event_lock, flags);
> -
> -             e = kzalloc(sizeof *e, GFP_KERNEL);
> -             if (e == NULL) {
> -                     spin_lock_irqsave(&dev->event_lock, flags);
> -                     file_priv->event_space += sizeof e->event;
> -                     spin_unlock_irqrestore(&dev->event_lock, flags);
> +             ret = dev->driver->atomic_set_event(dev, state, &crtc->base, e);
> +             if (ret) {
>                       goto out;
>               }
> -
> -             e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
> -             e->event.base.length = sizeof e->event;
> -             e->event.user_data = page_flip->user_data;
> -             e->base.event = &e->event.base;
> -             e->base.file_priv = file_priv;
> -             e->base.destroy =
> -                     (void (*) (struct drm_pending_event *)) kfree;
>       }
>  
> -     old_fb = crtc->primary->fb;
> -     ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags);
> -     if (ret) {
> -             if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
> -                     spin_lock_irqsave(&dev->event_lock, flags);
> -                     file_priv->event_space += sizeof e->event;
> -                     spin_unlock_irqrestore(&dev->event_lock, flags);
> -                     kfree(e);
> -             }
> -             /* Keep the old fb, don't unref it. */
> -             old_fb = NULL;
> -     } else {
> -             /*
> -              * Warn if the driver hasn't properly updated the crtc->fb
> -              * field to reflect that the new framebuffer is now used.
> -              * Failing to do so will screw with the reference counting
> -              * on framebuffers.
> -              */
> -             WARN_ON(crtc->primary->fb != fb);
> -             /* Unref only the old framebuffer. */
> -             fb = NULL;
> -     }
> +     ret = drm_mode_plane_set_obj_prop(crtc->primary, state,
> +                     config->prop_fb_id, page_flip->fb_id, NULL);
> +     if (ret)
> +             goto out;
>  
> -out:
> -     if (fb)
> -             drm_framebuffer_unreference(fb);
> -     if (old_fb)
> -             drm_framebuffer_unreference(old_fb);
> -     drm_modeset_unlock(&crtc->mutex);
> +     ret = dev->driver->atomic_check(dev, state);
> +     if (ret)
> +             goto out;
> +
> +     ret = dev->driver->atomic_commit(dev, state);
>  
> +out:
> +     if (ret && e)
> +             destroy_vblank_event(dev, file_priv, e);
> +     dev->driver->atomic_end(dev, state);
> +     if (ret == -EDEADLK)
> +             goto retry;
>       return ret;
>  }
>  
> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> index b73d3b0..4669e69 100644
> --- a/drivers/gpu/drm/drm_fb_helper.c
> +++ b/drivers/gpu/drm/drm_fb_helper.c
> @@ -286,7 +286,7 @@ 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;
> +     struct drm_atomic_state *state;
>       int i;
>  
>       drm_warn_on_modeset_not_all_locked(dev);
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c 
> b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
> index 2a56973..f3c7e77 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
> @@ -14,6 +14,7 @@
>  
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic.h>
>  
>  #include "exynos_drm_crtc.h"
>  #include "exynos_drm_drv.h"
> @@ -289,6 +290,10 @@ static int exynos_drm_crtc_set_property(struct drm_crtc 
> *crtc,
>       struct drm_device *dev = crtc->dev;
>       struct exynos_drm_private *dev_priv = dev->dev_private;
>       struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
> +     struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
> +
> +     if (IS_ERR(cstate))
> +             return PTR_ERR(cstate);
>  
>       if (property == dev_priv->crtc_mode_property) {
>               enum exynos_crtc_mode mode = val;
> @@ -313,7 +318,7 @@ static int exynos_drm_crtc_set_property(struct drm_crtc 
> *crtc,
>               return 0;
>       }
>  
> -     return -EINVAL;
> +     return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
>  }
>  
>  static struct drm_crtc_funcs exynos_crtc_funcs = {
> diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c 
> b/drivers/gpu/drm/gma500/cdv_intel_display.c
> index 6672732..5b6eee9 100644
> --- a/drivers/gpu/drm/gma500/cdv_intel_display.c
> +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c
> @@ -989,6 +989,7 @@ const struct drm_crtc_funcs cdv_intel_crtc_funcs = {
>       .cursor_move = gma_crtc_cursor_move,
>       .gamma_set = gma_crtc_gamma_set,
>       .set_config = gma_crtc_set_config,
> +     .set_property = drm_atomic_crtc_set_property,
>       .destroy = gma_crtc_destroy,
>  };
>  
> diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c 
> b/drivers/gpu/drm/gma500/psb_intel_display.c
> index 87b50ba..79b5692 100644
> --- a/drivers/gpu/drm/gma500/psb_intel_display.c
> +++ b/drivers/gpu/drm/gma500/psb_intel_display.c
> @@ -444,6 +444,7 @@ const struct drm_crtc_funcs psb_intel_crtc_funcs = {
>       .cursor_move = gma_crtc_cursor_move,
>       .gamma_set = gma_crtc_gamma_set,
>       .set_config = gma_crtc_set_config,
> +     .set_property = drm_atomic_crtc_set_property,
>       .destroy = gma_crtc_destroy,
>  };
>  
> diff --git a/drivers/gpu/drm/i915/intel_display.c 
> b/drivers/gpu/drm/i915/intel_display.c
> index e9f6eb7..53b996f 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -10430,6 +10430,7 @@ static const struct drm_crtc_funcs intel_crtc_funcs = 
> {
>       .cursor_move = intel_crtc_cursor_move,
>       .gamma_set = intel_crtc_gamma_set,
>       .set_config = intel_crtc_set_config,
> +     .set_property = drm_atomic_crtc_set_property,
>       .destroy = intel_crtc_destroy,
>       .page_flip = intel_crtc_page_flip,
>  };
> diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c 
> b/drivers/gpu/drm/mgag200/mgag200_mode.c
> index a034ed4..ba9bd91 100644
> --- a/drivers/gpu/drm/mgag200/mgag200_mode.c
> +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c
> @@ -1296,6 +1296,7 @@ static const struct drm_crtc_funcs mga_crtc_funcs = {
>       .cursor_move = mga_crtc_cursor_move,
>       .gamma_set = mga_crtc_gamma_set,
>       .set_config = drm_crtc_helper_set_config,
> +     .set_property = drm_atomic_crtc_set_property,
>       .destroy = mga_crtc_destroy,
>  };
>  
> diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c 
> b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
> index 7cf0f78..d0d8befd 100644
> --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
> +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
> @@ -471,8 +471,10 @@ static int mdp4_crtc_set_property(struct drm_crtc *crtc,
>               struct drm_atomic_state *state, struct drm_property *property,
>               uint64_t val, void *blob_data)
>  {
> -     // XXX
> -     return -EINVAL;
> +     struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
> +     if (IS_ERR(cstate))
> +             return PTR_ERR(cstate);
> +     return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
>  }
>  
>  #define CURSOR_WIDTH 64
> diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c 
> b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
> index 771390b..7f4ee99 100644
> --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
> +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
> @@ -389,8 +389,10 @@ static int mdp5_crtc_set_property(struct drm_crtc *crtc,
>               struct drm_atomic_state *state, struct drm_property *property,
>               uint64_t val, void *blob_data)
>  {
> -     // XXX
> -     return -EINVAL;
> +     struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
> +     if (IS_ERR(cstate))
> +             return PTR_ERR(cstate);
> +     return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
>  }
>  
>  static const struct drm_crtc_funcs mdp5_crtc_funcs = {
> diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c 
> b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
> index 41be342..9e24632 100644
> --- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c
> +++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
> @@ -1086,6 +1086,7 @@ static const struct drm_crtc_funcs nv04_crtc_funcs = {
>       .cursor_move = nv04_crtc_cursor_move,
>       .gamma_set = nv_crtc_gamma_set,
>       .set_config = nouveau_crtc_set_config,
> +     .set_property = drm_atomic_crtc_set_property,
>       .page_flip = nouveau_crtc_page_flip,
>       .destroy = nv_crtc_destroy,
>  };
> diff --git a/drivers/gpu/drm/nouveau/nv50_display.c 
> b/drivers/gpu/drm/nouveau/nv50_display.c
> index 58af547..ecbffeb 100644
> --- a/drivers/gpu/drm/nouveau/nv50_display.c
> +++ b/drivers/gpu/drm/nouveau/nv50_display.c
> @@ -1329,6 +1329,7 @@ static const struct drm_crtc_funcs nv50_crtc_func = {
>       .cursor_move = nv50_crtc_cursor_move,
>       .gamma_set = nv50_crtc_gamma_set,
>       .set_config = nouveau_crtc_set_config,
> +     .set_property = drm_atomic_crtc_set_property,
>       .destroy = nv50_crtc_destroy,
>       .page_flip = nouveau_crtc_page_flip,
>  };
> diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c 
> b/drivers/gpu/drm/omapdrm/omap_crtc.c
> index a75934d..772687b 100644
> --- a/drivers/gpu/drm/omapdrm/omap_crtc.c
> +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
> @@ -387,14 +387,22 @@ static int omap_crtc_set_property(struct drm_crtc *crtc,
>  {
>       struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
>       struct omap_drm_private *priv = crtc->dev->dev_private;
> +     struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
> +     int ret;
> +
> +     if (IS_ERR(cstate))
> +             return PTR_ERR(cstate);
>  
>       if (property == priv->rotation_prop) {
> -             crtc->invert_dimensions =
> +             cstate->invert_dimensions =
>                               !!(val & ((1LL << DRM_ROTATE_90) | (1LL << 
> DRM_ROTATE_270)));
>       }
>  
> -     return omap_plane_set_property(omap_crtc->plane, state,
> +     ret = omap_plane_set_property(omap_crtc->plane, state,
>                       property, val, blob_data);
> +     if (ret)
> +             ret = drm_crtc_set_property(crtc, cstate, property, val, 
> blob_data);
> +     return ret;
>  }
>  
>  static const struct drm_crtc_funcs omap_crtc_funcs = {
> diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c 
> b/drivers/gpu/drm/omapdrm/omap_drv.c
> index da80bdc..3f64c47 100644
> --- a/drivers/gpu/drm/omapdrm/omap_drv.c
> +++ b/drivers/gpu/drm/omapdrm/omap_drv.c
> @@ -579,7 +579,7 @@ static void dev_lastclose(struct drm_device *dev)
>                */
>               for (i = 0; i < priv->num_crtcs; i++) {
>                       drm_object_property_set_value(&priv->crtcs[i]->base,
> -                                     &priv->crtcs[i]->propvals,
> +                                     &priv->crtcs[i]->state->propvals,
>                                       priv->rotation_prop, 0, NULL);
>               }
>  
> diff --git a/drivers/gpu/drm/qxl/qxl_display.c 
> b/drivers/gpu/drm/qxl/qxl_display.c
> index b54c970..25896a9 100644
> --- a/drivers/gpu/drm/qxl/qxl_display.c
> +++ b/drivers/gpu/drm/qxl/qxl_display.c
> @@ -29,6 +29,7 @@
>  #include "qxl_drv.h"
>  #include "qxl_object.h"
>  #include "drm_crtc_helper.h"
> +#include "drm_atomic.h"
>  
>  static bool qxl_head_enabled(struct qxl_head *head)
>  {
> @@ -373,6 +374,7 @@ static const struct drm_crtc_funcs qxl_crtc_funcs = {
>       .cursor_set2 = qxl_crtc_cursor_set2,
>       .cursor_move = qxl_crtc_cursor_move,
>       .set_config = drm_crtc_helper_set_config,
> +     .set_property = drm_atomic_crtc_set_property,
>       .destroy = qxl_crtc_destroy,
>  };
>  
> diff --git a/drivers/gpu/drm/radeon/radeon_display.c 
> b/drivers/gpu/drm/radeon/radeon_display.c
> index 8d99d5e..cc86aac 100644
> --- a/drivers/gpu/drm/radeon/radeon_display.c
> +++ b/drivers/gpu/drm/radeon/radeon_display.c
> @@ -32,6 +32,7 @@
>  
>  #include <linux/pm_runtime.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic.h>
>  #include <drm/drm_edid.h>
>  
>  #include <linux/gcd.h>
> @@ -546,6 +547,7 @@ static const struct drm_crtc_funcs radeon_crtc_funcs = {
>       .cursor_move = radeon_crtc_cursor_move,
>       .gamma_set = radeon_crtc_gamma_set,
>       .set_config = radeon_crtc_set_config,
> +     .set_property = drm_atomic_crtc_set_property,
>       .destroy = radeon_crtc_destroy,
>       .page_flip = radeon_crtc_page_flip,
>  };
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c 
> b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
> index 299267d..f5a3d55 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
> @@ -17,6 +17,7 @@
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic.h>
>  #include <drm/drm_fb_cma_helper.h>
>  #include <drm/drm_gem_cma_helper.h>
>  
> @@ -527,6 +528,7 @@ static int rcar_du_crtc_page_flip(struct drm_crtc *crtc,
>  static const struct drm_crtc_funcs crtc_funcs = {
>       .destroy = drm_crtc_cleanup,
>       .set_config = drm_crtc_helper_set_config,
> +     .set_property = drm_atomic_crtc_set_property,
>       .page_flip = rcar_du_crtc_page_flip,
>  };
>  
> diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c 
> b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
> index 90e023a..0a5280c 100644
> --- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
> +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
> @@ -17,6 +17,7 @@
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic.h>
>  #include <drm/drm_fb_cma_helper.h>
>  #include <drm/drm_gem_cma_helper.h>
>  
> @@ -506,6 +507,7 @@ static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc,
>  static const struct drm_crtc_funcs crtc_funcs = {
>       .destroy = drm_crtc_cleanup,
>       .set_config = drm_crtc_helper_set_config,
> +     .set_property = drm_atomic_crtc_set_property,
>       .page_flip = shmob_drm_crtc_page_flip,
>  };
>  
> diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c 
> b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
> index 92839ba..b07f116 100644
> --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
> +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
> @@ -411,6 +411,7 @@ static int tilcdc_crtc_mode_set_base(struct drm_crtc 
> *crtc, int x, int y,
>  static const struct drm_crtc_funcs tilcdc_crtc_funcs = {
>               .destroy        = tilcdc_crtc_destroy,
>               .set_config     = drm_crtc_helper_set_config,
> +             .set_property   = drm_atomic_crtc_set_property,
>               .page_flip      = tilcdc_crtc_page_flip,
>  };
>  
> diff --git a/drivers/gpu/drm/udl/udl_modeset.c 
> b/drivers/gpu/drm/udl/udl_modeset.c
> index cddc4fc..36d0116 100644
> --- a/drivers/gpu/drm/udl/udl_modeset.c
> +++ b/drivers/gpu/drm/udl/udl_modeset.c
> @@ -14,6 +14,7 @@
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_atomic.h>
>  #include "udl_drv.h"
>  
>  /*
> @@ -383,6 +384,7 @@ static struct drm_crtc_helper_funcs udl_helper_funcs = {
>  
>  static const struct drm_crtc_funcs udl_crtc_funcs = {
>       .set_config = drm_crtc_helper_set_config,
> +     .set_property = drm_atomic_crtc_set_property,
>       .destroy = udl_crtc_destroy,
>  };
>  
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c 
> b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
> index b2b9bd2..0313b00 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
> @@ -300,6 +300,7 @@ static struct drm_crtc_funcs vmw_legacy_crtc_funcs = {
>       .cursor_move = vmw_du_crtc_cursor_move,
>       .gamma_set = vmw_du_crtc_gamma_set,
>       .destroy = vmw_ldu_crtc_destroy,
> +     .set_property = drm_atomic_crtc_set_property,
>       .set_config = vmw_ldu_crtc_set_config,
>  };
>  
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c 
> b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
> index a95d3a0..b723e09 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
> @@ -397,6 +397,7 @@ static struct drm_crtc_funcs vmw_screen_object_crtc_funcs 
> = {
>       .gamma_set = vmw_du_crtc_gamma_set,
>       .destroy = vmw_sou_crtc_destroy,
>       .set_config = vmw_sou_crtc_set_config,
> +     .set_property = drm_atomic_crtc_set_property,
>       .page_flip = vmw_du_page_flip,
>  };
>  
> diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
> index 78e93ec..7946b7f 100644
> --- a/include/drm/drm_atomic.h
> +++ b/include/drm/drm_atomic.h
> @@ -70,6 +70,9 @@
>  struct drm_atomic_funcs {
>       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);
> +
> +     int (*check_crtc_state)(struct drm_crtc *crtc, struct drm_crtc_state 
> *cstate);
> +     int (*commit_crtc_state)(struct drm_crtc *crtc, struct drm_crtc_state 
> *cstate);
>  };
>  
>  const extern struct drm_atomic_funcs drm_atomic_funcs;
> @@ -109,6 +112,30 @@ drm_atomic_commit_plane_state(struct drm_plane *plane,
>       return funcs->commit_plane_state(plane, pstate);
>  }
>  
> +int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
> +             struct drm_atomic_state *state, struct drm_property *property,
> +             uint64_t val, void *blob_data);
> +struct drm_crtc_state *drm_atomic_get_crtc_state(struct drm_crtc *crtc,
> +             struct drm_atomic_state *state);
> +
> +static inline int
> +drm_atomic_check_crtc_state(struct drm_crtc *crtc,
> +             struct drm_crtc_state *cstate)
> +{
> +     const struct drm_atomic_funcs *funcs =
> +                     crtc->dev->driver->atomic_funcs;
> +     return funcs->check_crtc_state(crtc, cstate);
> +}
> +
> +static inline int
> +drm_atomic_commit_crtc_state(struct drm_crtc *crtc,
> +             struct drm_crtc_state *cstate)
> +{
> +     const struct drm_atomic_funcs *funcs =
> +                     crtc->dev->driver->atomic_funcs;
> +     return funcs->commit_crtc_state(crtc, cstate);
> +}
> +
>  /**
>   * struct drm_atomic_state - the state object used by atomic helpers
>   */
> @@ -118,6 +145,8 @@ struct drm_atomic_state {
>       uint32_t flags;
>       struct drm_plane **planes;
>       struct drm_plane_state **pstates;
> +     struct drm_crtc **crtcs;
> +     struct drm_crtc_state **cstates;
>  
>       bool committed;
>       bool checked;       /* just for debugging */
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index 58309cc..2fbf13a 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -284,6 +284,10 @@ struct drm_crtc_funcs {
>                        struct drm_pending_vblank_event *event,
>                        uint32_t flags);
>  
> +     struct drm_crtc_state *(*create_state)(struct drm_crtc *crtc);
> +     void (*destroy_state)(struct drm_crtc *crtc,
> +                         struct drm_crtc_state *cstate);
> +
>       int (*set_property)(struct drm_crtc *crtc,
>                           struct drm_atomic_state *state,
>                           struct drm_property *property, uint64_t val,
> @@ -291,21 +295,52 @@ struct drm_crtc_funcs {
>  };
>  
>  /**
> + * drm_crtc_state - mutable crtc state
> + * @invert_dimensions: for purposes of error checking crtc vs fb sizes,
> + *    invert the width/height of the crtc.  This is used if the driver
> + *    is performing 90 or 270 degree rotated scanout
> + * @mode_valid: a valid mode has been set
> + * @set_config: needs modeset (crtc->set_config())
> + * @connectors_change: the connector-ids array has changed
> + * @num_connector_ids: the number of connector-ids
> + * @connector_ids: array of connector ids
> + * @mode: current mode timings
> + * @event: pending pageflip event
> + * @propvals: property values
> + * @state: current global/toplevel state object (for atomic) while an
> + *    update is in progress, NULL otherwise.
> + */
> +struct drm_crtc_state {
> +     bool invert_dimensions : 1;
> +     bool mode_valid        : 1;
> +
> +     /* transient state, only valid during atomic operation: */
> +     bool set_config        : 1;
> +     bool connectors_change : 1;
> +
> +     uint8_t num_connector_ids;
> +     uint32_t *connector_ids;
> +     struct drm_mode_modeinfo mode;
> +
> +     struct drm_pending_vblank_event *event;
> +
> +     struct drm_object_property_values propvals;
> +
> +     struct drm_atomic_state *state;
> +};
> +
> +/**
>   * drm_crtc - central CRTC control structure
>   * @dev: parent DRM device
>   * @head: list management
> + * @id: CRTC number, 0..n
>   * @mutex: per-CRTC locking
>   * @base: base KMS object for ID tracking etc.
>   * @primary: primary plane for this CRTC
>   * @cursor: cursor plane for this CRTC
> + * @state: the mutable state
>   * @enabled: is this CRTC enabled?
> - * @mode: current mode timings
>   * @hwmode: mode timings as programmed to hw regs
> - * @invert_dimensions: for purposes of error checking crtc vs fb sizes,
> - *    invert the width/height of the crtc.  This is used if the driver
> - *    is performing 90 or 270 degree rotated scanout
> - * @x: x position on screen
> - * @y: y position on screen
>   * @funcs: CRTC control functions
>   * @gamma_size: size of gamma ramp
>   * @gamma_store: gamma ramp values
> @@ -322,6 +357,8 @@ struct drm_crtc {
>       struct drm_device *dev;
>       struct list_head head;
>  
> +     int id;
> +
>       /**
>        * crtc mutex
>        *
> @@ -337,23 +374,19 @@ struct drm_crtc {
>       struct drm_plane *primary;
>       struct drm_plane *cursor;
>  
> +     struct drm_crtc_state *state;
> +
>       /* Temporary tracking of the old fb while a modeset is ongoing. Used
>        * by drm_mode_set_config_internal to implement correct refcounting. */
>       struct drm_framebuffer *old_fb;
>  
>       bool enabled;
>  
> -     /* Requested mode from modesetting. */
> -     struct drm_display_mode mode;
> -
>       /* Programmed mode in hw, after adjustments for encoders,
>        * crtc, panel scaling etc. Needed for timestamping etc.
>        */
>       struct drm_display_mode hwmode;
>  
> -     bool invert_dimensions;
> -
> -     int x, y;
>       const struct drm_crtc_funcs *funcs;
>  
>       /* CRTC gamma size for reporting to userspace */
> @@ -367,9 +400,15 @@ struct drm_crtc {
>       void *helper_private;
>  
>       struct drm_object_properties properties;
> -     struct drm_object_property_values propvals;
> -};
>  
> +     /* These are (temporary) duplicate information from what is in the
> +      * drm_crtc_state struct..  keeping duplicate copy here makes the
> +      * switch to atomic far less intrusive.  Once all the drivers and
> +      * the crtc/fb helpers are updated, then we can remove these:
> +      */
> +     int x, y;
> +     struct drm_display_mode mode;
> +};
>  
>  /**
>   * drm_connector_funcs - control connectors on a given device
> @@ -875,6 +914,8 @@ struct drm_mode_config {
>       struct drm_property *prop_crtc_h;
>       struct drm_property *prop_fb_id;
>       struct drm_property *prop_crtc_id;
> +     struct drm_property *prop_connector_ids;
> +     struct drm_property *prop_mode;
>       struct drm_property *edid_property;
>       struct drm_property *dpms_property;
>       struct drm_property *plane_type_property;
> @@ -935,7 +976,8 @@ extern int drm_crtc_init(struct drm_device *dev,
>                        struct drm_crtc *crtc,
>                        const struct drm_crtc_funcs *funcs);
>  extern void drm_crtc_cleanup(struct drm_crtc *crtc);
> -extern unsigned int drm_crtc_index(struct drm_crtc *crtc);
> +struct drm_display_mode *drm_crtc_get_mode(struct drm_crtc *crtc,
> +             struct drm_crtc_state *cstate);
>  
>  /**
>   * drm_crtc_mask - find the mask of a registered CRTC
> @@ -946,9 +988,18 @@ extern unsigned int drm_crtc_index(struct drm_crtc 
> *crtc);
>   */
>  static inline uint32_t drm_crtc_mask(struct drm_crtc *crtc)
>  {
> -     return 1 << drm_crtc_index(crtc);
> +     return 1 << crtc->id;
>  }
>  
> +extern int drm_crtc_check_state(struct drm_crtc *crtc,
> +             struct drm_crtc_state *state);
> +extern void drm_crtc_commit_state(struct drm_crtc *crtc,
> +             struct drm_crtc_state *state);
> +extern int drm_crtc_set_property(struct drm_crtc *crtc,
> +             struct drm_crtc_state *state,
> +             struct drm_property *property,
> +             uint64_t value, void *blob_data);
> +
>  extern void drm_connector_ida_init(void);
>  extern void drm_connector_ida_destroy(void);
>  extern int drm_connector_init(struct drm_device *dev,
> @@ -1024,6 +1075,7 @@ extern const char *drm_get_tv_select_name(int val);
>  extern void drm_fb_release(struct drm_file *file_priv);
>  extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct 
> drm_mode_group *group);
>  extern void drm_mode_group_destroy(struct drm_mode_group *group);
> +extern int drm_crtc_convert_umode(struct drm_display_mode *out, const struct 
> drm_mode_modeinfo *in);
>  extern bool drm_probe_ddc(struct i2c_adapter *adapter);
>  extern struct edid *drm_get_edid(struct drm_connector *connector,
>                                struct i2c_adapter *adapter);
> @@ -1251,6 +1303,25 @@ drm_property_blob_find(struct drm_device *dev, 
> uint32_t id)
>       return mo ? obj_to_blob(mo) : NULL;
>  }
>  
> +static inline struct drm_crtc_state *
> +drm_crtc_create_state(struct drm_crtc *crtc)
> +{
> +     if (crtc->funcs->create_state)
> +             return crtc->funcs->create_state(crtc);
> +     return kzalloc(sizeof(struct drm_crtc_state), GFP_KERNEL);
> +}
> +
> +static inline void
> +drm_crtc_destroy_state(struct drm_crtc *crtc,
> +             struct drm_crtc_state *cstate)
> +{
> +     kfree(cstate->connector_ids);
> +     if (crtc->funcs->destroy_state)
> +             crtc->funcs->destroy_state(crtc, cstate);
> +     else
> +             kfree(cstate);
> +}
> +
>  static inline struct drm_plane_state *
>  drm_plane_create_state(struct drm_plane *plane)
>  {
> -- 
> 1.9.0
> 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

Reply via email to