[PATCH 45/51] drm/i915: Implement atomic modesetting

ville.syrjala at linux.intel.com ville.syrjala at linux.intel.com
Thu Oct 25 11:05:48 PDT 2012


From: Ville Syrjälä <ville.syrjala at linux.intel.com>

Implement the mandatory hooks for the atomic modeset ioctl.

The code first makes a backup of the current state, then proceeds to
modify the state as properties are modified. After all the properties
have been handled the new state is checked, and if everything checks
out, the new state is commited to hardware. Finally we clean up any
temporary storage.

In theory everything is checked before the hardware state is clobbered,
so there should be no need for rollback. But as the current modesetting
code can still return errors from some of of the operation, the rollback
code is included.

Signed-off-by: Ville Syrjälä <ville.syrjala at linux.intel.com>
---
 drivers/gpu/drm/i915/Makefile        |    1 +
 drivers/gpu/drm/i915/intel_atomic.c  | 1625 ++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_display.c |    5 +
 drivers/gpu/drm/i915/intel_drv.h     |    3 +
 include/drm/drm_crtc.h               |    2 +
 5 files changed, 1636 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 0f2c549..cca14e4 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..4899f8c
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_atomic.c
@@ -0,0 +1,1625 @@
+/*
+ * Copyright (C) 2011-2012 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ * Ville Syrjälä <ville.syrjala at linux.intel.com>
+ */
+
+#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 intel_plane_coords coords;
+	bool dirty;
+	bool pinned;
+	bool changed;
+
+	struct {
+		struct drm_crtc *crtc;
+		struct drm_framebuffer *fb;
+		uint32_t src_x, src_y, src_w, src_h;
+		int32_t crtc_x, crtc_y;
+		uint32_t crtc_w, crtc_h;
+	} old;
+};
+
+struct intel_crtc_state {
+	struct drm_crtc *crtc;
+	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;
+	bool changed;
+
+	struct {
+		bool enabled;
+		struct drm_display_mode mode;
+		struct drm_framebuffer *fb;
+		int x, y;
+		unsigned long connectors_bitmask;
+		unsigned long encoders_bitmask;
+
+		struct drm_i915_gem_object *cursor_bo;
+		uint32_t cursor_handle;
+		int16_t cursor_x, cursor_y;
+		int16_t cursor_width, cursor_height;
+		bool cursor_visible;
+	} old;
+};
+
+struct intel_atomic_state {
+	struct drm_file *file;
+	struct intel_plane_state *plane;
+	struct intel_crtc_state *crtc;
+	bool dirty;
+	bool restore_state;
+	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 drm_crtc *crtc,
+				      unsigned long *connectors_bitmask)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_connector *connector;
+	unsigned int i = 0;
+
+	*connectors_bitmask = 0;
+
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		if (connector->encoder && connector->encoder->crtc == crtc)
+			__set_bit(i, connectors_bitmask);
+
+		i++;
+	}
+}
+
+static void update_encoders_bitmask(struct drm_crtc *crtc,
+				    unsigned long *encoders_bitmask)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_encoder *encoder;
+	unsigned int i = 0;
+
+	*encoders_bitmask = 0;
+
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		if (encoder->crtc == crtc)
+			__set_bit(i, 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) {
+			DRM_DEBUG_KMS("Unknown connector ID %u\n", ids[i]);
+			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 set to drive it */
+			if (connector->encoder && connector->encoder->crtc == crtc)
+				connector->encoder = NULL;
+			continue;
+		}
+
+		encoder = connector_funcs->best_encoder(connector);
+
+		connector->encoder = encoder;
+		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->crtc, &s->connectors_bitmask);
+	update_encoders_bitmask(s->crtc, &s->encoders_bitmask);
+
+	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 populate_old(struct drm_device *dev)
+{
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+		encoder->old_crtc = encoder->crtc;
+
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+		connector->old_encoder = connector->encoder;
+}
+
+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);
+
+	populate_old(dev);
+
+	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.cursor_bo = intel_crtc->cursor_bo;
+		s->old.cursor_x = intel_crtc->cursor_x;
+		s->old.cursor_y = intel_crtc->cursor_y;
+		s->old.cursor_width = intel_crtc->cursor_width;
+		s->old.cursor_height = intel_crtc->cursor_height;
+		s->old.cursor_visible = intel_crtc->cursor_visible;
+
+		/* save current config */
+		s->old.enabled = crtc->enabled;
+		s->old.mode = crtc->mode;
+		s->old.fb = crtc->fb;
+		s->old.x = crtc->x;
+		s->old.y = crtc->y;
+
+		update_connectors_bitmask(crtc, &s->connectors_bitmask);
+		update_encoders_bitmask(crtc, &s->encoders_bitmask);
+
+		s->old.connectors_bitmask = s->connectors_bitmask;
+		s->old.encoders_bitmask = s->encoders_bitmask;
+	}
+
+	i = 0;
+	list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+		struct intel_plane_state *s = &state->plane[i++];
+
+		s->plane = plane;
+
+		/* save current config */
+		s->old.crtc = plane->crtc;
+		s->old.fb = plane->fb;
+		s->old.src_x = plane->src_x;
+		s->old.src_y = plane->src_y;
+		s->old.src_w = plane->src_w;
+		s->old.src_h = plane->src_h;
+		s->old.crtc_x = plane->crtc_x;
+		s->old.crtc_y = plane->crtc_y;
+		s->old.crtc_w = plane->crtc_w;
+		s->old.crtc_h = plane->crtc_h;
+	}
+
+	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;
+
+	state->changed = true;
+
+	if (prop == prop_src_x) {
+		plane->src_x = value;
+	} else if (prop == prop_src_y) {
+		plane->src_y = value;
+	} else if (prop == prop_src_w) {
+		plane->src_w = value;
+	} else if (prop == prop_src_h) {
+		plane->src_h = value;
+	} else if (prop == prop_crtc_x) {
+		plane->crtc_x = value;
+	} else if (prop == prop_crtc_y) {
+		plane->crtc_y = value;
+	} else if (prop == prop_crtc_w) {
+		plane->crtc_w = value;
+	} else if (prop == prop_crtc_h) {
+		plane->crtc_h = value;
+	} else if (prop == prop_crtc_id) {
+		if (value) {
+			obj = drm_mode_object_find(plane->dev, value, DRM_MODE_OBJECT_CRTC);
+			if (!obj) {
+				DRM_DEBUG_KMS("Unknown CRTC ID %llu\n", value);
+				return -ENOENT;
+			}
+			plane->crtc = obj_to_crtc(obj);
+		} else
+			plane->crtc = NULL;
+	} else if (prop == prop_fb_id) {
+		if (value) {
+			obj = drm_mode_object_find(plane->dev, value, DRM_MODE_OBJECT_FB);
+			if (!obj) {
+				DRM_DEBUG_KMS("Unknown framebuffer ID %llu\n", value);
+				return -ENOENT;
+			}
+			plane->fb = obj_to_fb(obj);
+		} else
+			plane->fb = NULL;
+	} else
+		return -ENOENT;
+
+	s->restore_state = true;
+
+	return 0;
+}
+
+static int crtc_set(struct intel_atomic_state *s,
+		    struct intel_crtc_state *state,
+		    struct drm_property *prop,
+		    uint64_t value, const void *blob_data)
+{
+	struct drm_crtc *crtc = state->crtc;
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+	struct drm_mode_object *obj;
+
+	state->changed = true;
+
+	if (prop == prop_src_x) {
+		crtc->x = value;
+	} else if (prop == prop_src_y) {
+		crtc->y = value;
+	} else if (prop == prop_mode) {
+		const struct drm_mode_modeinfo *umode = blob_data;
+
+		if (value != 0 && value != sizeof *umode) {
+			DRM_DEBUG_KMS("Invalid mode length\n");
+			return -EINVAL;
+		}
+
+		if (value) {
+			struct drm_display_mode *mode;
+			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);
+
+			crtc->mode = *mode;
+			crtc->enabled = true;
+			drm_mode_destroy(crtc->dev, mode);
+		} else
+			crtc->enabled = false;
+	} else if (prop == prop_fb_id) {
+		if (value) {
+			obj = drm_mode_object_find(crtc->dev, value, DRM_MODE_OBJECT_FB);
+			if (!obj) {
+				DRM_DEBUG_KMS("Unknown framebuffer ID %llu\n", value);
+				return -ENOENT;
+			}
+			crtc->fb = obj_to_fb(obj);
+		} else
+			crtc->fb = NULL;
+	} else if (prop == prop_connector_ids) {
+		const uint32_t *ids = blob_data;
+		uint64_t count_ids = value / sizeof(uint32_t);
+		int ret;
+
+		if (value & 3) {
+			DRM_DEBUG_KMS("Invalid connectors length\n");
+			return -EINVAL;
+		}
+
+		if (count_ids > crtc->dev->mode_config.num_connector) {
+			DRM_DEBUG_KMS("Too many connectors specified\n");
+			return -ERANGE;
+		}
+
+		ret = process_connectors(state, ids, count_ids);
+		if (ret)
+			return ret;
+	} else if (prop == prop_cursor_id) {
+		intel_crtc->cursor_handle = value;
+	} else if (prop == prop_cursor_x) {
+		intel_crtc->cursor_x = value;
+	} else if (prop == prop_cursor_y) {
+		intel_crtc->cursor_y = value;
+	} else if (prop == prop_cursor_w) {
+		if (value != 0 && value != 64) {
+			DRM_DEBUG_KMS("only 64x64 cursor sprites are supported\n");
+			return -EINVAL;
+		}
+		intel_crtc->cursor_width = value;
+	} else if (prop == prop_cursor_h) {
+		if (value != 0 && value != 64) {
+			DRM_DEBUG_KMS("only 64x64 cursor sprites are supported\n");
+			return -EINVAL;
+		}
+		intel_crtc->cursor_height = value;
+	} else
+		return -ENOENT;
+
+	s->restore_state = true;
+
+	return 0;
+}
+
+static void crtc_compute_dirty(struct intel_atomic_state *s,
+			       struct intel_crtc_state *state)
+{
+	struct drm_crtc *crtc = state->crtc;
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+	/* if no properties were specified nothing is dirty */
+	if (!state->changed)
+		return;
+
+	/* fb/pan changed? */
+	if (crtc->x != state->old.x ||
+	    crtc->y != state->old.y ||
+	    crtc->fb != state->old.fb) {
+		/* FIXME do we need a full modeset sometimes? */
+		state->fb_dirty = true;
+	}
+
+	/* enabled <-> disabled? */
+	if (crtc->enabled != state->old.enabled) {
+		state->mode_dirty = true;
+		state->active_dirty = true;
+	}
+
+	/* mode changed? */
+	if (crtc->enabled && state->old.enabled &&
+	    !drm_mode_equal(&crtc->mode, &state->old.mode)) {
+		state->mode_dirty = true;
+
+		if (crtc->mode.hdisplay != state->old.mode.hdisplay ||
+		    crtc->mode.vdisplay != state->old.mode.vdisplay)
+			state->active_dirty = true;
+	}
+
+	/* connectors changed? */
+	if (state->connectors_bitmask != state->old.connectors_bitmask ||
+	    state->encoders_bitmask != state->old.encoders_bitmask)
+		state->mode_dirty = true;
+
+	/* cursor changed? */
+	if (intel_crtc->cursor_handle != state->old.cursor_handle ||
+	    intel_crtc->cursor_x != state->old.cursor_x ||
+	    intel_crtc->cursor_y != state->old.cursor_y ||
+	    intel_crtc->cursor_width != state->old.cursor_width ||
+	    intel_crtc->cursor_height != state->old.cursor_height)
+		state->cursor_dirty = true;
+
+	if (state->fb_dirty || state->mode_dirty || state->cursor_dirty)
+		s->dirty = true;
+}
+
+static void plane_compute_dirty(struct intel_atomic_state *s,
+				struct intel_plane_state *state)
+{
+	struct drm_plane *plane = state->plane;
+
+	/* if no properties were specified nothing is dirty */
+	if (!state->changed)
+		return;
+
+	if (plane->src_x != state->old.src_x ||
+	    plane->src_y != state->old.src_x ||
+	    plane->src_w != state->old.src_x ||
+	    plane->src_h != state->old.src_x ||
+	    plane->crtc_x != state->old.crtc_x ||
+	    plane->crtc_y != state->old.crtc_y ||
+	    plane->crtc_w != state->old.crtc_w ||
+	    plane->crtc_h != state->old.crtc_h ||
+	    plane->crtc != state->old.crtc ||
+	    plane->fb != state->old.fb)
+		state->dirty = true;
+
+	if (state->dirty)
+		s->dirty = true;
+}
+
+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 intel_crtc_state *st,
+			 struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+	struct intel_encoder *intel_encoder;
+
+	dev_priv->display.crtc_disable(&intel_crtc->base);
+
+	/* FIXME need to check where this stuff is used really */
+	list_for_each_entry(intel_encoder, &dev->mode_config.encoder_list, base.head) {
+		if (intel_encoder->base.crtc == crtc)
+			intel_encoder->connectors_active = false;
+	}
+}
+
+static int crtc_set_base(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	return dev_priv->display.update_plane(crtc, crtc->fb, crtc->x, crtc->y);
+}
+
+static int crtc_mode_set(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct drm_encoder *encoder;
+	int pipe = to_intel_crtc(crtc)->pipe;
+	int ret;
+
+	if (!crtc->enabled) {
+		dev_priv->display.off(crtc);
+		return 0;
+	}
+
+	drm_vblank_pre_modeset(dev, pipe);
+
+	ret = dev_priv->display.crtc_mode_set(crtc, &crtc->mode, &crtc->hwmode,
+					      crtc->x, crtc->y, crtc->fb);
+	if (!ret)
+		ret = dev_priv->display.update_plane(crtc, crtc->fb, crtc->x, crtc->y);
+
+	if (!ret) {
+		intel_update_watermarks(dev);
+
+		if (HAS_PCH_SPLIT(dev))
+			intel_update_linetime_watermarks(dev, pipe, &crtc->hwmode);
+	}
+
+	drm_vblank_post_modeset(dev, pipe);
+
+	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;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+	struct intel_encoder *intel_encoder;
+
+	if (!crtc->enabled)
+		return;
+
+	dev_priv->display.crtc_enable(&intel_crtc->base);
+
+	/* FIXME need to check where this stuff is used really */
+	/* FIXME need to update DPMS state somewhere */
+	list_for_each_entry(intel_encoder, &dev->mode_config.encoder_list, base.head) {
+		if (intel_encoder->base.crtc == crtc)
+			intel_encoder->connectors_active = true;
+	}
+}
+
+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;
+	}
+}
+
+/* FIXME need to refactor the sprite code some more */
+int intel_disable_plane_nopin(struct drm_plane *plane);
+int intel_commit_plane_nopin(struct drm_plane *plane,
+			     struct drm_crtc *crtc,
+			     struct drm_framebuffer *fb,
+			     const struct intel_plane_coords *coords);
+
+static void swap_old_new(struct drm_device *dev,
+			 struct intel_atomic_state *s)
+{
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	int i;
+
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+		swap(encoder->crtc, encoder->old_crtc);
+
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+		swap(connector->encoder, connector->old_encoder);
+
+	for (i = 0; i < dev->mode_config.num_crtc; i++) {
+		struct intel_crtc_state *st = &s->crtc[i];
+		struct drm_crtc *crtc = st->crtc;
+
+		swap(crtc->enabled, st->old.enabled);
+	}
+}
+
+static int apply_config(struct drm_device *dev,
+			struct intel_atomic_state *s)
+{
+	int i, ret;
+
+	/*
+	 * FIXME
+`	 * Hackish way to make crtc_disable() see the current
+	 * state (actually just some select pieces of it).
+	 */
+	swap_old_new(dev, s);
+
+	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);
+		}
+
+		mutex_unlock(&dev->struct_mutex);
+
+		if (!st->mode_dirty)
+			continue;
+
+		crtc_prepare(st, st->crtc);
+	}
+
+	/* Undo the hack above. */
+	swap_old_new(dev, s);
+
+	for (i = 0; i < dev->mode_config.num_crtc; i++) {
+		struct intel_crtc_state *st = &s->crtc[i];
+
+		if (!st->mode_dirty)
+			continue;
+
+		ret = crtc_mode_set(st->crtc);
+		if (ret)
+			return 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;
+		int j;
+
+		if (st->mode_dirty)
+			crtc_commit(crtc);
+		else if (st->fb_dirty) {
+			ret = crtc_set_base(st->crtc);
+			if (ret)
+				return ret;
+		}
+
+		/*
+		 * FIXME these should happen alongside the primary plane setup
+		 * which occurs inside the crtc_enable() hook.
+		 */
+
+		if (st->cursor_dirty) {
+			struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+			intel_crtc_cursor_commit(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;
+			struct intel_plane *intel_plane = to_intel_plane(plane);
+
+			if (!pst->dirty)
+				continue;
+
+			if (pst->coords.visible && plane->crtc == crtc)
+				intel_plane->update_plane(plane, plane->fb, &pst->coords);
+			else if (!pst->coords.visible && pst->old.crtc == crtc)
+				intel_plane->disable_plane(plane);
+		}
+	}
+
+	/* don't restore the old state in end() */
+	s->dirty = false;
+	s->restore_state = 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->base = s->saved_crtcs[i].base;
+		intel_crtc->cursor_bo = s->saved_crtcs[i].cursor_bo;
+		intel_crtc->cursor_addr = s->saved_crtcs[i].cursor_addr;
+		intel_crtc->cursor_handle = s->saved_crtcs[i].cursor_handle;
+		intel_crtc->cursor_x = s->saved_crtcs[i].cursor_x;
+		intel_crtc->cursor_y = s->saved_crtcs[i].cursor_y;
+		intel_crtc->cursor_width = s->saved_crtcs[i].cursor_width;
+		intel_crtc->cursor_height = s->saved_crtcs[i].cursor_height;
+		intel_crtc->cursor_visible = s->saved_crtcs[i].cursor_visible;
+
+		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;
+	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 (fb) {
+		/* FIXME refactor and check */
+		switch (fb->pixel_format) {
+		case DRM_FORMAT_C8:
+		case DRM_FORMAT_RGB565:
+		case DRM_FORMAT_XRGB8888:
+		case DRM_FORMAT_ARGB8888:
+		case DRM_FORMAT_XBGR8888:
+		case DRM_FORMAT_ABGR8888:
+		case DRM_FORMAT_XRGB2101010:
+		case DRM_FORMAT_ARGB2101010:
+		case DRM_FORMAT_XBGR2101010:
+		case DRM_FORMAT_ABGR2101010:
+		case DRM_FORMAT_XRGB1555:
+		case DRM_FORMAT_ARGB1555:
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	if (intel_crtc->cursor_visible &&
+	    (intel_crtc->cursor_width != 64 ||
+	     intel_crtc->cursor_height != 64)) {
+		return -EINVAL;
+	}
+
+	if (!crtc->enabled || !s->mode_dirty)
+		return 0;
+
+	crtc->hwmode = crtc->mode;
+
+	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;
+
+		if (!encoder_funcs->mode_fixup(encoder, &crtc->mode, &crtc->hwmode))
+			return -EINVAL;
+	}
+
+	if (!intel_crtc_mode_fixup(crtc, &crtc->mode, &crtc->hwmode))
+		return -EINVAL;
+
+	ret = intel_check_clock(crtc, &crtc->hwmode);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int intel_atomic_check(struct drm_device *dev, void *state)
+{
+	struct intel_atomic_state *s = state;
+	int ret;
+	int i;
+
+	for (i = 0; i < dev->mode_config.num_crtc; i++) {
+		struct intel_crtc_state *st = &s->crtc[i];
+
+		crtc_compute_dirty(s, st);
+	}
+
+	for (i = 0; i < dev->mode_config.num_crtc; i++) {
+		struct intel_plane_state *st = &s->plane[i];
+
+		plane_compute_dirty(s, st);
+	}
+
+	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_prop_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_prop_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_prop_mode(crtc);
+	update_prop_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 void update_crtc(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;
+
+		/* FIXME is this OK? */
+		if (st->fb_dirty && !st->mode_dirty) {
+			mutex_lock(&dev->struct_mutex);
+			intel_update_fbc(dev);
+			mutex_unlock(&dev->struct_mutex);
+		}
+
+		if (st->mode_dirty) {
+			drm_calc_timestamping_constants(crtc);
+			intel_crtc_update_sarea(crtc, crtc->enabled);
+		}
+
+		if (st->fb_dirty)
+			intel_crtc_update_sarea_pos(crtc, crtc->x, crtc->y);
+	}
+}
+
+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->flags & DRM_MODE_ATOMIC_EVENT)
+		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_crtc(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->restore_state)
+		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 0fb1e09..ac46281 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -7830,6 +7830,9 @@ static void intel_setup_outputs(struct drm_device *dev)
 			intel_encoder_clones(encoder);
 	}
 
+	/* FIXME error handling */
+	intel_atomic_init(dev);
+
 	if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev))
 		ironlake_init_pch_refclk(dev);
 }
@@ -8565,6 +8568,8 @@ void intel_modeset_cleanup(struct drm_device *dev)
 	/* flush any delayed tasks or pending work */
 	flush_scheduled_work();
 
+	intel_atomic_fini(dev);
+
 	drm_mode_config_cleanup(dev);
 }
 
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 6ab8f65..616d5d3 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -627,4 +627,7 @@ extern bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
 				  const struct drm_display_mode *mode,
 				  struct drm_display_mode *adjusted_mode);
 
+extern int intel_atomic_init(struct drm_device *dev);
+extern void intel_atomic_fini(struct drm_device *dev);
+
 #endif /* __INTEL_DRV_H__ */
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 69420e7..f37e8a7 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -509,6 +509,7 @@ struct drm_encoder {
 	struct drm_crtc *crtc;
 	const struct drm_encoder_funcs *funcs;
 	void *helper_private;
+	struct drm_crtc *old_crtc;
 };
 
 enum drm_connector_force {
@@ -614,6 +615,7 @@ struct drm_connector {
 	int audio_latency[2];
 	int null_edid_counter; /* needed to workaround some HW bugs where we get all 0s */
 	unsigned bad_edid_counter;
+	struct drm_encoder *old_encoder;
 };
 
 /**
-- 
1.7.8.6



More information about the dri-devel mailing list