All of lore.kernel.org
 help / color / mirror / Atom feed
From: ville.syrjala@linux.intel.com
To: dri-devel@lists.freedesktop.org
Subject: [PATCH 45/51] drm/i915: Implement atomic modesetting
Date: Thu, 25 Oct 2012 21:05:48 +0300	[thread overview]
Message-ID: <1351188354-24233-46-git-send-email-ville.syrjala@linux.intel.com> (raw)
In-Reply-To: <1351188354-24233-1-git-send-email-ville.syrjala@linux.intel.com>

From: Ville Syrjälä <ville.syrjala@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@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@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

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

  parent reply	other threads:[~2012-10-25 18:08 UTC|newest]

Thread overview: 61+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-10-25 18:05 [PATCH 00/51] Atomic mode setting and page flip ville.syrjala
2012-10-25 18:05 ` [PATCH 01/51] drm: Be more paranoid with integer overflows ville.syrjala
2012-10-25 18:05 ` [PATCH 02/51] drm: Constify some function arguments ville.syrjala
2012-10-25 18:05 ` [PATCH 03/51] drm: Ignore blob propertys in drm_property_change_is_valid() ville.syrjala
2012-10-25 18:05 ` [PATCH 04/51] drm: Add struct drm_region and assorted utility functions ville.syrjala
2012-10-25 18:05 ` [PATCH 05/51] drm: Add drm_calc_{hscale, vscale}() " ville.syrjala
2012-10-25 18:05 ` [PATCH 06/51] drm: Keep a copy of last plane coordinates ville.syrjala
2012-10-25 18:05 ` [PATCH 07/51] drm: Add restore_fbdev_mode() hook to drm_fb_helper ville.syrjala
2012-10-25 18:05 ` [PATCH 08/51] drm: Export drm_property_create_blob() and drm_property_destroy_blob() ville.syrjala
2012-10-25 18:05 ` [PATCH 09/51] drm: Allow signed values for range properties ville.syrjala
2012-10-25 18:05 ` [PATCH 10/51] drm: Allow drm_mode_object_find() to look up an object of any type ville.syrjala
2012-10-25 18:05 ` [PATCH 11/51] drm: Export drm_encoder_crtc_ok ville.syrjala
2012-10-25 18:05 ` [PATCH 12/51] drm: Export drm_crtc_prepare_encoders() ville.syrjala
2012-10-25 18:05 ` [PATCH 13/51] drm: Refactor object property check code ville.syrjala
2012-10-25 18:05 ` [PATCH 14/51] drm: Export mode<->umode conversion functions ville.syrjala
2012-10-25 18:05 ` [PATCH 15/51] drm: Make blobs resizeable ville.syrjala
2012-10-25 18:05 ` [PATCH 16/51] drm: Add drm_flip helper ville.syrjala
2012-10-25 18:05 ` [PATCH 17/51] drm: Add mode_blob and connector_ids_blob to drm_crtc ville.syrjala
2012-10-25 18:05 ` [PATCH 18/51] drm: Add the atomic modeset ioctl ville.syrjala
2012-10-25 18:05 ` [PATCH 19/51] drm/i915: Fix display pixel format handling ville.syrjala
2012-10-25 18:05 ` [PATCH 20/51] drm/i915: Add SURFLIVE register definitions ville.syrjala
2012-10-25 18:05 ` [PATCH 21/51] drm/i915: Implement execbuffer wait for all planes ville.syrjala
2012-10-25 18:05 ` [PATCH 22/51] drm/i915: Check framebuffer stride more thoroughly ville.syrjala
2012-10-25 18:05 ` [PATCH 23/51] drm/i915: Check the framebuffer offset ville.syrjala
2012-10-25 18:05 ` [PATCH 24/51] drm/i915: Handle framebuffer offsets[] ville.syrjala
2012-10-25 18:05 ` [PATCH 25/51] drm/i915: Implement proper clipping for video sprites ville.syrjala
2012-10-25 18:05 ` [PATCH 26/51] drm/i915: pixel_size == cpp ville.syrjala
2012-10-25 18:05 ` [PATCH 27/51] drm/i915: Bad pixel formats can't reach the sprite code ville.syrjala
2012-10-25 18:05 ` [PATCH 28/51] drm/i915: Implement restore_fbdev_mode hook ville.syrjala
2012-10-25 18:05 ` [PATCH 29/51] drm/i915: Split clipping and checking from update_plane hook ville.syrjala
2012-10-25 18:05 ` [PATCH 30/51] drm/i915: Factor out i9xx_compute_clocks() like ironlake_compute_clocks() ville.syrjala
2012-10-25 18:05 ` [PATCH 31/51] drm/i915: Consitify adjusted_mode parameter ville.syrjala
2012-10-25 18:05 ` [PATCH 32/51] drm/i915: Add intel_check_clock() ville.syrjala
2012-10-25 18:05 ` [PATCH 33/51] drm/i915: store cursor_handle in struct intel_crtc ville.syrjala
2012-10-25 18:05 ` [PATCH 34/51] drm/i915: split cursor setting code into prepare/commit/unref parts ville.syrjala
2012-10-25 18:05 ` [PATCH 35/51] drm/i915: unstatic cursor functions for use with atomic modesetting ville.syrjala
2012-10-25 18:05 ` [PATCH 36/51] drm/i915: Unstatic intel_finish_fb() ville.syrjala
2012-10-25 18:05 ` [PATCH 37/51] drm/i915: Pull intel_pipe_set_base() out of the crtc_mode_set() functions ville.syrjala
2012-10-25 18:05 ` [PATCH 38/51] drm/i915: Unstatic intel_crtc_update_sarea() ville.syrjala
2012-10-25 18:05 ` [PATCH 39/51] drm/i915: Introduce intel_crtc_update_sarea_pos() ville.syrjala
2012-10-25 18:05 ` [PATCH 40/51] drm/i915: Constify mode argument to intel_modeset_adjusted_mode() ville.syrjala
2012-10-25 18:05 ` [PATCH 41/51] drm/i915: Unstatic intel_crtc_mode_fixup() ville.syrjala
2012-10-25 18:05 ` [PATCH 42/51] drm/i915: Introduce intel_plane_regs ville.syrjala
2012-10-25 18:05 ` [PATCH 43/51] drm/i915: Split primary plane update_plane() into calc+commit phases ville.syrjala
2012-10-25 18:05 ` [PATCH 44/51] drm/i915: Split sprite " ville.syrjala
2012-10-25 18:05 ` ville.syrjala [this message]
2012-10-25 18:05 ` [PATCH 46/51] drm/i915: Add support for atomic modesetting completion events ville.syrjala
2012-11-01 11:12   ` Daniel Vetter
2012-11-01 14:39     ` Jesse Barnes
2012-11-01 17:07       ` Ville Syrjälä
2012-11-01 17:12         ` Jesse Barnes
2012-11-01 22:39           ` Daniel Vetter
2012-11-02  9:10             ` Ville Syrjälä
2012-11-07 20:29             ` Rob Clark
2012-11-09 21:20               ` Daniel Vetter
2012-11-09 21:25                 ` Rob Clark
2012-10-25 18:05 ` [PATCH 47/51] drm/i915: Add atomic page flip support ville.syrjala
2012-10-25 18:05 ` [PATCH 48/51] drm/i915: Unstatic intel_enable_primary() and intel_disable_primary() ville.syrjala
2012-10-25 18:05 ` [PATCH 49/51] drm/i915: Respect primary_disabled in crtc_enable() ville.syrjala
2012-10-25 18:05 ` [PATCH 50/51] drm/i915: Enable/disable primary plane in calc_plane() ville.syrjala
2012-10-25 18:05 ` [PATCH 51/51] drm/i915: Add primary plane disable logic to atomic mode setting code ville.syrjala

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1351188354-24233-46-git-send-email-ville.syrjala@linux.intel.com \
    --to=ville.syrjala@linux.intel.com \
    --cc=dri-devel@lists.freedesktop.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.