From: Ville Syrjälä <ville.syrj...@linux.intel.com>

Implement the atomic modeset operations.

TODO: need to rewrite this for the new intel modeset code

Signed-off-by: Ville Syrjälä <ville.syrj...@linux.intel.com>
---
 drivers/gpu/drm/i915/Makefile        |    1 +
 drivers/gpu/drm/i915/intel_atomic.c  | 1462 ++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_display.c |    7 +
 3 files changed, 1470 insertions(+), 0 deletions(-)
 create mode 100644 drivers/gpu/drm/i915/intel_atomic.c

diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index b0bacdb..377d100 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -16,6 +16,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \
          i915_gem_tiling.o \
          i915_sysfs.o \
          i915_trace_points.o \
+         intel_atomic.o \
          intel_display.o \
          intel_crt.o \
          intel_lvds.o \
diff --git a/drivers/gpu/drm/i915/intel_atomic.c 
b/drivers/gpu/drm/i915/intel_atomic.c
new file mode 100644
index 0000000..363018f
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_atomic.c
@@ -0,0 +1,1462 @@
+/*
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+
+#include "intel_drv.h"
+
+static struct drm_property *prop_src_x;
+static struct drm_property *prop_src_y;
+static struct drm_property *prop_src_w;
+static struct drm_property *prop_src_h;
+static struct drm_property *prop_crtc_x;
+static struct drm_property *prop_crtc_y;
+static struct drm_property *prop_crtc_w;
+static struct drm_property *prop_crtc_h;
+static struct drm_property *prop_fb_id;
+static struct drm_property *prop_crtc_id;
+static struct drm_property *prop_mode;
+static struct drm_property *prop_connector_ids;
+static struct drm_property *prop_cursor_id;
+static struct drm_property *prop_cursor_x;
+static struct drm_property *prop_cursor_y;
+static struct drm_property *prop_cursor_w;
+static struct drm_property *prop_cursor_h;
+
+struct intel_plane_state {
+       struct drm_plane *plane;
+       struct drm_framebuffer *old_fb;
+       struct intel_plane_coords coords;
+       bool dirty;
+       bool pinned;
+};
+
+struct intel_crtc_state {
+       struct drm_crtc *crtc;
+       struct drm_framebuffer *old_fb;
+       struct drm_i915_gem_object *old_cursor_bo;
+       bool mode_dirty;
+       bool fb_dirty;
+       bool cursor_dirty;
+       bool active_dirty;
+       bool pinned;
+       bool cursor_pinned;
+       unsigned long connectors_bitmask;
+       unsigned long encoders_bitmask;
+};
+
+struct intel_atomic_state {
+       struct drm_file *file;
+       struct intel_plane_state *plane;
+       struct intel_crtc_state *crtc;
+       bool dirty;
+       bool restore_hw;
+       unsigned int flags;
+       uint64_t user_data;
+       struct drm_plane *saved_planes;
+       struct intel_crtc *saved_crtcs;
+       struct drm_connector *saved_connectors;
+       struct drm_encoder *saved_encoders;
+};
+
+static void update_connectors_bitmask(struct intel_crtc_state *st)
+{
+       struct drm_device *dev = st->crtc->dev;
+       struct drm_connector *connector;
+       unsigned int i;
+
+       st->connectors_bitmask = 0;
+
+       i = 0;
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               if (connector->encoder && connector->encoder->crtc == st->crtc)
+                       __set_bit(i, &st->connectors_bitmask);
+
+               i++;
+       }
+}
+
+static void update_encoders_bitmask(struct intel_crtc_state *st)
+{
+       struct drm_device *dev = st->crtc->dev;
+       struct drm_encoder *encoder;
+       unsigned int i;
+
+       st->encoders_bitmask = 0;
+
+       i = 0;
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               if (encoder->crtc == st->crtc)
+                       __set_bit(i, &st->encoders_bitmask);
+
+               i++;
+       }
+}
+
+static int process_connectors(struct intel_crtc_state *s, const uint32_t *ids, 
int count_ids)
+{
+       struct drm_crtc *crtc = s->crtc;
+       struct drm_device *dev = crtc->dev;
+       struct drm_connector *connectors[count_ids];
+       struct drm_connector *connector;
+       struct drm_encoder *encoder;
+       int i;
+
+       for (i = 0; i < count_ids; i++) {
+               struct drm_encoder *encoder;
+               const struct drm_connector_helper_funcs *connector_funcs;
+               struct drm_mode_object *obj;
+               int j;
+
+               /* don't accept duplicates */
+               for (j = i + 1; j < count_ids; j++)
+                       if (ids[i] == ids[j])
+                               return -EINVAL;
+
+               obj = drm_mode_object_find(dev, ids[i], 
DRM_MODE_OBJECT_CONNECTOR);
+               if (!obj)
+                       return -ENOENT;
+
+               connector = obj_to_connector(obj);
+               connector_funcs = connector->helper_private;
+
+               encoder = connector_funcs->best_encoder(connector);
+
+               if (!drm_encoder_crtc_ok(encoder, crtc))
+                       return -EINVAL;
+
+               connectors[i] = connector;
+       }
+
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               const struct drm_connector_helper_funcs *connector_funcs =
+                       connector->helper_private;
+
+               for (i = 0; i < count_ids; i++) {
+                       if (connector == connectors[i])
+                               break;
+               }
+
+               /* this connector isn't in the set */
+               if (i == count_ids) {
+                       /* remove the link to the encoder if this crtc was 
driving it previously */
+                       if (connector->encoder && connector->encoder->crtc == 
crtc) {
+                               s->mode_dirty = true;
+                               connector->encoder = NULL;
+                       }
+                       continue;
+               }
+
+               encoder = connector_funcs->best_encoder(connector);
+
+               if (encoder != connector->encoder) {
+                       s->mode_dirty = true;
+                       connector->encoder = encoder;
+               }
+
+               if (crtc != encoder->crtc) {
+                       s->mode_dirty = true;
+                       encoder->crtc = crtc;
+               }
+       }
+
+       /* prune dangling encoder->crtc links pointing to this crtc  */
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               if (encoder->crtc == crtc && 
!drm_helper_encoder_in_use(encoder))
+                       encoder->crtc = NULL;
+       }
+
+       update_connectors_bitmask(s);
+       update_encoders_bitmask(s);
+
+       return 0;
+}
+
+static size_t intel_atomic_state_size(const struct drm_device *dev)
+{
+       struct intel_atomic_state *state;
+       unsigned int num_connector = dev->mode_config.num_connector;
+       unsigned int num_encoder = dev->mode_config.num_encoder;
+       unsigned int num_crtc = dev->mode_config.num_crtc;
+       unsigned int num_plane = dev->mode_config.num_plane;
+
+       return sizeof *state +
+               num_crtc * sizeof state->crtc[0] +
+               num_plane * sizeof state->plane[0] +
+               num_connector * sizeof state->saved_connectors[0] +
+               num_encoder * sizeof state->saved_encoders[0] +
+               num_crtc * sizeof state->saved_crtcs[0] +
+               num_plane * sizeof state->saved_planes[0];
+}
+
+static void *intel_atomic_begin(struct drm_device *dev, struct drm_file *file,
+                               uint32_t flags, uint64_t user_data)
+{
+       struct intel_atomic_state *state;
+       struct drm_plane *plane;
+       struct drm_crtc *crtc;
+       struct drm_connector *connector;
+       struct drm_encoder *encoder;
+       unsigned int num_connector = dev->mode_config.num_connector;
+       unsigned int num_encoder = dev->mode_config.num_encoder;
+       unsigned int num_crtc = dev->mode_config.num_crtc;
+       unsigned int num_plane = dev->mode_config.num_plane;
+       int i;
+
+       state = kzalloc(intel_atomic_state_size(dev), GFP_KERNEL);
+       if (!state)
+               return ERR_PTR(-ENOMEM);
+
+       state->flags = flags;
+       state->file = file;
+       state->user_data = user_data;
+
+       state->crtc = (struct intel_crtc_state *)(state + 1);
+       state->plane = (struct intel_plane_state  *)(state->crtc + num_crtc);
+
+       state->saved_connectors = (struct drm_connector *)(state->plane + 
num_plane);
+       state->saved_encoders = (struct drm_encoder *)(state->saved_connectors 
+ num_connector);
+       state->saved_crtcs = (struct intel_crtc *)(state->saved_encoders + 
num_encoder);
+       state->saved_planes = (struct drm_plane *)(state->saved_crtcs + 
num_crtc);
+
+       i = 0;
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               struct intel_crtc_state *s = &state->crtc[i++];
+               struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+               s->crtc = crtc;
+               s->old_fb = crtc->fb;
+               s->old_cursor_bo = intel_crtc->cursor_bo;
+
+               update_connectors_bitmask(s);
+               update_encoders_bitmask(s);
+       }
+
+       i = 0;
+       list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+               struct intel_plane_state *s = &state->plane[i++];
+
+               s->plane = plane;
+               s->old_fb = plane->fb;
+       }
+
+       i = 0;
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+               state->saved_connectors[i++] = *connector;
+       i = 0;
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+               state->saved_encoders[i++] = *encoder;
+       i = 0;
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+               state->saved_crtcs[i++] = *intel_crtc;
+       }
+       i = 0;
+       list_for_each_entry(plane, &dev->mode_config.plane_list, head)
+               state->saved_planes[i++] = *plane;
+
+       state->file = file;
+
+       return state;
+}
+
+static int plane_set(struct intel_atomic_state *s,
+                    struct intel_plane_state *state,
+                    struct drm_property *prop,
+                    uint64_t value)
+{
+       struct drm_plane *plane = state->plane;
+       struct drm_mode_object *obj;
+
+       if (prop == prop_src_x) {
+               if (plane->src_x == value)
+                       return 0;
+               plane->src_x = value;
+       } else if (prop == prop_src_y) {
+               if (plane->src_y == value)
+                       return 0;
+               plane->src_y = value;
+       } else if (prop == prop_src_w) {
+               if (plane->src_w == value)
+                       return 0;
+               plane->src_w = value;
+       } else if (prop == prop_src_h) {
+               if (plane->src_h == value)
+                       return 0;
+               plane->src_h = value;
+       } else if (prop == prop_crtc_x) {
+               if (plane->crtc_x == value)
+                       return 0;
+               plane->crtc_x = value;
+       } else if (prop == prop_crtc_y) {
+               if (plane->crtc_y == value)
+                       return 0;
+               plane->crtc_y = value;
+       } else if (prop == prop_crtc_w) {
+               if (plane->crtc_w == value)
+                       return 0;
+               plane->crtc_w = value;
+       } else if (prop == prop_crtc_h) {
+               if (plane->crtc_h == value)
+                       return 0;
+               plane->crtc_h = value;
+       } else if (prop == prop_crtc_id) {
+               struct drm_crtc *crtc = NULL;
+
+               if (plane->crtc) {
+                       if (value == plane->crtc->base.id)
+                               return 0;
+               } else {
+                       if (value == 0)
+                               return 0;
+               }
+
+               if (value) {
+                       obj = drm_mode_object_find(plane->dev, value, 
DRM_MODE_OBJECT_CRTC);
+                       if (!obj) {
+                               printk("Unknown CRTC ID %llu\n", value);
+                               return -ENOENT;
+                       }
+                       crtc = obj_to_crtc(obj);
+               }
+
+               plane->crtc = crtc;
+       } else if (prop == prop_fb_id) {
+               struct drm_framebuffer *fb = NULL;
+
+               if (plane->fb) {
+                       if (value == plane->fb->base.id)
+                               return 0;
+               } else {
+                       if (value == 0)
+                               return 0;
+               }
+
+               if (value) {
+                       obj = drm_mode_object_find(plane->dev, value, 
DRM_MODE_OBJECT_FB);
+                       if (!obj) {
+                               printk("Unknown framebuffer ID %llu\n", value);
+                               return -ENOENT;
+                       }
+                       fb = obj_to_fb(obj);
+               }
+
+               plane->fb = fb;
+       } else
+               return -ENOENT;
+
+       state->dirty = true;
+       s->dirty = true;
+
+       return 0;
+}
+
+static int crtc_set(struct intel_atomic_state *s,
+                   struct intel_crtc_state *state,
+                   struct drm_property *prop,
+                   uint64_t value, void *blob_data)
+{
+       struct drm_crtc *crtc = state->crtc;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+       struct drm_mode_object *obj;
+
+       if (prop == prop_src_x) {
+               if (crtc->x == value)
+                       return 0;
+               crtc->x = value;
+               if (crtc_funcs->mode_set_base)
+                       state->fb_dirty = true;
+               else
+                       state->mode_dirty = true;
+       } else if (prop == prop_src_y) {
+               if (crtc->y == value)
+                       return 0;
+               crtc->y = value;
+               if (crtc_funcs->mode_set_base)
+                       state->fb_dirty = true;
+               else
+                       state->mode_dirty = true;
+       } else if (prop == prop_mode) {
+               struct drm_mode_modeinfo *umode = blob_data;
+               struct drm_display_mode *mode = NULL;
+
+               if (value != 0 && value != sizeof(*umode)) {
+                       DRM_DEBUG_KMS("Invalid mode length\n");
+                       return -EINVAL;
+               }
+
+               if (!crtc->enabled) {
+                       if (value == 0)
+                               return 0;
+               }
+
+               if (value) {
+                       int ret;
+
+                       mode = drm_mode_create(crtc->dev);
+                       if (!mode)
+                               return -ENOMEM;
+
+                       ret = drm_crtc_convert_umode(mode, umode);
+                       if (ret) {
+                               DRM_DEBUG_KMS("Invalid mode\n");
+                               drm_mode_debug_printmodeline(mode);
+                               drm_mode_destroy(crtc->dev, mode);
+                               return ret;
+                       }
+
+                       drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+               }
+
+               if (crtc->enabled && mode && drm_mode_equal(&crtc->mode, mode)) 
{
+                       drm_mode_destroy(crtc->dev, mode);
+                       return 0;
+               }
+
+               /* turn on/off or active area changed? */
+               if (!crtc->enabled || !mode ||
+                   crtc->mode.hdisplay != mode->hdisplay ||
+                   crtc->mode.vdisplay != mode->vdisplay)
+                       state->active_dirty = true;
+
+               if (mode) {
+                       crtc->mode = *mode;
+                       crtc->enabled = true;
+                       drm_mode_destroy(crtc->dev, mode);
+               } else
+                       crtc->enabled = false;
+               state->mode_dirty = true;
+       } else if (prop == prop_fb_id) {
+               struct drm_framebuffer *fb = NULL;
+
+               if (crtc->fb) {
+                       if (value == crtc->fb->base.id)
+                               return 0;
+               } else {
+                       if (value == 0)
+                               return 0;
+               }
+
+               if (value) {
+                       obj = drm_mode_object_find(crtc->dev, value, 
DRM_MODE_OBJECT_FB);
+                       if (!obj) {
+                               printk("Unknown framebuffer ID %llu\n", value);
+                               return -ENOENT;
+                       }
+                       fb = obj_to_fb(obj);
+               }
+
+               crtc->fb = fb;
+               if (crtc_funcs->mode_set_base)
+                       state->fb_dirty = true;
+               else
+                       state->mode_dirty = true;
+       } else if (prop == prop_connector_ids) {
+               const uint32_t *ids = blob_data;
+               uint32_t count_ids = value / sizeof(uint32_t);
+               int ret;
+
+               if (value & 3)
+                       return -EINVAL;
+
+               if (count_ids > crtc->dev->mode_config.num_connector)
+                       return -ERANGE;
+
+               ret = process_connectors(state, ids, count_ids);
+               if (ret)
+                       return ret;
+       } else if (prop == prop_cursor_id) {
+               if (intel_crtc->cursor_handle == value)
+                       return 0;
+               intel_crtc->cursor_handle = value;
+               state->cursor_dirty = true;
+       } else if (prop == prop_cursor_x) {
+               if (intel_crtc->cursor_x == value)
+                       return 0;
+               intel_crtc->cursor_x = value;
+               state->cursor_dirty = true;
+       } else if (prop == prop_cursor_y) {
+               if (intel_crtc->cursor_y == value)
+                       return 0;
+               intel_crtc->cursor_y = value;
+               state->cursor_dirty = true;
+       } else if (prop == prop_cursor_w) {
+               if (value != 0 && value != 64)
+                       return -EINVAL;
+               if (intel_crtc->cursor_width == value)
+                       return 0;
+               intel_crtc->cursor_width = value;
+               state->cursor_dirty = true;
+       } else if (prop == prop_cursor_h) {
+               if (value != 0 && value != 64)
+                       return -EINVAL;
+               if (intel_crtc->cursor_height == value)
+                       return 0;
+               intel_crtc->cursor_height = value;
+               state->cursor_dirty = true;
+       } else
+               return -ENOENT;
+
+       s->dirty = true;
+
+       return 0;
+}
+
+static struct intel_plane_state *get_plane_state(const struct drm_device *dev,
+                                                struct intel_atomic_state 
*state,
+                                                const struct drm_plane *plane)
+{
+       int i;
+
+       for (i = 0; i < dev->mode_config.num_plane; i++)
+               if (plane == state->plane[i].plane)
+                       return &state->plane[i];
+
+       return NULL;
+}
+
+static struct intel_crtc_state *get_crtc_state(const struct drm_device *dev,
+                                              struct intel_atomic_state *state,
+                                              const struct drm_crtc *crtc)
+{
+       int i;
+
+       for (i = 0; i < dev->mode_config.num_crtc; i++)
+               if (crtc == state->crtc[i].crtc)
+                       return &state->crtc[i];
+
+       return NULL;
+}
+
+static void crtc_prepare(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+       struct drm_encoder *encoder;
+
+       if (!crtc->enabled)
+               return;
+
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               const struct drm_encoder_helper_funcs *encoder_funcs = 
encoder->helper_private;
+
+               if (encoder->crtc != crtc)
+                       continue;
+
+               encoder_funcs->prepare(encoder);
+       }
+
+       drm_crtc_prepare_encoders(dev);
+
+       crtc_funcs->prepare(crtc);
+}
+
+static int crtc_set_base(struct drm_crtc *crtc)
+{
+       const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+
+       return crtc_funcs->mode_set_base_nopin(crtc, crtc->x, crtc->y);
+}
+
+static int crtc_mode_set(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+       struct drm_encoder *encoder;
+       int ret;
+
+       if (!crtc->enabled) {
+               crtc_funcs->disable_nopin(crtc);
+               return 0;
+       }
+
+       ret = crtc_funcs->mode_set_nopin(crtc, &crtc->mode, &crtc->hwmode, 
crtc->x, crtc->y);
+       if (ret)
+               return ret;
+
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               const struct drm_encoder_helper_funcs *encoder_funcs = 
encoder->helper_private;
+
+               if (encoder->crtc != crtc)
+                       continue;
+
+               encoder_funcs->mode_set(encoder, &crtc->mode, &crtc->hwmode);
+       }
+
+       return 0;
+}
+
+static void crtc_commit(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+       struct drm_encoder *encoder;
+
+       if (!crtc->enabled)
+               return;
+
+       crtc_funcs->commit(crtc);
+
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               const struct drm_encoder_helper_funcs *encoder_funcs = 
encoder->helper_private;
+
+               if (encoder->crtc != crtc)
+                       continue;
+
+               encoder_funcs->commit(encoder);
+       }
+}
+
+int intel_commit_plane(struct drm_plane *plane,
+                      struct drm_crtc *crtc,
+                      struct drm_framebuffer *fb,
+                      const struct intel_plane_coords *st,
+                      bool pin);
+
+static void unpin_cursors(struct drm_device *dev,
+                         struct intel_atomic_state *s)
+{
+       int i;
+
+       for (i = 0; i < dev->mode_config.num_crtc; i++) {
+               struct intel_crtc_state *st = &s->crtc[i];
+               struct drm_crtc *crtc = st->crtc;
+               struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+               if (!st->cursor_pinned)
+                       continue;
+
+               intel_crtc_cursor_bo_unref(crtc, intel_crtc->cursor_bo);
+
+               st->cursor_pinned = false;
+       }
+}
+
+static int pin_cursors(struct drm_device *dev,
+                       struct intel_atomic_state *s)
+{
+       int i, ret;
+
+       for (i = 0; i < dev->mode_config.num_crtc; i++) {
+               struct intel_crtc_state *st = &s->crtc[i];
+               struct drm_crtc *crtc = st->crtc;
+               struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+               if (!st->cursor_dirty)
+                       continue;
+
+               ret = intel_crtc_cursor_prepare(crtc, s->file,
+                                               intel_crtc->cursor_handle,
+                                               intel_crtc->cursor_width,
+                                               intel_crtc->cursor_height,
+                                               &intel_crtc->cursor_bo,
+                                               &intel_crtc->cursor_addr);
+               if (ret)
+                       goto unpin;
+
+               st->cursor_pinned = true;
+       }
+
+       return 0;
+
+unpin:
+       unpin_cursors(dev, s);
+
+       return ret;
+}
+
+static void unpin_old_cursors(struct drm_device *dev,
+                             struct intel_atomic_state *s)
+{
+       int i;
+
+       for (i = 0; i < dev->mode_config.num_crtc; i++) {
+               struct intel_crtc_state *st = &s->crtc[i];
+               struct drm_crtc *crtc = st->crtc;
+
+               if (!st->cursor_dirty)
+                       continue;
+
+               if (!st->old_cursor_bo)
+                       continue;
+
+               intel_crtc_cursor_bo_unref(crtc, st->old_cursor_bo);
+       }
+}
+
+static void unpin_fbs(struct drm_device *dev,
+                     struct intel_atomic_state *s)
+{
+       int i;
+
+       for (i = dev->mode_config.num_plane - 1; i >= 0; i--) {
+               struct intel_plane_state *st = &s->plane[i];
+               struct drm_plane *plane = st->plane;
+               struct drm_i915_gem_object *obj;
+
+               if (!st->pinned)
+                       continue;
+
+               obj = to_intel_framebuffer(plane->fb)->obj;
+
+               mutex_lock(&dev->struct_mutex);
+               intel_unpin_fb_obj(obj);
+               mutex_unlock(&dev->struct_mutex);
+
+               st->pinned = false;
+       }
+
+       for (i = dev->mode_config.num_crtc - 1; i >= 0; i--) {
+               struct intel_crtc_state *st = &s->crtc[i];
+               struct drm_crtc *crtc = st->crtc;
+               struct drm_i915_gem_object *obj;
+
+               if (!st->pinned)
+                       continue;
+
+               obj = to_intel_framebuffer(crtc->fb)->obj;
+
+               mutex_lock(&dev->struct_mutex);
+               intel_unpin_fb_obj(obj);
+               mutex_unlock(&dev->struct_mutex);
+
+               st->pinned = false;
+       }
+}
+
+static int pin_fbs(struct drm_device *dev,
+                  struct intel_atomic_state *s)
+{
+       int i, ret;
+
+       for (i = 0; i < dev->mode_config.num_crtc; i++) {
+               struct intel_crtc_state *st = &s->crtc[i];
+               struct drm_crtc *crtc = st->crtc;
+               struct drm_i915_gem_object *obj;
+
+               if (!st->fb_dirty)
+                       continue;
+
+               if (!crtc->fb)
+                       continue;
+
+               obj = to_intel_framebuffer(crtc->fb)->obj;
+
+               mutex_lock(&dev->struct_mutex);
+               ret = intel_pin_and_fence_fb_obj(dev, obj, NULL);
+               mutex_unlock(&dev->struct_mutex);
+
+               if (ret)
+                       goto unpin;
+
+               st->pinned = true;
+       }
+
+       for (i = 0; i < dev->mode_config.num_plane; i++) {
+               struct intel_plane_state *st = &s->plane[i];
+               struct drm_plane *plane = st->plane;
+               struct drm_i915_gem_object *obj;
+
+               if (!st->dirty)
+                       continue;
+
+               if (!plane->fb)
+                       continue;
+
+               obj = to_intel_framebuffer(plane->fb)->obj;
+
+               mutex_lock(&dev->struct_mutex);
+               ret = intel_pin_and_fence_fb_obj(dev, obj, NULL);
+               mutex_unlock(&dev->struct_mutex);
+
+               if (ret)
+                       goto unpin;
+
+               st->pinned = true;
+       }
+
+       return 0;
+
+ unpin:
+       unpin_fbs(dev, s);
+
+       return ret;
+}
+
+static void unpin_old_fbs(struct drm_device *dev,
+                         struct intel_atomic_state *s)
+{
+       int i;
+
+       for (i = 0; i < dev->mode_config.num_crtc; i++) {
+               struct intel_crtc_state *st = &s->crtc[i];
+               struct drm_i915_gem_object *obj;
+
+               if (!st->fb_dirty)
+                       continue;
+
+               if (!st->old_fb)
+                       continue;
+
+               obj = to_intel_framebuffer(st->old_fb)->obj;
+
+               mutex_lock(&dev->struct_mutex);
+               intel_unpin_fb_obj(obj);
+               mutex_unlock(&dev->struct_mutex);
+       }
+
+       for (i = 0; i < dev->mode_config.num_plane; i++) {
+               struct intel_plane_state *st = &s->plane[i];
+               struct drm_i915_gem_object *obj;
+
+               if (!st->dirty)
+                       continue;
+
+               if (!st->old_fb)
+                       continue;
+
+               obj = to_intel_framebuffer(st->old_fb)->obj;
+
+               mutex_lock(&dev->struct_mutex);
+               intel_unpin_fb_obj(obj);
+               mutex_unlock(&dev->struct_mutex);
+       }
+}
+
+static void update_plane_obj(struct drm_device *dev,
+                            struct intel_atomic_state *s)
+{
+       int i;
+
+       for (i = 0; i < dev->mode_config.num_plane; i++) {
+               struct intel_plane_state *st = &s->plane[i];
+               struct drm_plane *plane = st->plane;
+               struct intel_plane *intel_plane = to_intel_plane(plane);
+
+               if (!st->dirty)
+                       continue;
+
+               if (plane->fb)
+                       intel_plane->obj = to_intel_framebuffer(plane->fb)->obj;
+               else
+                       intel_plane->obj = NULL;
+       }
+}
+
+void _intel_disable_plane(struct drm_plane *plane, bool unpin);
+
+static int apply_config(struct drm_device *dev,
+                       struct intel_atomic_state *s)
+{
+       int i, ret;
+       struct drm_plane *plane;
+
+       for (i = 0; i < dev->mode_config.num_crtc; i++) {
+               struct intel_crtc_state *st = &s->crtc[i];
+
+               mutex_lock(&dev->struct_mutex);
+
+               if (st->mode_dirty) {
+                       /* wait for pending MI_WAIT_FOR_EVENTs */
+                       if (st->old_fb)
+                               intel_finish_fb(st->old_fb);
+               }
+
+               if (st->fb_dirty) {
+                       /* wait for pending page flips */
+                       if (st->crtc->fb)
+                               intel_finish_fb(st->crtc->fb);
+               }
+
+               mutex_unlock(&dev->struct_mutex);
+
+               if (!st->mode_dirty)
+                       continue;
+
+               crtc_prepare(st->crtc);
+       }
+
+       for (i = 0; i < dev->mode_config.num_crtc; i++) {
+               struct intel_crtc_state *st = &s->crtc[i];
+               struct intel_crtc *intel_crtc = to_intel_crtc(st->crtc);
+               int j;
+
+               if (st->mode_dirty) {
+                       ret = crtc_mode_set(st->crtc);
+                       if (ret)
+                               return ret;
+               } else if (st->fb_dirty) {
+                       ret = crtc_set_base(st->crtc);
+                       if (ret)
+                               return ret;
+               }
+
+               if (st->cursor_dirty)
+                       intel_crtc_cursor_commit(st->crtc,
+                                                intel_crtc->cursor_handle,
+                                                intel_crtc->cursor_width,
+                                                intel_crtc->cursor_height,
+                                                intel_crtc->cursor_bo,
+                                                intel_crtc->cursor_addr);
+
+               for (j = 0; j < dev->mode_config.num_plane; j++) {
+                       struct intel_plane_state *pst = &s->plane[j];
+                       struct drm_plane *plane = pst->plane;
+
+                       if (!pst->dirty)
+                               continue;
+
+                       if (plane->crtc != st->crtc)
+                               continue;
+
+                       ret = intel_commit_plane(plane, plane->crtc, plane->fb, 
&pst->coords, false);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       for (i = 0; i < dev->mode_config.num_crtc; i++) {
+               struct intel_crtc_state *st = &s->crtc[i];
+
+               if (!st->mode_dirty)
+                       continue;
+
+               crtc_commit(st->crtc);
+       }
+
+       /*
+        * FIXME perhaps better order would be
+        * 1. prepare all current objects
+        * 2. disable unused objects
+        * 3. set mode for current objects
+        * 4. commit current objects
+        */
+       drm_helper_disable_unused_functions(dev);
+       list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+               /* planes attached to crtcs already handled in mode_set() */
+               if (!plane->crtc || !plane->fb)
+                       _intel_disable_plane(plane, false);
+       }
+
+       /* don't restore the old state in end() */
+       s->dirty = false;
+
+       return 0;
+}
+
+static void restore_state(struct drm_device *dev,
+                         struct intel_atomic_state *s)
+{
+       int i;
+       struct drm_connector *connector;
+       struct drm_encoder *encoder;
+       struct drm_crtc *crtc;
+       struct drm_plane *plane;
+
+       i = 0;
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+               *connector = s->saved_connectors[i++];
+       i = 0;
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+               *encoder = s->saved_encoders[i++];
+       i = 0;
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+               *intel_crtc = s->saved_crtcs[i++];
+       }
+       i = 0;
+       list_for_each_entry(plane, &dev->mode_config.plane_list, head)
+               *plane = s->saved_planes[i++];
+
+       /* FIXME props etc. */
+
+       /* was the hardware state clobbered? */
+       if (s->restore_hw)
+               apply_config(dev, s);
+}
+
+static int intel_atomic_set(struct drm_device *dev, void *state,
+                           struct drm_mode_object *obj,
+                           struct drm_property *prop,
+                           uint64_t value, void *blob_data)
+{
+       struct intel_atomic_state *s = state;
+       int ret = -EINVAL;
+
+       switch (obj->type) {
+       case DRM_MODE_OBJECT_PLANE:
+               ret = plane_set(s, get_plane_state(dev, s, obj_to_plane(obj)), 
prop, value);
+               break;
+       case DRM_MODE_OBJECT_CRTC:
+               ret = crtc_set(s, get_crtc_state(dev, s, obj_to_crtc(obj)), 
prop, value, blob_data);
+               break;
+       default:
+               break;
+       }
+
+       kfree(blob_data);
+
+       return ret;
+}
+
+int intel_check_plane(const struct drm_plane *plane,
+                     const struct drm_crtc *crtc,
+                     const struct drm_framebuffer *fb,
+                     struct intel_plane_coords *st);
+
+static void dirty_planes(const struct drm_device *dev,
+                        struct intel_atomic_state *state,
+                        const struct drm_crtc *crtc)
+{
+       int i;
+
+       for (i = 0; i < dev->mode_config.num_plane; i++) {
+               struct intel_plane_state *s = &state->plane[i];
+
+               if (s->plane->crtc == crtc)
+                       s->dirty = true;
+       }
+}
+
+static int check_crtc(struct intel_crtc_state *s)
+{
+       struct drm_crtc *crtc = s->crtc;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct drm_encoder *encoder;
+       struct drm_framebuffer *fb = crtc->fb;
+       struct drm_display_mode mode, adjusted_mode;
+       const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+       int ret;
+
+       /* must have a fb and connectors if we have a mode, and vice versa */
+       if (crtc->enabled) {
+               if (!fb)
+                       return -EINVAL;
+               if (!drm_helper_crtc_in_use(crtc))
+                       return -EINVAL;
+       } else {
+               if (fb)
+                       return -EINVAL;
+               if (drm_helper_crtc_in_use(crtc))
+                       return -EINVAL;
+       }
+
+       if (crtc->enabled) {
+               if (crtc->mode.hdisplay > fb->width ||
+                   crtc->mode.vdisplay > fb->height ||
+                   crtc->x > fb->width - crtc->mode.hdisplay ||
+                   crtc->y > fb->height - crtc->mode.vdisplay)
+                       return -ENOSPC;
+       }
+
+       if (intel_crtc->cursor_visible &&
+           (intel_crtc->cursor_width != 64 ||
+            intel_crtc->cursor_height != 64)) {
+               DRM_DEBUG_KMS("only 64x64 cursor sprites are supported\n");
+               return -EINVAL;
+       }
+
+       if (!crtc->enabled || !s->mode_dirty)
+               return 0;
+
+       mode = adjusted_mode = crtc->mode;
+
+       ret = intel_check_clock(dev, crtc, mode.clock);
+       if (ret)
+               return ret;
+
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               const struct drm_encoder_helper_funcs *encoder_funcs = 
encoder->helper_private;
+
+               if (!encoder_funcs->mode_fixup(encoder, &mode, &adjusted_mode))
+                       return -EINVAL;
+       }
+
+       if (!crtc_funcs->mode_fixup(crtc, &mode, &adjusted_mode))
+               return -EINVAL;
+
+       crtc->hwmode = adjusted_mode;
+
+       return 0;
+}
+
+static int intel_atomic_check(struct drm_device *dev, void *state)
+{
+       struct intel_atomic_state *s = state;
+       int ret;
+       int i;
+
+       if (!s->dirty)
+               return 0;
+
+       for (i = 0; i < dev->mode_config.num_crtc; i++) {
+               struct intel_crtc_state *st = &s->crtc[i];
+
+               if (!st->fb_dirty && !st->mode_dirty && !st->cursor_dirty)
+                       continue;
+
+               if (st->mode_dirty && s->flags & DRM_MODE_ATOMIC_NONBLOCK)
+                       return -EAGAIN;
+
+               ret = check_crtc(st);
+               if (ret)
+                       return ret;
+
+               /*
+                * Mark all planes on this CRTC as dirty if the active video
+                * area changed so that the planes will get reclipped correctly.
+                *
+                * Also any modesetting will disable+enable the pipe, so the
+                * plane needs to be re-enabled afterwards too.
+                * TODO: there's no need to redo the clipping in such cases
+                * if the computed values were cached, the could be commited
+                * directly.
+                */
+               if (st->active_dirty || st->mode_dirty)
+                       dirty_planes(dev, s, st->crtc);
+       }
+
+       /* check for conflicts in encoder/connector assignment */
+       for (i = 0; i < dev->mode_config.num_crtc; i++) {
+               struct intel_crtc_state *st = &s->crtc[i];
+               int j;
+
+               for (j = i + 1; j < dev->mode_config.num_crtc; j++) {
+                       struct intel_crtc_state *st2 = &s->crtc[j];
+
+                       if (st->connectors_bitmask & st2->connectors_bitmask)
+                               return -EINVAL;
+
+                       if (st->encoders_bitmask & st2->encoders_bitmask)
+                               return -EINVAL;
+               }
+       }
+
+       for (i = 0; i < dev->mode_config.num_plane; i++) {
+               struct intel_plane_state *st = &s->plane[i];
+               const struct drm_plane *plane = st->plane;
+
+               if (!st->dirty)
+                       continue;
+
+               st->coords.crtc_x = plane->crtc_x;
+               st->coords.crtc_y = plane->crtc_y;
+               st->coords.crtc_w = plane->crtc_w;
+               st->coords.crtc_h = plane->crtc_h;
+
+               st->coords.src_x = plane->src_x;
+               st->coords.src_y = plane->src_y;
+               st->coords.src_w = plane->src_w;
+               st->coords.src_h = plane->src_h;
+
+               ret = intel_check_plane(plane, plane->crtc, plane->fb, 
&st->coords);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static void update_plane_props(struct drm_plane *plane)
+{
+       struct drm_mode_object *obj = &plane->base;
+
+       drm_object_property_set_value(obj, prop_src_x, plane->src_x);
+       drm_object_property_set_value(obj, prop_src_y, plane->src_y);
+       drm_object_property_set_value(obj, prop_src_w, plane->src_w);
+       drm_object_property_set_value(obj, prop_src_h, plane->src_h);
+
+       drm_object_property_set_value(obj, prop_crtc_x, plane->crtc_x);
+       drm_object_property_set_value(obj, prop_crtc_y, plane->crtc_y);
+       drm_object_property_set_value(obj, prop_crtc_w, plane->crtc_w);
+       drm_object_property_set_value(obj, prop_crtc_h, plane->crtc_h);
+
+       drm_object_property_set_value(obj, prop_fb_id, plane->fb ? 
plane->fb->base.id : 0);
+       drm_object_property_set_value(obj, prop_crtc_id, plane->crtc ? 
plane->crtc->base.id : 0);
+}
+
+static int update_connector_ids(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_connector *connector;
+       uint64_t value = 0;
+       int i = 0;
+       uint32_t connector_ids[dev->mode_config.num_connector];
+
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               if (connector->encoder && connector->encoder->crtc == crtc)
+                       connector_ids[i++] = connector->base.id;
+       }
+
+       if (i) {
+               drm_property_blob_replace_data(crtc->connector_ids_blob,
+                                              i * sizeof connector_ids[0], 
connector_ids);
+               value = crtc->connector_ids_blob->base.id;
+       } else
+               drm_property_blob_replace_data(crtc->connector_ids_blob, 0, 
NULL);
+
+       drm_object_property_set_value(&crtc->base, prop_connector_ids, value);
+
+       return 0;
+}
+
+static int update_mode(struct drm_crtc *crtc)
+{
+       uint64_t value = 0;
+
+       if (crtc->enabled) {
+               struct drm_mode_modeinfo umode;
+
+               drm_crtc_convert_to_umode(&umode, &crtc->mode);
+               drm_property_blob_replace_data(crtc->mode_blob, sizeof umode, 
&umode);
+               value = crtc->mode_blob->base.id;
+       } else
+               drm_property_blob_replace_data(crtc->mode_blob, 0, NULL);
+
+       drm_object_property_set_value(&crtc->base, prop_mode, value);
+
+       return 0;
+}
+
+static void update_crtc_props(struct drm_crtc *crtc)
+{
+       struct drm_mode_object *obj = &crtc->base;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+       drm_object_property_set_value(obj, prop_src_x, crtc->x);
+       drm_object_property_set_value(obj, prop_src_y, crtc->y);
+
+       drm_object_property_set_value(obj, prop_fb_id, crtc->fb ? 
crtc->fb->base.id : 0);
+
+       drm_object_property_set_value(obj, prop_cursor_id,
+                                     intel_crtc->cursor_handle);
+       drm_object_property_set_value(obj, prop_cursor_x, intel_crtc->cursor_x);
+       drm_object_property_set_value(obj, prop_cursor_y, intel_crtc->cursor_y);
+       drm_object_property_set_value(obj, prop_cursor_w,
+                                     intel_crtc->cursor_width);
+       drm_object_property_set_value(obj, prop_cursor_h,
+                                     intel_crtc->cursor_height);
+
+       update_mode(crtc);
+       update_connector_ids(crtc);
+}
+
+static void update_props(struct drm_device *dev,
+                        struct intel_atomic_state *s)
+{
+       int i;
+
+       for (i = 0; i < dev->mode_config.num_crtc; i++) {
+               struct intel_crtc_state *st = &s->crtc[i];
+
+               if (!st->fb_dirty && !st->mode_dirty)
+                       continue;
+
+               update_crtc_props(st->crtc);
+       }
+
+       for (i = 0; i < dev->mode_config.num_plane; i++) {
+               struct intel_plane_state *st = &s->plane[i];
+
+               if (!st->dirty)
+                       continue;
+
+               update_plane_props(st->plane);
+       }
+}
+
+static int intel_atomic_commit(struct drm_device *dev, void *state)
+{
+       struct intel_atomic_state *s = state;
+       int ret;
+
+       if (s->flags & DRM_MODE_ATOMIC_NONBLOCK)
+               return -ENOSYS;
+
+       if (!s->dirty)
+               return 0;
+
+       ret = pin_fbs(dev, s);
+       if (ret)
+               return ret;
+
+       ret = pin_cursors(dev, s);
+       if (ret)
+               return ret;
+
+       /* apply in a blocking manner */
+       ret = apply_config(dev, s);
+       if (ret) {
+               unpin_cursors(dev, s);
+               unpin_fbs(dev, s);
+               s->restore_hw = true;
+               return ret;
+       }
+
+       unpin_old_cursors(dev, s);
+       unpin_old_fbs(dev, s);
+
+       update_plane_obj(dev, s);
+
+       update_props(dev, s);
+
+       return 0;
+}
+
+static void intel_atomic_end(struct drm_device *dev, void *state)
+{
+       struct intel_atomic_state *s = state;
+
+       /* restore the state of all objects */
+       if (s->dirty)
+               restore_state(dev, state);
+
+       kfree(state);
+}
+
+static const struct drm_atomic_funcs intel_atomic_funcs = {
+       .begin = intel_atomic_begin,
+       .set = intel_atomic_set,
+       .check = intel_atomic_check,
+       .commit = intel_atomic_commit,
+       .end = intel_atomic_end,
+};
+
+static struct {
+       struct drm_property **prop;
+       const char *name;
+       uint64_t min;
+       uint64_t max;
+} props[] = {
+       { &prop_src_x, "SRC_X", 0, UINT_MAX },
+       { &prop_src_y, "SRC_Y", 0, UINT_MAX },
+       { &prop_src_w, "SRC_W", 0, UINT_MAX },
+       { &prop_src_h, "SRC_H", 0, UINT_MAX },
+
+       { &prop_crtc_x, "CRTC_X", INT_MIN, INT_MAX },
+       { &prop_crtc_y, "CRTC_Y", INT_MIN, INT_MAX },
+       { &prop_crtc_w, "CRTC_W", 0, INT_MAX },
+       { &prop_crtc_h, "CRTC_H", 0, INT_MAX },
+
+       { &prop_fb_id, "FB_ID", 0, UINT_MAX },
+       { &prop_crtc_id, "CRTC_ID", 0, UINT_MAX },
+
+       { &prop_cursor_id, "CURSOR_ID", 0, UINT_MAX },
+       { &prop_cursor_w, "CURSOR_W", 0, UINT_MAX },
+       { &prop_cursor_h, "CURSOR_H", 0, UINT_MAX },
+       { &prop_cursor_x, "CURSOR_X", INT_MIN, INT_MAX },
+       { &prop_cursor_y, "CURSOR_Y", INT_MIN, INT_MAX },
+};
+
+int intel_atomic_init(struct drm_device *dev)
+{
+       struct drm_crtc *crtc;
+       struct drm_plane *plane;
+       int ret = -ENOMEM;
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(props); i++) {
+               *props[i].prop =
+                       drm_property_create_range(dev, 0, props[i].name,
+                                                 props[i].min, props[i].max);
+               if (!*props[i].prop)
+                       goto out;
+       }
+
+       /* FIXME create special object ID list property type? */
+       prop_connector_ids = drm_property_create(dev, DRM_MODE_PROP_BLOB, 
"CONNECTOR_IDS", 0);
+       if (!prop_connector_ids)
+               goto out;
+
+       prop_mode = drm_property_create(dev, DRM_MODE_PROP_BLOB, "MODE", 0);
+       if (!prop_mode)
+               goto out;
+
+       list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+               struct drm_mode_object *obj = &plane->base;
+
+               drm_object_attach_property(obj, prop_src_x, 0);
+               drm_object_attach_property(obj, prop_src_y, 0);
+               drm_object_attach_property(obj, prop_src_w, 0);
+               drm_object_attach_property(obj, prop_src_h, 0);
+
+               drm_object_attach_property(obj, prop_crtc_x, 0);
+               drm_object_attach_property(obj, prop_crtc_y, 0);
+               drm_object_attach_property(obj, prop_crtc_w, 0);
+               drm_object_attach_property(obj, prop_crtc_h, 0);
+
+               drm_object_attach_property(obj, prop_fb_id, 0);
+               drm_object_attach_property(obj, prop_crtc_id, 0);
+
+               update_plane_props(plane);
+       }
+
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               struct drm_mode_object *obj = &crtc->base;
+
+               drm_object_attach_property(obj, prop_src_x, 0);
+               drm_object_attach_property(obj, prop_src_y, 0);
+
+               drm_object_attach_property(obj, prop_fb_id, 0);
+               drm_object_attach_property(obj, prop_mode, 0);
+               drm_object_attach_property(obj, prop_connector_ids, 0);
+
+               drm_object_attach_property(obj, prop_cursor_id, 0);
+               drm_object_attach_property(obj, prop_cursor_x, 0);
+               drm_object_attach_property(obj, prop_cursor_y, 0);
+               drm_object_attach_property(obj, prop_cursor_w, 0);
+               drm_object_attach_property(obj, prop_cursor_h, 0);
+
+               crtc->mode_blob = drm_property_create_blob(dev, 0, 
sizeof(struct drm_mode_modeinfo), NULL);
+               if (!crtc->mode_blob)
+                       goto out;
+
+               crtc->connector_ids_blob = drm_property_create_blob(dev, 0,
+                                                                   
dev->mode_config.num_connector * sizeof(uint32_t), NULL);
+               if (!crtc->connector_ids_blob)
+                       goto out;
+
+               update_crtc_props(crtc);
+       }
+
+       dev->driver->atomic_funcs = &intel_atomic_funcs;
+
+       return 0;
+
+ out:
+       drm_property_destroy(dev, prop_mode);
+       drm_property_destroy(dev, prop_connector_ids);
+
+       while (i--)
+               drm_property_destroy(dev, *props[i].prop);
+
+       return ret;
+}
+
+void intel_atomic_fini(struct drm_device *dev)
+{
+       struct drm_crtc *crtc;
+
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               drm_property_destroy_blob(dev, crtc->mode_blob);
+               drm_property_destroy_blob(dev, crtc->connector_ids_blob);
+       }
+
+       drm_property_destroy(dev, prop_connector_ids);
+       drm_property_destroy(dev, prop_mode);
+       drm_property_destroy(dev, prop_crtc_id);
+       drm_property_destroy(dev, prop_fb_id);
+
+       drm_property_destroy(dev, prop_crtc_h);
+       drm_property_destroy(dev, prop_crtc_w);
+       drm_property_destroy(dev, prop_crtc_y);
+       drm_property_destroy(dev, prop_crtc_x);
+
+       drm_property_destroy(dev, prop_src_h);
+       drm_property_destroy(dev, prop_src_w);
+       drm_property_destroy(dev, prop_src_y);
+       drm_property_destroy(dev, prop_src_x);
+}
diff --git a/drivers/gpu/drm/i915/intel_display.c 
b/drivers/gpu/drm/i915/intel_display.c
index 592065e..d6a3e51 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -43,6 +43,9 @@
 
 #define HAS_eDP (intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))
 
+void intel_atomic_init(struct drm_device *dev);
+void intel_atomic_fini(struct drm_device *dev);
+
 bool intel_pipe_has_type(struct drm_crtc *crtc, int type);
 static void intel_increase_pllclock(struct drm_crtc *crtc);
 static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on);
@@ -7014,6 +7017,8 @@ static void intel_setup_outputs(struct drm_device *dev)
                        intel_encoder_clones(dev, encoder->clone_mask);
        }
 
+       intel_atomic_init(dev);
+
        /* disable all the possible outputs/crtcs before entering KMS mode */
        drm_helper_disable_unused_functions(dev);
 
@@ -7484,6 +7489,8 @@ void intel_modeset_cleanup(struct drm_device *dev)
        del_timer_sync(&dev_priv->idle_timer);
        cancel_work_sync(&dev_priv->idle_work);
 
+       intel_atomic_fini(dev);
+
        drm_mode_config_cleanup(dev);
 }
 
-- 
1.7.8.6

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to