All of lore.kernel.org
 help / color / mirror / Atom feed
From: Rob Clark <rob.clark@linaro.org>
To: dri-devel@lists.freedesktop.org
Cc: daniel.vetter@ffwll.ch, Rob Clark <rob@ti.com>, patches@linaro.org
Subject: [RFC 11/11] drm/omap: update for atomic age
Date: Wed, 12 Sep 2012 23:00:11 -0500	[thread overview]
Message-ID: <1347508811-15029-2-git-send-email-rob.clark@linaro.org> (raw)
In-Reply-To: <1347508811-15029-1-git-send-email-rob.clark@linaro.org>

From: Rob Clark <rob@ti.com>

---
 drivers/staging/omapdrm/Makefile      |    1 +
 drivers/staging/omapdrm/omap_atomic.c |  339 +++++++++++++++++++++++++++++++++
 drivers/staging/omapdrm/omap_atomic.h |   52 +++++
 drivers/staging/omapdrm/omap_crtc.c   |  246 +++++++++++-------------
 drivers/staging/omapdrm/omap_drv.c    |   21 +-
 drivers/staging/omapdrm/omap_drv.h    |   45 +++--
 drivers/staging/omapdrm/omap_fb.c     |   44 +++--
 drivers/staging/omapdrm/omap_plane.c  |  296 +++++++++++++++-------------
 8 files changed, 727 insertions(+), 317 deletions(-)
 create mode 100644 drivers/staging/omapdrm/omap_atomic.c
 create mode 100644 drivers/staging/omapdrm/omap_atomic.h

diff --git a/drivers/staging/omapdrm/Makefile b/drivers/staging/omapdrm/Makefile
index d85e058..7d45e4d 100644
--- a/drivers/staging/omapdrm/Makefile
+++ b/drivers/staging/omapdrm/Makefile
@@ -13,6 +13,7 @@ omapdrm-y := omap_drv.o \
 	omap_connector.o \
 	omap_fb.o \
 	omap_fbdev.o \
+	omap_atomic.o \
 	omap_gem.o \
 	omap_gem_dmabuf.o \
 	omap_dmm_tiler.o \
diff --git a/drivers/staging/omapdrm/omap_atomic.c b/drivers/staging/omapdrm/omap_atomic.c
new file mode 100644
index 0000000..27e5751
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_atomic.c
@@ -0,0 +1,339 @@
+/*
+ * drivers/staging/omapdrm/omap_atomic.c
+ *
+ * Copyright (C) 2012 Texas Instruments
+ * Author: Rob Clark <rob.clark@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "omap_drv.h"
+#include "omap_atomic.h"
+
+struct omap_atomic_state {
+	struct drm_device *dev;
+
+	/* for page-flips, this is the CRTC involved: */
+	struct drm_crtc *crtc;
+	int pipe;
+
+	int num_dirty_planes, num_dirty_crtcs;
+	struct omap_plane_state *plane_state[8];
+	struct omap_crtc_state  *crtc_state[8];
+
+	int num_pending_fbs;
+	atomic_t num_ready_fbs;
+	struct drm_framebuffer  *pending_fbs[8];
+
+	/* for handling page flips without caring about what
+	 * the callback is called from.  Possibly we should just
+	 * make omap_gem always call the cb from the worker so
+	 * we don't have to care about this..
+	 */
+	struct work_struct commit_work;
+};
+
+static void commit_worker(struct work_struct *work);
+
+static int crtc2pipe(struct drm_device *dev, struct drm_crtc *crtc)
+{
+	struct omap_drm_private *priv = dev->dev_private;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(priv->crtcs); i++)
+		if (priv->crtcs[i] == crtc)
+			return i;
+
+	BUG();  /* bogus CRTC ptr */
+	return -1;
+}
+
+void *omap_atomic_begin(struct drm_device *dev, struct drm_crtc *crtc)
+{
+	struct omap_drm_private *priv = dev->dev_private;
+	struct omap_atomic_state *omap_state;
+	int pipe = 0;
+
+	if (crtc) {
+		pipe = crtc2pipe(dev, crtc);
+		if (priv->event[pipe]) {
+			dev_err(dev->dev, "pending page-flip!\n");
+			return ERR_PTR(-EBUSY);
+		}
+		WARN_ON(priv->crtc_atomic[pipe]);
+		priv->crtc_atomic[pipe] = true;
+	} else {
+		WARN_ON(priv->global_atomic);
+		priv->global_atomic = true;
+	}
+
+	omap_state = kzalloc(sizeof(*omap_state), GFP_KERNEL);
+	if (!omap_state) {
+		dev_err(dev->dev, "failed to allocate state\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	omap_state->dev = dev;
+	omap_state->crtc = crtc;
+	omap_state->pipe = pipe;
+
+	INIT_WORK(&omap_state->commit_work, commit_worker);
+
+	DBG("state=%p, crtc=%p", omap_state, crtc);
+
+	return omap_state;
+}
+
+static void release_state(struct omap_atomic_state *omap_state)
+{
+	int i;
+
+	DBG("state=%p", omap_state);
+
+	for (i = 0; i < omap_state->num_pending_fbs; i++)
+		drm_framebuffer_unreference(omap_state->pending_fbs[i]);
+
+	/*
+	 * omap_plane_commit()/omap_crtc_commit() have taken ownership
+	 * of their respective state objects, so don't need to kfree()
+	 * 'em here
+	 */
+
+	kfree(omap_state);
+}
+
+int omap_atomic_check(struct drm_device *dev, void *state)
+{
+	struct omap_atomic_state *omap_state = state;
+	struct omap_drm_private *priv = dev->dev_private;
+	int i, ret = 0;
+
+	for (i = 0; (i < ARRAY_SIZE(omap_state->plane_state)) && !ret; i++)
+		if (omap_state->plane_state[i])
+			ret = omap_plane_check_state(priv->planes[i],
+					omap_state->plane_state[i]);
+
+	for (i = 0; (i < ARRAY_SIZE(omap_state->crtc_state)) && !ret; i++)
+		if (omap_state->crtc_state[i])
+			ret = omap_crtc_check_state(priv->crtcs[i],
+					omap_state->crtc_state[i]);
+
+	DBG("state=%p, ret=%d", omap_state, ret);
+
+	if (ret)
+		release_state(omap_state);
+
+	return ret;
+}
+
+static void commit_state(struct omap_atomic_state *omap_state)
+{
+	struct drm_device *dev = omap_state->dev;
+	struct omap_drm_private *priv = dev->dev_private;
+	int i;
+
+	DBG("state=%p", omap_state);
+
+	for (i = 0; i < ARRAY_SIZE(omap_state->plane_state); i++) {
+		struct omap_plane_state *plane_state =
+				omap_state->plane_state[i];
+		if (plane_state)
+			omap_plane_commit_state(priv->planes[i], plane_state);
+	}
+
+	if (omap_state->crtc) {
+		int pipe = omap_state->pipe;
+		priv->crtc_atomic[pipe] = false;
+	} else {
+		priv->global_atomic = false;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(omap_state->crtc_state); i++) {
+		struct omap_crtc_state *crtc_state =
+				omap_state->crtc_state[i];
+		if (crtc_state)
+			omap_crtc_commit_state(priv->crtcs[i], crtc_state);
+	}
+
+	release_state(omap_state);
+}
+
+static void commit_worker(struct work_struct *work)
+{
+	struct omap_atomic_state *omap_state =
+			container_of(work, struct omap_atomic_state, commit_work);
+	struct drm_device *dev = omap_state->dev;
+
+	mutex_lock(&dev->mode_config.mutex);
+	DBG("state=%p", omap_state);
+	commit_state(omap_state);
+	mutex_unlock(&dev->mode_config.mutex);
+}
+
+static void commit_cb(void *state)
+{
+	struct omap_atomic_state *omap_state = state;
+	struct omap_drm_private *priv = omap_state->dev->dev_private;
+	int num_ready_fbs = atomic_inc_return(&omap_state->num_ready_fbs);
+
+	if (num_ready_fbs == omap_state->num_pending_fbs)
+		queue_work(priv->wq, &omap_state->commit_work);
+}
+
+static void commit_async(struct drm_device *dev, void *state,
+		struct drm_pending_vblank_event *event)
+{
+	struct omap_atomic_state *omap_state = state;
+	struct omap_drm_private *priv = omap_state->dev->dev_private;
+	int i;
+
+	if (event) {
+		int pipe = omap_state->pipe;
+		WARN_ON(priv->event[pipe]);
+		priv->event[pipe] = event;
+	}
+
+	if (!omap_state->num_pending_fbs) {
+		commit_state(omap_state);
+		return;
+	}
+
+	for (i = 0; i < omap_state->num_pending_fbs; i++) {
+		struct drm_gem_object *bo;
+		bo = omap_framebuffer_bo(omap_state->pending_fbs[i], 0);
+		omap_gem_op_async(bo, OMAP_GEM_READ, commit_cb, omap_state);
+	}
+}
+
+int omap_atomic_commit(struct drm_device *dev, void *state,
+		struct drm_pending_vblank_event *event)
+{
+	struct omap_atomic_state *omap_state = state;
+
+	DBG("state=%p, event=%p", omap_state, event);
+
+	if (omap_state->crtc) {
+		/* async page-flip */
+		commit_async(dev, state, event);
+	} else {
+		/* sync mode-set, etc */
+		WARN_ON(event);  /* this should not happen */
+		commit_state(omap_state);
+	}
+
+	return 0;
+}
+
+void omap_atomic_end(struct drm_device *dev, void *state)
+{
+	/*
+	 * State is freed either if atomic_check() fails or
+	 * when async pageflip completes, so we don't need
+	 * to do anything here.
+	 */
+}
+
+struct omap_plane_state *omap_atomic_plane_state(void *state, int id)
+{
+	struct omap_atomic_state *omap_state = state;
+	struct omap_drm_private *priv = omap_state->dev->dev_private;
+	struct omap_plane_state *plane_state = omap_state->plane_state[id];
+	int i;
+
+	if (!plane_state) {
+		struct drm_plane *plane = priv->planes[id];
+
+		plane_state = kzalloc(sizeof(*plane_state), GFP_KERNEL);
+
+		/* snapshot current state: */
+		*plane_state = *to_omap_plane_state(plane->state);
+
+		omap_state->plane_state[id] = plane_state;
+	}
+
+	/* updating a plane implicitly dirties the crtc: */
+	for (i = 0; i < priv->num_crtcs; i++) {
+		if (priv->crtcs[i] == plane_state->base.crtc) {
+			omap_atomic_crtc_state(state, i);
+			break;
+		}
+	}
+
+	return plane_state;
+}
+
+struct omap_crtc_state *omap_atomic_crtc_state(void *state, int id)
+{
+	struct omap_atomic_state *omap_state = state;
+	struct omap_drm_private *priv = omap_state->dev->dev_private;
+	struct omap_crtc_state *crtc_state = omap_state->crtc_state[id];
+
+	if (!crtc_state) {
+		struct drm_crtc *crtc = priv->crtcs[id];
+
+		crtc_state = kzalloc(sizeof(*crtc_state), GFP_KERNEL);
+
+		/* snapshot current state: */
+		*crtc_state = *to_omap_crtc_state(crtc->state);
+
+		omap_state->crtc_state[id] = crtc_state;
+	}
+
+	return crtc_state;
+}
+
+/* when fb is changed, that gets recorded in the state, so that pageflips
+ * can defer until all fb's are ready
+ */
+void omap_atomic_add_fb(void *state, struct drm_framebuffer *fb)
+{
+	struct omap_atomic_state *omap_state = state;
+	drm_framebuffer_reference(fb);
+	omap_state->pending_fbs[omap_state->num_pending_fbs++] = fb;
+}
+
+/* possibly this could be in drm core? */
+static void send_page_flip_event(struct drm_device *dev, int crtc,
+		struct drm_pending_vblank_event *event)
+{
+	unsigned long flags;
+	struct timeval now;
+
+	DBG("%p", event);
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	event->event.sequence = drm_vblank_count_and_time(dev, crtc, &now);
+	event->event.tv_sec = now.tv_sec;
+	event->event.tv_usec = now.tv_usec;
+	list_add_tail(&event->base.link,
+			&event->base.file_priv->event_list);
+	wake_up_interruptible(&event->base.file_priv->event_wait);
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+/* called when plane is updated.. so we can keep track of when to send
+ * page-flip events
+ */
+void omap_atomic_plane_update(struct drm_device *dev, int id)
+{
+	struct omap_drm_private *priv = dev->dev_private;
+	int pipe = crtc2pipe(dev, priv->planes[id]->state->crtc);
+
+	DBG("id=%d", id);
+
+	if (priv->event[pipe]) {
+		/* wakeup userspace */
+		send_page_flip_event(dev, pipe, priv->event[pipe]);
+		priv->event[pipe] = NULL;
+	}
+}
diff --git a/drivers/staging/omapdrm/omap_atomic.h b/drivers/staging/omapdrm/omap_atomic.h
new file mode 100644
index 0000000..6ba596a
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_atomic.h
@@ -0,0 +1,52 @@
+/*
+ * drivers/staging/omapdrm/omap_atomic.h
+ *
+ * Copyright (C) 2012 Texas Instruments
+ * Author: Rob Clark <rob.clark@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __OMAP_ATOMIC_H__
+#define __OMAP_ATOMIC_H__
+
+#include "drm_mode.h"
+#include "drm_crtc.h"
+
+struct omap_plane_state {
+	struct drm_plane_state base;
+	uint8_t rotation;
+	uint8_t zorder;
+	uint8_t enabled;
+};
+#define to_omap_plane_state(x) container_of(x, struct omap_plane_state, base)
+
+struct omap_crtc_state {
+	struct drm_crtc_state base;
+};
+#define to_omap_crtc_state(x) container_of(x, struct omap_crtc_state, base)
+
+void *omap_atomic_begin(struct drm_device *dev, struct drm_crtc *crtc);
+int omap_atomic_check(struct drm_device *dev, void *state);
+int omap_atomic_commit(struct drm_device *dev, void *state,
+		struct drm_pending_vblank_event *event);
+void omap_atomic_end(struct drm_device *dev, void *state);
+
+struct omap_plane_state *omap_atomic_plane_state(void *state, int id);
+struct omap_crtc_state *omap_atomic_crtc_state(void *state, int id);
+
+void omap_atomic_add_fb(void *state, struct drm_framebuffer *fb);
+
+void omap_atomic_plane_update(struct drm_device *dev, int id);
+
+#endif /* __OMAP_ATOMIC_H__ */
diff --git a/drivers/staging/omapdrm/omap_crtc.c b/drivers/staging/omapdrm/omap_crtc.c
index b0d22c3..805c75c 100644
--- a/drivers/staging/omapdrm/omap_crtc.c
+++ b/drivers/staging/omapdrm/omap_crtc.c
@@ -32,7 +32,6 @@ struct omap_crtc {
 	const char *name;
 	int pipe;
 	enum omap_channel channel;
-	struct omap_overlay_manager_info info;
 
 	struct omap_video_timings timings;
 	bool enabled, timings_valid;
@@ -48,25 +47,17 @@ struct omap_crtc {
 
 	/* for handling queued and in-progress applies: */
 	struct work_struct apply_work;
-
-	/* if there is a pending flip, these will be non-null: */
-	struct drm_pending_vblank_event *event;
-
-	/* for handling page flips without caring about what
-	 * the callback is called from.  Possibly we should just
-	 * make omap_gem always call the cb from the worker so
-	 * we don't have to care about this..
-	 *
-	 * XXX maybe fold into apply_work??
-	 */
-	struct work_struct page_flip_work;
 };
 
+static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+		struct drm_framebuffer *old_fb);
+
 static void omap_crtc_destroy(struct drm_crtc *crtc)
 {
 	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
 	omap_crtc->plane->funcs->destroy(omap_crtc->plane);
 	drm_crtc_cleanup(crtc);
+	kfree(crtc->state);
 	kfree(omap_crtc);
 }
 
@@ -89,7 +80,7 @@ static void omap_crtc_dpms(struct drm_crtc *crtc, int mode)
 		/* and any attached overlay planes: */
 		for (i = 0; i < priv->num_planes; i++) {
 			struct drm_plane *plane = priv->planes[i];
-			if (plane->crtc == crtc)
+			if (plane->state->crtc == crtc)
 				WARN_ON(omap_plane_dpms(plane, mode));
 		}
 	}
@@ -127,11 +118,7 @@ static int omap_crtc_mode_set(struct drm_crtc *crtc,
 		}
 	}
 
-	return omap_plane_mode_set(omap_crtc->plane, crtc, crtc->fb,
-			0, 0, mode->hdisplay, mode->vdisplay,
-			x << 16, y << 16,
-			mode->hdisplay << 16, mode->vdisplay << 16,
-			NULL, NULL);
+	return omap_crtc_mode_set_base(crtc, x, y, old_fb);
 }
 
 static void omap_crtc_prepare(struct drm_crtc *crtc)
@@ -153,134 +140,83 @@ static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
 {
 	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
 	struct drm_plane *plane = omap_crtc->plane;
-	struct drm_display_mode *mode = &crtc->mode;
-
-	return omap_plane_mode_set(plane, crtc, crtc->fb,
-			0, 0, mode->hdisplay, mode->vdisplay,
-			x << 16, y << 16,
-			mode->hdisplay << 16, mode->vdisplay << 16,
-			NULL, NULL);
-}
-
-static void omap_crtc_load_lut(struct drm_crtc *crtc)
-{
-}
-
-/* possibly this could be in drm core? */
-static void send_page_flip_event(struct drm_device *dev, int crtc,
-		struct drm_pending_vblank_event *event)
-{
-	unsigned long flags;
-	struct timeval now;
-
-	DBG("%p", event);
-
-	spin_lock_irqsave(&dev->event_lock, flags);
-	event->event.sequence = drm_vblank_count_and_time(dev, crtc, &now);
-	event->event.tv_sec = now.tv_sec;
-	event->event.tv_usec = now.tv_usec;
-	list_add_tail(&event->base.link,
-			&event->base.file_priv->event_list);
-	wake_up_interruptible(&event->base.file_priv->event_wait);
-	spin_unlock_irqrestore(&dev->event_lock, flags);
-}
-
-static void vblank_cb(void *arg)
-{
-	struct drm_crtc *crtc = arg;
-	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-	struct drm_pending_vblank_event *event = omap_crtc->event;
-
-	WARN_ON(!event);
-
-	omap_crtc->event = NULL;
-
-	/* wakeup userspace */
-	if (event)
-		send_page_flip_event(crtc->dev, omap_crtc->pipe, event);
-}
-
-static void page_flip_worker(struct work_struct *work)
-{
-	struct omap_crtc *omap_crtc =
-			container_of(work, struct omap_crtc, page_flip_work);
-	struct drm_crtc *crtc = &omap_crtc->base;
 	struct drm_device *dev = crtc->dev;
+	struct drm_mode_config *config = &dev->mode_config;
 	struct drm_display_mode *mode = &crtc->mode;
-	struct drm_gem_object *bo;
-
-	mutex_lock(&dev->mode_config.mutex);
-	omap_plane_mode_set(omap_crtc->plane, crtc, crtc->fb,
-			0, 0, mode->hdisplay, mode->vdisplay,
-			crtc->x << 16, crtc->y << 16,
-			mode->hdisplay << 16, mode->vdisplay << 16,
-			vblank_cb, crtc);
-	mutex_unlock(&dev->mode_config.mutex);
-
-	bo = omap_framebuffer_bo(crtc->fb, 0);
-	drm_gem_object_unreference_unlocked(bo);
+	void *state;
+	int ret;
+
+	/* for now, until property based atomic mode-set: */
+	state = omap_atomic_begin(dev, NULL);
+	if (IS_ERR(state))
+		return PTR_ERR(state);
+
+	ret =
+		drm_mode_plane_set_obj_prop(plane, state,
+				config->prop_crtc_id, crtc->base.id) ||
+		drm_mode_plane_set_obj_prop(plane, state,
+				config->prop_fb_id, crtc->state->fb->base.id) ||
+		drm_mode_plane_set_obj_prop(plane, state,
+				config->prop_crtc_x, 0) ||
+		drm_mode_plane_set_obj_prop(plane, state,
+				config->prop_crtc_y, 0) ||
+		drm_mode_plane_set_obj_prop(plane, state,
+				config->prop_crtc_w, mode->hdisplay) ||
+		drm_mode_plane_set_obj_prop(plane, state,
+				config->prop_crtc_h, mode->vdisplay) ||
+		drm_mode_plane_set_obj_prop(plane, state,
+				config->prop_src_w, mode->hdisplay << 16) ||
+		drm_mode_plane_set_obj_prop(plane, state,
+				config->prop_src_h, mode->vdisplay << 16) ||
+		drm_mode_plane_set_obj_prop(plane, state,
+				config->prop_src_x, x << 16) ||
+		drm_mode_plane_set_obj_prop(plane, state,
+				config->prop_src_y, y << 16) ||
+		dev->driver->atomic_check(dev, state);
+
+	if (!ret)
+		ret = omap_atomic_commit(dev, state, NULL);
+
+	omap_atomic_end(dev, state);
+
+	return ret;
 }
 
-static void page_flip_cb(void *arg)
-{
-	struct drm_crtc *crtc = arg;
-	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-	struct omap_drm_private *priv = crtc->dev->dev_private;
-
-	/* avoid assumptions about what ctxt we are called from: */
-	queue_work(priv->wq, &omap_crtc->page_flip_work);
-}
-
-static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
-		 struct drm_framebuffer *fb,
-		 struct drm_pending_vblank_event *event)
+static void omap_crtc_load_lut(struct drm_crtc *crtc)
 {
-	struct drm_device *dev = crtc->dev;
-	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-	struct drm_gem_object *bo;
-
-	DBG("%d -> %d (event=%p)", crtc->fb ? crtc->fb->base.id : -1,
-			fb->base.id, event);
-
-	if (omap_crtc->event) {
-		dev_err(dev->dev, "already a pending flip\n");
-		return -EINVAL;
-	}
-
-	omap_crtc->event = event;
-	crtc->fb = fb;
-
-	/*
-	 * Hold a reference temporarily until the crtc is updated
-	 * and takes the reference to the bo.  This avoids it
-	 * getting freed from under us:
-	 */
-	bo = omap_framebuffer_bo(fb, 0);
-	drm_gem_object_reference(bo);
-
-	omap_gem_op_async(bo, OMAP_GEM_READ, page_flip_cb, crtc);
-
-	return 0;
 }
 
-static int omap_crtc_set_property(struct drm_crtc *crtc,
+static int omap_crtc_set_property(struct drm_crtc *crtc, void *state,
 		struct drm_property *property, uint64_t val)
 {
 	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
 	struct omap_drm_private *priv = crtc->dev->dev_private;
+	struct omap_crtc_state *crtc_state =
+			omap_atomic_crtc_state(state, omap_crtc->pipe);
+	int ret;
+
+	DBG("%s: %s = %llx", omap_crtc->name, property->name, val);
+
+	ret = drm_crtc_set_property(crtc, &crtc_state->base, property, val);
+	if (!ret) {
+		/* we need to set fb property on our private plane too:
+		 */
+		struct drm_mode_config *config = &crtc->dev->mode_config;
+		if (property != config->prop_fb_id)
+			return ret;
+	}
 
 	if (property == priv->rotation_prop) {
-		crtc->invert_dimensions =
+		crtc_state->base.invert_dimensions =
 				!!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270)));
 	}
 
-	return omap_plane_set_property(omap_crtc->plane, property, val);
+	return omap_plane_set_property(omap_crtc->plane, state, property, val);
 }
 
 static const struct drm_crtc_funcs omap_crtc_funcs = {
 	.set_config = drm_crtc_helper_set_config,
 	.destroy = omap_crtc_destroy,
-	.page_flip = omap_crtc_page_flip_locked,
 	.set_property = omap_crtc_set_property,
 };
 
@@ -306,6 +242,23 @@ enum omap_channel omap_crtc_channel(struct drm_crtc *crtc)
 	return omap_crtc->channel;
 }
 
+int omap_crtc_check_state(struct drm_crtc *crtc,
+		struct omap_crtc_state *crtc_state)
+{
+	return drm_crtc_check_state(crtc, &crtc_state->base);
+}
+
+void omap_crtc_commit_state(struct drm_crtc *crtc,
+		struct omap_crtc_state *crtc_state)
+{
+
+	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+	struct omap_crtc_state *old_state = to_omap_crtc_state(crtc->state);
+	drm_crtc_commit_state(crtc, &crtc_state->base);
+	kfree(old_state);
+	omap_crtc_apply(crtc, &omap_crtc->apply);
+}
+
 static void omap_crtc_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
 {
 	struct omap_crtc *omap_crtc =
@@ -345,6 +298,9 @@ static void apply_worker(struct work_struct *work)
 		list_del(&apply->pending_node);
 	}
 
+	if (pipe_in_atomic(dev, omap_crtc->pipe))
+		goto out;
+
 	need_apply = !list_empty(&omap_crtc->queued_applies);
 
 	/* then handle the next round of of queued apply's: */
@@ -368,6 +324,8 @@ static void apply_worker(struct work_struct *work)
 			omap_crtc_irq(&omap_crtc->irq, 0);
 		}
 	}
+
+out:
 	dispc_runtime_put();
 	mutex_unlock(&dev->mode_config.mutex);
 }
@@ -381,11 +339,14 @@ int omap_crtc_apply(struct drm_crtc *crtc,
 	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
 
 	/* no need to queue it again if it is already queued: */
-	if (apply->queued)
-		return 0;
+	if (! apply->queued) {
+		apply->queued = true;
+		list_add_tail(&apply->queued_node,
+				&omap_crtc->queued_applies);
+	}
 
-	apply->queued = true;
-	list_add_tail(&apply->queued_node, &omap_crtc->queued_applies);
+	if (pipe_in_atomic(dev, omap_crtc->pipe))
+		return 0;
 
 	/*
 	 * If there are no currently pending updates, then go ahead and
@@ -404,6 +365,7 @@ static void omap_crtc_pre_apply(struct omap_drm_apply *apply)
 {
 	struct omap_crtc *omap_crtc =
 			container_of(apply, struct omap_crtc, apply);
+	struct omap_overlay_manager_info info = {0};
 
 	DBG("%s: enabled=%d, timings_valid=%d", omap_crtc->name,
 			omap_crtc->enabled,
@@ -414,7 +376,12 @@ static void omap_crtc_pre_apply(struct omap_drm_apply *apply)
 		return;
 	}
 
-	dispc_mgr_setup(omap_crtc->channel, &omap_crtc->info);
+	info.default_color = 0x00000000;
+	info.trans_key = 0x00000000;
+	info.trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
+	info.trans_enabled = false;
+
+	dispc_mgr_setup(omap_crtc->channel, &info);
 	dispc_mgr_set_timings(omap_crtc->channel,
 			&omap_crtc->timings);
 	dispc_mgr_enable(omap_crtc->channel, true);
@@ -437,7 +404,6 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
 {
 	struct drm_crtc *crtc = NULL;
 	struct omap_crtc *omap_crtc;
-	struct omap_overlay_manager_info *info;
 
 	DBG("%s", channel_names[channel]);
 
@@ -450,7 +416,13 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
 
 	crtc = &omap_crtc->base;
 
-	INIT_WORK(&omap_crtc->page_flip_work, page_flip_worker);
+	crtc->state = kzalloc(sizeof(struct omap_crtc_state), GFP_KERNEL);
+
+	if (!crtc->state) {
+		dev_err(dev->dev, "could not allocate CRTC state\n");
+		goto fail;
+	}
+
 	INIT_WORK(&omap_crtc->apply_work, apply_worker);
 
 	INIT_LIST_HEAD(&omap_crtc->pending_applies);
@@ -464,16 +436,10 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
 
 	omap_crtc->channel = channel;
 	omap_crtc->plane = plane;
-	omap_crtc->plane->crtc = crtc;
+	omap_crtc->plane->state->crtc = crtc;
 	omap_crtc->name = channel_names[channel];
 	omap_crtc->pipe = id;
 
-	/* TODO: fix hard-coded setup.. add properties! */
-	info->default_color = 0x00000000;
-	info->trans_key = 0x00000000;
-	info->trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
-	info->trans_enabled = false;
-
 	drm_crtc_init(dev, crtc, &omap_crtc_funcs);
 	drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs);
 
diff --git a/drivers/staging/omapdrm/omap_drv.c b/drivers/staging/omapdrm/omap_drv.c
index 04577bc..fe5fd15 100644
--- a/drivers/staging/omapdrm/omap_drv.c
+++ b/drivers/staging/omapdrm/omap_drv.c
@@ -18,6 +18,7 @@
  */
 
 #include "omap_drv.h"
+#include "omap_atomic.h"
 
 #include "drm_crtc_helper.h"
 #include "drm_fb_helper.h"
@@ -441,7 +442,9 @@ static void dev_lastclose(struct drm_device *dev)
 	 * default state on lastclose?
 	 */
 	for (i = 0; i < priv->num_planes; i++) {
-		drm_object_property_set_value(&priv->planes[i]->base,
+		struct drm_plane *plane = priv->planes[i];
+		drm_object_property_set_value(&plane->base,
+				&plane->state->propvals,
 				priv->rotation_prop, 0);
 	}
 
@@ -454,7 +457,19 @@ static void dev_lastclose(struct drm_device *dev)
 
 static void dev_preclose(struct drm_device *dev, struct drm_file *file)
 {
+	struct omap_drm_private *priv = dev->dev_private;
+	int i;
+
 	DBG("preclose: dev=%p", dev);
+
+	/*
+	 * Clear out pending events before they get destroyed in
+	 * drm_events_release(), so that if we later get a vblank
+	 * we don't deref a bogus ptr
+	 */
+	for (i = 0; i < ARRAY_SIZE(priv->event); i++)
+		if (priv->event[i] && priv->event[i]->base.file_priv == file)
+			priv->event[i] = NULL;
 }
 
 static void dev_postclose(struct drm_device *dev, struct drm_file *file)
@@ -511,6 +526,10 @@ static struct drm_driver omap_drm_driver = {
 		.dumb_create = omap_gem_dumb_create,
 		.dumb_map_offset = omap_gem_dumb_map_offset,
 		.dumb_destroy = omap_gem_dumb_destroy,
+		.atomic_begin = omap_atomic_begin,
+		.atomic_check = omap_atomic_check,
+		.atomic_commit = omap_atomic_commit,
+		.atomic_end = omap_atomic_end,
 		.ioctls = ioctls,
 		.num_ioctls = DRM_OMAP_NUM_IOCTLS,
 		.fops = &omapdriver_fops,
diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/staging/omapdrm/omap_drv.h
index 40b626b..d86ccb0 100644
--- a/drivers/staging/omapdrm/omap_drv.h
+++ b/drivers/staging/omapdrm/omap_drv.h
@@ -27,6 +27,7 @@
 #include <drm/drm_crtc_helper.h>
 #include <linux/platform_data/omap_drm.h>
 #include "omap_drm.h"
+#include "omap_atomic.h"
 
 /* APIs we need from dispc.. TODO omapdss should export these */
 void dispc_clear_irqs(u32 mask);
@@ -80,15 +81,6 @@ void hdmi_dump_regs(struct seq_file *s);
  */
 #define MAX_MAPPERS 2
 
-/* parameters which describe (unrotated) coordinates of scanout within a fb: */
-struct omap_drm_window {
-	uint32_t rotation;
-	int32_t  crtc_x, crtc_y;		/* signed because can be offscreen */
-	uint32_t crtc_w, crtc_h;
-	uint32_t src_x, src_y;
-	uint32_t src_w, src_h;
-};
-
 /* Once GO bit is set, we can't make further updates to shadowed registers
  * until the GO bit is cleared.  So various parts in the kms code that need
  * to update shadowed registers queue up a pair of callbacks, pre_apply
@@ -144,9 +136,16 @@ struct omap_drm_private {
 	struct drm_property *zorder_prop;
 
 	/* irq handling: */
-	struct list_head irq_list;    /* list of omap_drm_irq */
+	struct list_head irq_list;   /* list of omap_drm_irq */
 	uint32_t vblank_mask;         /* irq bits set for userspace vblank */
 	struct omap_drm_irq error_handler;
+
+	/* atomic: */
+	bool global_atomic;           /* in global atomic update (ie. modeset) */
+	bool crtc_atomic[8];          /* in per-crtc atomic update (ie. pageflip) */
+
+	/* pending vblank event per CRTC: */
+	struct drm_pending_vblank_event *event[8];
 };
 
 /* this should probably be in drm-core to standardize amongst drivers */
@@ -179,6 +178,10 @@ void omap_fbdev_free(struct drm_device *dev);
 
 const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc);
 enum omap_channel omap_crtc_channel(struct drm_crtc *crtc);
+int omap_crtc_check_state(struct drm_crtc *crtc,
+		struct omap_crtc_state *crtc_state);
+void omap_crtc_commit_state(struct drm_crtc *crtc,
+		struct omap_crtc_state *crtc_state);
 int omap_crtc_apply(struct drm_crtc *crtc,
 		struct omap_drm_apply *apply);
 struct drm_crtc *omap_crtc_init(struct drm_device *dev,
@@ -187,17 +190,14 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
 struct drm_plane *omap_plane_init(struct drm_device *dev,
 		int plane_id, bool private_plane);
 int omap_plane_dpms(struct drm_plane *plane, int mode);
-int omap_plane_mode_set(struct drm_plane *plane,
-		struct drm_crtc *crtc, struct drm_framebuffer *fb,
-		int crtc_x, int crtc_y,
-		unsigned int crtc_w, unsigned int crtc_h,
-		uint32_t src_x, uint32_t src_y,
-		uint32_t src_w, uint32_t src_h,
-		void (*fxn)(void *), void *arg);
 void omap_plane_install_properties(struct drm_plane *plane,
 		struct drm_mode_object *obj);
-int omap_plane_set_property(struct drm_plane *plane,
+int omap_plane_set_property(struct drm_plane *plane, void *state,
 		struct drm_property *property, uint64_t val);
+int omap_plane_check_state(struct drm_plane *plane,
+		struct omap_plane_state *plane_state);
+void omap_plane_commit_state(struct drm_plane *plane,
+		struct omap_plane_state *plane_state);
 
 struct drm_encoder *omap_encoder_init(struct drm_device *dev);
 struct drm_encoder *omap_connector_attached_encoder(
@@ -223,7 +223,7 @@ int omap_framebuffer_replace(struct drm_framebuffer *a,
 		struct drm_framebuffer *b, void *arg,
 		void (*unpin)(void *arg, struct drm_gem_object *bo));
 void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
-		struct omap_drm_window *win, struct omap_overlay_info *info);
+		struct drm_plane_state *state, struct omap_overlay_info *info);
 struct drm_connector *omap_framebuffer_get_next_connector(
 		struct drm_framebuffer *fb, struct drm_connector *from);
 void omap_framebuffer_flush(struct drm_framebuffer *fb,
@@ -309,6 +309,13 @@ static inline uint32_t pipe2vbl(int crtc)
 	return dispc_mgr_get_vsync_irq(channel);
 }
 
+/* is the specified pipe/crtc blocked for atomic update? */
+static inline bool pipe_in_atomic(struct drm_device *dev, int pipe)
+{
+	struct omap_drm_private *priv = dev->dev_private;
+	return priv->global_atomic || priv->crtc_atomic[pipe];
+}
+
 /* should these be made into common util helpers?
  */
 
diff --git a/drivers/staging/omapdrm/omap_fb.c b/drivers/staging/omapdrm/omap_fb.c
index 8025670..669afd9 100644
--- a/drivers/staging/omapdrm/omap_fb.c
+++ b/drivers/staging/omapdrm/omap_fb.c
@@ -154,33 +154,34 @@ static uint32_t get_linear_addr(struct plane *plane,
 /* update ovl info for scanout, handles cases of multi-planar fb's, etc.
  */
 void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
-		struct omap_drm_window *win, struct omap_overlay_info *info)
+		struct drm_plane_state *state, struct omap_overlay_info *info)
 {
 	struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
+	struct omap_plane_state *plane_state = to_omap_plane_state(state);
 	const struct format *format = omap_fb->format;
 	struct plane *plane = &omap_fb->planes[0];
 	uint32_t x, y, orient = 0;
 
 	info->color_mode = format->dss_format;
 
-	info->pos_x      = win->crtc_x;
-	info->pos_y      = win->crtc_y;
-	info->out_width  = win->crtc_w;
-	info->out_height = win->crtc_h;
-	info->width      = win->src_w;
-	info->height     = win->src_h;
+	info->pos_x      = state->crtc_x;
+	info->pos_y      = state->crtc_y;
+	info->out_width  = state->crtc_w;
+	info->out_height = state->crtc_h;
+	info->width      = state->src_w >> 16;
+	info->height     = state->src_h >> 16;
 
-	x = win->src_x;
-	y = win->src_y;
+	x = state->src_x >> 16;
+	y = state->src_y >> 16;
 
 	if (omap_gem_flags(plane->bo) & OMAP_BO_TILED) {
-		uint32_t w = win->src_w;
-		uint32_t h = win->src_h;
+		uint32_t w = state->src_w >> 16;
+		uint32_t h = state->src_h >> 16;
 
-		switch (win->rotation & 0xf) {
+		switch (plane_state->rotation & 0xf) {
 		default:
 			dev_err(fb->dev->dev, "invalid rotation: %02x",
-					(uint32_t)win->rotation);
+					plane_state->rotation);
 			/* fallthru to default to no rotation */
 		case 0:
 		case BIT(DRM_ROTATE_0):
@@ -197,10 +198,10 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
 			break;
 		}
 
-		if (win->rotation & BIT(DRM_REFLECT_X))
+		if (plane_state->rotation & BIT(DRM_REFLECT_X))
 			orient ^= MASK_X_INVERT;
 
-		if (win->rotation & BIT(DRM_REFLECT_Y))
+		if (plane_state->rotation & BIT(DRM_REFLECT_Y))
 			orient ^= MASK_Y_INVERT;
 
 		/* adjust x,y offset for flip/invert: */
@@ -220,6 +221,9 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
 		info->screen_width  = plane->pitch;
 	}
 
+	/* always do the rotation in tiler (or none at all): */
+	info->rotation = OMAP_DSS_ROT_0;
+
 	/* convert to pixels: */
 	info->screen_width /= format->planes[0].stride_bpp;
 
@@ -316,7 +320,7 @@ struct drm_connector *omap_framebuffer_get_next_connector(
 		if (connector != from) {
 			struct drm_encoder *encoder = connector->encoder;
 			struct drm_crtc *crtc = encoder ? encoder->crtc : NULL;
-			if (crtc && crtc->fb == fb) {
+			if (crtc && crtc->state->fb == fb) {
 				return connector;
 			}
 		}
@@ -342,10 +346,10 @@ void omap_framebuffer_flush(struct drm_framebuffer *fb,
 			 * could do the coordinate translation..
 			 */
 			struct drm_crtc *crtc = connector->encoder->crtc;
-			int cx = max(0, x - crtc->x);
-			int cy = max(0, y - crtc->y);
-			int cw = w + (x - crtc->x) - cx;
-			int ch = h + (y - crtc->y) - cy;
+			int cx = max(0, x - crtc->state->x);
+			int cy = max(0, y - crtc->state->y);
+			int cw = w + (x - crtc->state->x) - cx;
+			int ch = h + (y - crtc->state->y) - cy;
 
 			omap_connector_flush(connector, cx, cy, cw, ch);
 		}
diff --git a/drivers/staging/omapdrm/omap_plane.c b/drivers/staging/omapdrm/omap_plane.c
index 61b53f8..ec179be 100644
--- a/drivers/staging/omapdrm/omap_plane.c
+++ b/drivers/staging/omapdrm/omap_plane.c
@@ -32,24 +32,14 @@
  * plane funcs
  */
 
-struct callback {
-	void (*fxn)(void *);
-	void *arg;
-};
-
 #define to_omap_plane(x) container_of(x, struct omap_plane, base)
 
 struct omap_plane {
 	struct drm_plane base;
 	int id;  /* TODO rename omap_plane -> omap_plane_id in omapdss so I can use the enum */
 	const char *name;
-	struct omap_overlay_info info;
 	struct omap_drm_apply apply;
 
-	/* position/orientation of scanout within the fb: */
-	struct omap_drm_window win;
-	bool enabled;
-
 	/* last fb that we pinned: */
 	struct drm_framebuffer *pinned_fb;
 
@@ -58,9 +48,6 @@ struct omap_plane {
 
 	/* set of bo's pending unpin until next post_apply() */
 	DECLARE_KFIFO_PTR(unpin_fifo, struct drm_gem_object *);
-
-	// XXX maybe get rid of this and handle vblank in crtc too?
-	struct callback apply_done_cb;
 };
 
 static void unpin(void *arg, struct drm_gem_object *bo)
@@ -112,24 +99,53 @@ static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb)
 	return 0;
 }
 
+static inline bool is_enabled(struct drm_plane_state *state)
+{
+	return to_omap_plane_state(state)->enabled &&
+			state->crtc && state->fb;
+}
+
+/* TODO get rid of this and convert dispc code to use drm state
+ * structs directly..
+ */
+static void state2info(struct omap_plane_state *plane_state,
+		struct omap_overlay_info *info)
+{
+
+	memset(info, 0, sizeof(*info));
+
+	info->global_alpha = 0xff;
+	/* TODO: we should calculate valid zorder from all the planes: */
+	info->zorder = plane_state->zorder;
+
+	/* update scanout: */
+	omap_framebuffer_update_scanout(plane_state->base.fb,
+			&plane_state->base, info);
+
+	DBG("%dx%d -> %dx%d (%d)", info->width, info->height,
+			info->out_width, info->out_height, info->screen_width);
+	DBG("%d,%d %08x %08x", info->pos_x, info->pos_y,
+			info->paddr, info->p_uv_addr);
+}
+
 static void omap_plane_pre_apply(struct omap_drm_apply *apply)
 {
 	struct omap_plane *omap_plane =
 			container_of(apply, struct omap_plane, apply);
-	struct omap_drm_window *win = &omap_plane->win;
 	struct drm_plane *plane = &omap_plane->base;
 	struct drm_device *dev = plane->dev;
-	struct omap_overlay_info *info = &omap_plane->info;
-	struct drm_crtc *crtc = plane->crtc;
+	struct omap_overlay_info info;
+	struct drm_crtc *crtc = plane->state->crtc;
+	struct drm_framebuffer *fb = plane->state->fb;
 	enum omap_channel channel;
-	bool enabled = omap_plane->enabled && crtc;
-	bool ilace, replication;
+	bool enabled = is_enabled(plane->state);
+	bool replication;
 	int ret;
 
 	DBG("%s, enabled=%d", omap_plane->name, enabled);
 
 	/* if fb has changed, pin new fb: */
-	update_pin(plane, enabled ? plane->fb : NULL);
+	update_pin(plane, enabled ? fb : NULL);
 
 	if (!enabled) {
 		dispc_ovl_enable(omap_plane->id, false);
@@ -138,21 +154,13 @@ static void omap_plane_pre_apply(struct omap_drm_apply *apply)
 
 	channel = omap_crtc_channel(crtc);
 
-	/* update scanout: */
-	omap_framebuffer_update_scanout(plane->fb, win, info);
-
-	DBG("%dx%d -> %dx%d (%d)", info->width, info->height,
-			info->out_width, info->out_height,
-			info->screen_width);
-	DBG("%d,%d %08x %08x", info->pos_x, info->pos_y,
-			info->paddr, info->p_uv_addr);
+	state2info(to_omap_plane_state(plane->state), &info);
 
 	/* TODO: */
-	ilace = false;
 	replication = false;
 
 	/* and finally, update omapdss: */
-	ret = dispc_ovl_setup(omap_plane->id, channel, info, ilace,
+	ret = dispc_ovl_setup(omap_plane->id, channel, &info,
 			replication, omap_crtc_timings(crtc));
 	if (ret) {
 		dev_err(dev->dev, "dispc_ovl_setup failed: %d\n", ret);
@@ -168,116 +176,48 @@ static void omap_plane_post_apply(struct omap_drm_apply *apply)
 	struct omap_plane *omap_plane =
 			container_of(apply, struct omap_plane, apply);
 	struct drm_plane *plane = &omap_plane->base;
-	struct omap_overlay_info *info = &omap_plane->info;
 	struct drm_gem_object *bo = NULL;
-	struct callback cb;
-
-	cb = omap_plane->apply_done_cb;
-	omap_plane->apply_done_cb.fxn = NULL;
 
 	while (kfifo_get(&omap_plane->unpin_fifo, &bo)) {
 		omap_gem_put_paddr(bo);
 		drm_gem_object_unreference_unlocked(bo);
 	}
 
-	if (cb.fxn)
-		cb.fxn(cb.arg);
+	omap_atomic_plane_update(plane->dev, omap_plane->id);
 
-	if (omap_plane->enabled) {
-		omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y,
-				info->out_width, info->out_height);
-	}
-}
-
-static int apply(struct drm_plane *plane)
-{
-	if (plane->crtc) {
-		struct omap_plane *omap_plane = to_omap_plane(plane);
-		return omap_crtc_apply(plane->crtc, &omap_plane->apply);
+	if (is_enabled(plane->state)) {
+		struct drm_plane_state *state = plane->state;
+		omap_framebuffer_flush(plane->state->fb,
+				state->crtc_x, state->crtc_y,
+				state->crtc_w, state->crtc_h);
 	}
-	return 0;
-}
-
-int omap_plane_mode_set(struct drm_plane *plane,
-		struct drm_crtc *crtc, struct drm_framebuffer *fb,
-		int crtc_x, int crtc_y,
-		unsigned int crtc_w, unsigned int crtc_h,
-		uint32_t src_x, uint32_t src_y,
-		uint32_t src_w, uint32_t src_h,
-		void (*fxn)(void *), void *arg)
-{
-	struct omap_plane *omap_plane = to_omap_plane(plane);
-	struct omap_drm_window *win = &omap_plane->win;
-
-	win->crtc_x = crtc_x;
-	win->crtc_y = crtc_y;
-	win->crtc_w = crtc_w;
-	win->crtc_h = crtc_h;
-
-	/* src values are in Q16 fixed point, convert to integer: */
-	win->src_x = src_x >> 16;
-	win->src_y = src_y >> 16;
-	win->src_w = src_w >> 16;
-	win->src_h = src_h >> 16;
-
-	if (fxn) {
-		/* omap_crtc should ensure that a new page flip
-		 * isn't permitted while there is one pending:
-		 */
-		BUG_ON(omap_plane->apply_done_cb.fxn);
-
-		omap_plane->apply_done_cb.fxn = fxn;
-		omap_plane->apply_done_cb.arg = arg;
-	}
-
-	plane->fb = fb;
-	plane->crtc = crtc;
-
-	return apply(plane);
-}
-
-static int omap_plane_update(struct drm_plane *plane,
-		struct drm_crtc *crtc, struct drm_framebuffer *fb,
-		int crtc_x, int crtc_y,
-		unsigned int crtc_w, unsigned int crtc_h,
-		uint32_t src_x, uint32_t src_y,
-		uint32_t src_w, uint32_t src_h)
-{
-	struct omap_plane *omap_plane = to_omap_plane(plane);
-	omap_plane->enabled = true;
-	return omap_plane_mode_set(plane, crtc, fb,
-			crtc_x, crtc_y, crtc_w, crtc_h,
-			src_x, src_y, src_w, src_h,
-			NULL, NULL);
-}
-
-static int omap_plane_disable(struct drm_plane *plane)
-{
-	struct omap_plane *omap_plane = to_omap_plane(plane);
-	omap_plane->win.rotation = BIT(DRM_ROTATE_0);
-	return omap_plane_dpms(plane, DRM_MODE_DPMS_OFF);
 }
 
 static void omap_plane_destroy(struct drm_plane *plane)
 {
 	struct omap_plane *omap_plane = to_omap_plane(plane);
 	DBG("%s", omap_plane->name);
-	omap_plane_disable(plane);
+	omap_plane_dpms(plane, DRM_MODE_DPMS_OFF);
 	drm_plane_cleanup(plane);
 	WARN_ON(!kfifo_is_empty(&omap_plane->unpin_fifo));
 	kfifo_free(&omap_plane->unpin_fifo);
+	kfree(plane->state);
 	kfree(omap_plane);
 }
 
 int omap_plane_dpms(struct drm_plane *plane, int mode)
 {
 	struct omap_plane *omap_plane = to_omap_plane(plane);
+	struct drm_plane_state *state = plane->state;
 	bool enabled = (mode == DRM_MODE_DPMS_ON);
 	int ret = 0;
 
-	if (enabled != omap_plane->enabled) {
-		omap_plane->enabled = enabled;
-		ret = apply(plane);
+	DBG("%s: mode=%d", omap_plane->name, mode);
+
+	if (enabled != is_enabled(state)) {
+		to_omap_plane_state(state)->enabled = enabled;
+		if (state->crtc)
+			ret = omap_crtc_apply(state->crtc, &omap_plane->apply);
 	}
 
 	return ret;
@@ -319,29 +259,114 @@ void omap_plane_install_properties(struct drm_plane *plane,
 	drm_object_attach_property(obj, prop, 0);
 }
 
-int omap_plane_set_property(struct drm_plane *plane,
+int omap_plane_set_property(struct drm_plane *plane, void *state,
 		struct drm_property *property, uint64_t val)
 {
 	struct omap_plane *omap_plane = to_omap_plane(plane);
 	struct omap_drm_private *priv = plane->dev->dev_private;
-	int ret = -EINVAL;
+	struct omap_plane_state *plane_state =
+			omap_atomic_plane_state(state, omap_plane->id);
+	int ret;
+
+	DBG("%s: %s = %llx", omap_plane->name, property->name, val);
+
+	ret = drm_plane_set_property(plane, &plane_state->base, property, val);
+	if (!ret) {
+		/* if this property is handled by base, we are nearly done..
+		 * we just need to register an fb property w/ atomic so that
+		 * commit can be deferred until the fb is ready
+		 */
+		struct drm_mode_config *config = &plane->dev->mode_config;
+		if ((property == config->prop_fb_id) && val) {
+			struct drm_mode_object *obj =
+					drm_property_get_obj(property, val);
+			omap_atomic_add_fb(state, obj_to_fb(obj));
+		}
+		return ret;
+	}
+
+	/* if it is not a base plane property, see if it is one of ours: */
 
 	if (property == priv->rotation_prop) {
 		DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val);
-		omap_plane->win.rotation = val;
-		ret = apply(plane);
+		plane_state->rotation = val;
 	} else if (property == priv->zorder_prop) {
 		DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val);
-		omap_plane->info.zorder = val;
-		ret = apply(plane);
+		plane_state->zorder = val;
+	} else {
+		return -EINVAL;
 	}
 
-	return ret;
+	return 0;
+}
+
+int omap_plane_check_state(struct drm_plane *plane,
+		struct omap_plane_state *plane_state)
+{
+	struct omap_plane *omap_plane = to_omap_plane(plane);
+	struct drm_crtc *crtc = plane_state->base.crtc;
+	struct omap_overlay_info info;
+	int ret, x_predecim, y_predecim;
+	bool replication;
+
+	if (!is_enabled(&plane_state->base))
+		return 0;
+
+	ret = drm_plane_check_state(plane, &plane_state->base);
+	if (ret)
+		return ret;
+
+	state2info(plane_state, &info);
+
+	/* TODO: */
+	replication = false;
+
+	ret = dispc_ovl_check(omap_plane->id, omap_crtc_channel(crtc),
+			&info, replication, omap_crtc_timings(crtc),
+			&x_predecim, &y_predecim);
+	if (ret) {
+		DBG("%s: dispc_ovl_check failed: %d", omap_plane->name, ret);
+		return ret;
+	}
+
+	/* TODO add some properties to set max pre-decimation.. but
+	 * until then, we'd rather fallback to GPU than decimate:
+	 */
+	if ((x_predecim > 1) || (y_predecim > 1)) {
+		DBG("%s: x_predecim=%d, y_predecim=%d", omap_plane->name,
+				x_predecim, y_predecim);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+void omap_plane_commit_state(struct drm_plane *plane,
+		struct omap_plane_state *plane_state)
+{
+	struct omap_plane *omap_plane = to_omap_plane(plane);
+	struct omap_plane_state *old_state = to_omap_plane_state(plane->state);
+	struct drm_crtc *crtc;
+
+	DBG("%s", omap_plane->name);
+
+	crtc = plane_state->base.crtc;
+
+	/* TODO we need to handle crtc switch.. we should reject that
+	 * at the check() stage if we are still waiting for GO to clear
+	 * on the outgoing crtc..
+	 */
+	if (!crtc)
+		crtc = plane->state->crtc;
+
+	drm_plane_commit_state(plane, &plane_state->base);
+	kfree(old_state);
+
+	if (crtc)
+		omap_crtc_apply(crtc, &omap_plane->apply);
 }
 
 static const struct drm_plane_funcs omap_plane_funcs = {
-		.update_plane = omap_plane_update,
-		.disable_plane = omap_plane_disable,
 		.destroy = omap_plane_destroy,
 		.set_property = omap_plane_set_property,
 };
@@ -360,7 +385,6 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
 	struct omap_drm_private *priv = dev->dev_private;
 	struct drm_plane *plane = NULL;
 	struct omap_plane *omap_plane;
-	struct omap_overlay_info *info;
 	int ret;
 
 	DBG("%s: priv=%d", plane_names[id], private_plane);
@@ -385,6 +409,13 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
 
 	plane = &omap_plane->base;
 
+	plane->state = kzalloc(sizeof(struct omap_plane_state), GFP_KERNEL);
+
+	if (!plane->state) {
+		dev_err(dev->dev, "could not allocate CRTC state\n");
+		goto fail;
+	}
+
 	omap_plane->apply.pre_apply  = omap_plane_pre_apply;
 	omap_plane->apply.post_apply = omap_plane_post_apply;
 
@@ -393,24 +424,15 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
 
 	omap_plane_install_properties(plane, &plane->base);
 
-	/* get our starting configuration, set defaults for parameters
-	 * we don't currently use, etc:
-	 */
-	info = &omap_plane->info;
-	info->rotation_type = OMAP_DSS_ROT_DMA;
-	info->rotation = OMAP_DSS_ROT_0;
-	info->global_alpha = 0xff;
-	info->mirror = 0;
-
 	/* Set defaults depending on whether we are a CRTC or overlay
 	 * layer.
-	 * TODO add ioctl to give userspace an API to change this.. this
-	 * will come in a subsequent patch.
 	 */
-	if (private_plane)
-		omap_plane->info.zorder = 0;
-	else
-		omap_plane->info.zorder = id;
+	if (!private_plane) {
+		struct omap_plane_state *plane_state =
+				to_omap_plane_state(plane->state);
+		plane_state->zorder = id;
+		plane_state->enabled = true;
+	}
 
 	return plane;
 
-- 
1.7.9.5

  reply	other threads:[~2012-09-13  4:00 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-09-13  4:00 [RFC 10/11] drm: atomic pageflip Rob Clark
2012-09-13  4:00 ` Rob Clark [this message]
2012-10-13  0:49 [RFC 00/11] atomic pageflip v3 Rob Clark
2012-10-13  0:49 ` [RFC 11/11] drm/omap: update for atomic age Rob Clark

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=1347508811-15029-2-git-send-email-rob.clark@linaro.org \
    --to=rob.clark@linaro.org \
    --cc=daniel.vetter@ffwll.ch \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=patches@linaro.org \
    --cc=rob@ti.com \
    /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.