All of lore.kernel.org
 help / color / mirror / Atom feed
From: Rob Clark <rob.clark@linaro.org>
To: dri-devel@lists.freedesktop.org
Cc: patches@linaro.org, daniel.vetter@ffwll.ch, Rob Clark <rob@ti.com>
Subject: [RFC 11/11] drm/omap: update for atomic age
Date: Fri, 12 Oct 2012 19:49:12 -0500	[thread overview]
Message-ID: <1350089352-18162-12-git-send-email-rob.clark@linaro.org> (raw)
In-Reply-To: <1350089352-18162-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 |  347 +++++++++++++++++++++++++++++++++
 drivers/staging/omapdrm/omap_atomic.h |   52 +++++
 drivers/staging/omapdrm/omap_crtc.c   |  231 +++++++++++-----------
 drivers/staging/omapdrm/omap_drv.c    |   27 ++-
 drivers/staging/omapdrm/omap_drv.h    |   43 ++--
 drivers/staging/omapdrm/omap_fb.c     |   34 ++--
 drivers/staging/omapdrm/omap_plane.c  |  291 ++++++++++++++-------------
 8 files changed, 737 insertions(+), 289 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..fbf03f1
--- /dev/null
+++ b/drivers/staging/omapdrm/omap_atomic.c
@@ -0,0 +1,347 @@
+/*
+ * 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;
+}
+
+static void set_atomic(struct omap_atomic_state *omap_state, bool atomic)
+{
+	struct omap_drm_private *priv = omap_state->dev->dev_private;
+	if (omap_state->crtc) {
+		int pipe = omap_state->pipe;
+		priv->crtc_atomic[pipe] = atomic;
+	} else {
+		priv->global_atomic = atomic;
+	}
+}
+
+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]);
+	} else {
+		WARN_ON(priv->global_atomic);
+	}
+
+	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);
+
+	set_atomic(omap_state, true);
+
+	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) {
+		set_atomic(omap_state, false);
+		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);
+	}
+
+	set_atomic(omap_state, 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 b903c63..f9af31e 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 dss_lcd_mgr_config lcd_config;
 
@@ -50,21 +49,10 @@ struct omap_crtc {
 	/* list of queued apply's: */
 	struct list_head queued_applies;
 
+	bool in_apply;       /* for debug */
+
 	/* 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;
-	struct drm_framebuffer *old_fb;
-
-	/* 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;
 };
 
 /*
@@ -132,6 +120,9 @@ static const struct dss_mgr_ops mgr_ops = {
  * CRTC funcs:
  */
 
+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);
@@ -146,6 +137,7 @@ static void omap_crtc_destroy(struct drm_crtc *crtc)
 
 	channel2crtc[omap_crtc->channel] = NULL;
 
+	kfree(crtc->state);
 	kfree(omap_crtc);
 }
 
@@ -206,11 +198,7 @@ static int omap_crtc_mode_set(struct drm_crtc *crtc,
 		}
 	}
 
-	return omap_plane_mode_set(omap_crtc->plane, crtc, crtc->state->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)
@@ -232,99 +220,61 @@ 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->state->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)
-{
-}
-
-static void vblank_cb(void *arg)
-{
-	struct drm_crtc *crtc = arg;
-	struct drm_device *dev = crtc->dev;
-	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
-	unsigned long flags;
-
-	spin_lock_irqsave(&dev->event_lock, flags);
-
-	/* wakeup userspace */
-	if (omap_crtc->event)
-		drm_send_vblank_event(dev, omap_crtc->pipe, omap_crtc->event);
-
-	omap_crtc->event = NULL;
-	omap_crtc->old_fb = NULL;
-
-	spin_unlock_irqrestore(&dev->event_lock, flags);
-}
-
-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;
+	void *state;
+	int w, h, ret;
 
-	mutex_lock(&dev->mode_config.mutex);
-	omap_plane_mode_set(omap_crtc->plane, crtc, crtc->state->fb,
-			0, 0, mode->hdisplay, mode->vdisplay,
-			crtc->state->x << 16, crtc->state->y << 16,
-			mode->hdisplay << 16, mode->vdisplay << 16,
-			vblank_cb, crtc);
-	mutex_unlock(&dev->mode_config.mutex);
+	if (WARN_ON(!crtc->state->fb))
+		return -EINVAL;
 
-	bo = omap_framebuffer_bo(crtc->state->fb, 0);
-	drm_gem_object_unreference_unlocked(bo);
-}
+	w = mode->hdisplay;
+	h = mode->vdisplay;
 
-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;
+	if (crtc->state->invert_dimensions) {
+		swap(w, h);
+		swap(x, y);
+	}
 
-	/* avoid assumptions about what ctxt we are called from: */
-	queue_work(priv->wq, &omap_crtc->page_flip_work);
+	/* 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, w << 16) ||
+		drm_mode_plane_set_obj_prop(plane, state,
+				config->prop_src_h, h << 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 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->state->fb ? crtc->state->fb->base.id : -1,
-			fb->base.id, event);
-
-	if (omap_crtc->old_fb) {
-		dev_err(dev->dev, "already a pending flip\n");
-		return -EINVAL;
-	}
-
-	omap_crtc->event = event;
-	crtc->state->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, void *state,
@@ -332,9 +282,23 @@ static int omap_crtc_set_property(struct drm_crtc *crtc, void *state,
 {
 	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->state->invert_dimensions =
+		crtc_state->base.invert_dimensions =
 				!!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270)));
 	}
 
@@ -344,7 +308,6 @@ static int omap_crtc_set_property(struct drm_crtc *crtc, void *state,
 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,
 };
 
@@ -370,6 +333,24 @@ 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);
+	DBG("%s", omap_crtc->name);
+	drm_crtc_commit_state(crtc, &crtc_state->base);
+	kfree(old_state);
+	omap_crtc_apply(crtc, &omap_crtc->apply);
+}
+
 static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
 {
 	struct omap_crtc *omap_crtc =
@@ -407,6 +388,7 @@ static void apply_worker(struct work_struct *work)
 	 * with respect to modesetting ioctls from userspace.
 	 */
 	mutex_lock(&dev->mode_config.mutex);
+	omap_crtc->in_apply = true;
 	dispc_runtime_get();
 
 	/*
@@ -423,6 +405,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: */
@@ -449,6 +434,7 @@ static void apply_worker(struct work_struct *work)
 
 out:
 	dispc_runtime_put();
+	omap_crtc->in_apply = false;
 	mutex_unlock(&dev->mode_config.mutex);
 }
 
@@ -459,13 +445,17 @@ int omap_crtc_apply(struct drm_crtc *crtc,
 	struct drm_device *dev = crtc->dev;
 
 	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+	WARN_ON(omap_crtc->in_apply);
 
 	/* 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
@@ -539,6 +529,7 @@ static void omap_crtc_pre_apply(struct omap_drm_apply *apply)
 	struct omap_crtc *omap_crtc =
 			container_of(apply, struct omap_crtc, apply);
 	enum omap_channel channel = omap_crtc->channel;
+	struct omap_overlay_manager_info info = {0};
 
 	DBG("%s: enabled=%d, timings_valid=%d", omap_crtc->name,
 			omap_crtc->enabled,
@@ -549,9 +540,14 @@ static void omap_crtc_pre_apply(struct omap_drm_apply *apply)
 		return;
 	}
 
+	info.default_color = 0x00000000;
+	info.trans_key = 0x00000000;
+	info.trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
+	info.trans_enabled = false;
+
 	if (dss_mgr_is_lcd(channel))
 		dispc_mgr_set_lcd_config(channel, &omap_crtc->lcd_config);
-	dispc_mgr_setup(channel, &omap_crtc->info);
+	dispc_mgr_setup(channel, &info);
 	dispc_mgr_set_timings(channel, &omap_crtc->timings);
 	set_enabled(&omap_crtc->base, true);
 }
@@ -573,7 +569,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]);
 
@@ -586,7 +581,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);
@@ -612,12 +613,6 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
 	channel2crtc[omap_crtc->channel] = crtc;
 	dss_install_mgr_ops(&mgr_ops);
 
-	/* 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 7321510..0b180b7 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"
@@ -510,6 +511,7 @@ static int dev_firstopen(struct drm_device *dev)
  */
 static void dev_lastclose(struct drm_device *dev)
 {
+	void *state;
 	int i;
 
 	/* we don't support vga-switcheroo.. so just make sure the fbdev
@@ -525,11 +527,16 @@ static void dev_lastclose(struct drm_device *dev)
 	 * a flag that properties should automatically be restored to
 	 * default state on lastclose?
 	 */
+	state = dev->driver->atomic_begin(dev, NULL);
 	for (i = 0; i < priv->num_planes; i++) {
 		struct drm_plane *plane = priv->planes[i];
-		drm_object_property_set_value(&plane->base,
-				&plane->state->propvals, priv->rotation_prop, 0);
+		drm_mode_plane_set_obj_prop(plane, state,
+				priv->rotation_prop, 0);
 	}
+	/* disabling rotation won't ever fail: */
+	dev->driver->atomic_check(dev, state);
+	dev->driver->atomic_commit(dev, state, NULL);
+	dev->driver->atomic_end(dev, state);
 
 	mutex_lock(&dev->mode_config.mutex);
 	ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev);
@@ -540,7 +547,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)
@@ -597,6 +616,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 c20ed7e..131f0a8 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 */
 #include "../../video/omap2/dss/dss.h"
@@ -47,15 +48,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
@@ -119,9 +111,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 */
@@ -154,6 +153,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,
@@ -162,17 +165,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, 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(
@@ -198,7 +198,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,
@@ -284,6 +284,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 66c11f9..3d6350d 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;
 
diff --git a/drivers/staging/omapdrm/omap_plane.c b/drivers/staging/omapdrm/omap_plane.c
index cb5d427..6881532 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;
 
@@ -60,9 +50,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)
@@ -114,24 +101,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 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->state->fb : NULL);
+	update_pin(plane, enabled ? fb : NULL);
 
 	if (!enabled) {
 		dispc_ovl_enable(omap_plane->id, false);
@@ -140,21 +156,14 @@ static void omap_plane_pre_apply(struct omap_drm_apply *apply)
 
 	channel = omap_crtc_channel(crtc);
 
-	/* update scanout: */
-	omap_framebuffer_update_scanout(plane->state->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, info,
+	dispc_ovl_set_channel_out(omap_plane->id, channel);
+	ret = dispc_ovl_setup(omap_plane->id, &info,
 			replication, omap_crtc_timings(crtc), false);
 	if (ret) {
 		dev_err(dev->dev, "dispc_ovl_setup failed: %d\n", ret);
@@ -162,7 +171,6 @@ static void omap_plane_pre_apply(struct omap_drm_apply *apply)
 	}
 
 	dispc_ovl_enable(omap_plane->id, true);
-	dispc_ovl_set_channel_out(omap_plane->id, channel);
 }
 
 static void omap_plane_post_apply(struct omap_drm_apply *apply)
@@ -170,94 +178,21 @@ 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);
-
-	if (omap_plane->enabled) {
-		omap_framebuffer_flush(plane->state->fb, info->pos_x, info->pos_y,
-				info->out_width, info->out_height);
-	}
-}
-
-static int apply(struct drm_plane *plane)
-{
-	if (plane->state->crtc) {
-		struct omap_plane *omap_plane = to_omap_plane(plane);
-		return omap_crtc_apply(plane->state->crtc, &omap_plane->apply);
-	}
-	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_atomic_plane_update(plane->dev, omap_plane->id);
 
-		omap_plane->apply_done_cb.fxn = fxn;
-		omap_plane->apply_done_cb.arg = arg;
+	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);
 	}
-
-	plane->state->fb = fb;
-	plane->state->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)
@@ -268,24 +203,29 @@ static void omap_plane_destroy(struct drm_plane *plane)
 
 	omap_irq_unregister(plane->dev, &omap_plane->error_irq);
 
-	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;
@@ -332,24 +272,106 @@ int omap_plane_set_property(struct drm_plane *plane, void *state,
 {
 	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 = 0, y_predecim = 0;
+
+	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);
+
+	ret = dispc_ovl_check(omap_plane->id,
+			omap_crtc_channel(crtc),
+			&info, 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,
 };
@@ -382,7 +404,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);
@@ -407,6 +428,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;
 
@@ -419,24 +447,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

  parent reply	other threads:[~2012-10-13  0:49 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-10-13  0:49 [RFC 00/11] atomic pageflip v3 Rob Clark
2012-10-13  0:49 ` [RFC 01/11] drm: add atomic fxns Rob Clark
2012-10-13  0:49 ` [RFC 02/11] drm: add object property type Rob Clark
2012-10-13  0:49 ` [RFC 03/11] drm: add DRM_MODE_PROP_DYNAMIC property flag Rob Clark
2012-10-13  0:49 ` [RFC 04/11] drm: add DRM_MODE_PROP_SIGNED " Rob Clark
2012-10-13  0:49 ` [RFC 05/11] drm: split property values out Rob Clark
2012-10-13  0:49 ` [RFC 06/11] drm: convert plane to properties Rob Clark
2012-10-13  0:49 ` [RFC 07/11] drm: add drm_plane_state Rob Clark
2012-10-13  0:49 ` [RFC 08/11] drm: convert page_flip to properties Rob Clark
2012-10-13  0:49 ` [RFC 09/11] drm: add drm_crtc_state Rob Clark
2012-10-13  0:49 ` [RFC 10/11] drm: atomic pageflip Rob Clark
2012-10-13  0:49 ` Rob Clark [this message]
2012-10-13  0:58 ` [RFC 00/11] atomic pageflip v3 Rob Clark
  -- strict thread matches above, loose matches on Subject: below --
2012-09-13  4:00 [RFC 10/11] drm: atomic pageflip Rob Clark
2012-09-13  4:00 ` [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=1350089352-18162-12-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.