[kernel] Further ARM config updates, Add patch to fix building omapdrm

Peter Robinson pbrobinson at fedoraproject.org
Mon Jan 7 19:50:29 UTC 2013


commit 6f61d3d33ccb9f50fce95ee050b0c5eba1b4ed4c
Author: Peter Robinson <pbrobinson at gmail.com>
Date:   Mon Jan 7 19:50:16 2013 +0000

    Further ARM config updates, Add patch to fix building omapdrm

 arm-omapdrm-fixinc.patch     | 2774 +++++++++++++++++++++++++++++++++++++++++-
 arm-tegra-nvec-kconfig.patch |    8 +-
 config-arm-generic           |    1 +
 config-arm-omap              |    4 +-
 config-arm-tegra             |    4 +-
 config-armv7                 |   51 +-
 kernel.spec                  |   14 +-
 7 files changed, 2827 insertions(+), 29 deletions(-)
---
diff --git a/arm-omapdrm-fixinc.patch b/arm-omapdrm-fixinc.patch
index 001b4c5..6892655 100644
--- a/arm-omapdrm-fixinc.patch
+++ b/arm-omapdrm-fixinc.patch
@@ -1,15 +1,2767 @@
---- linux-3.7.0-0.rc2.git4.2.fc19.x86_64/drivers/staging/omapdrm/omap_crtc.c.orig	2012-10-30 09:58:47.613641237 +0000
-+++ linux-3.7.0-0.rc2.git4.2.fc19.x86_64/drivers/staging/omapdrm/omap_crtc.c	2012-10-30 10:05:36.996081758 +0000
-@@ -19,9 +19,9 @@
+--- linux-3.8.0-0.rc2.git1.1.fc19.x86_64/arch/arm/mach-omap2/drm.c.orig	2013-01-07 12:31:44.014857064 +0000
++++ linux-3.8.0-0.rc2.git1.1.fc19.x86_64/arch/arm/mach-omap2/drm.c	2013-01-07 12:33:33.570861903 +0000
+@@ -27,6 +27,7 @@
  
- #include "omap_drv.h"
+ #include "omap_device.h"
+ #include "omap_hwmod.h"
++#include "soc.h"
  
--#include "drm_mode.h"
--#include "drm_crtc.h"
--#include "drm_crtc_helper.h"
-+#include <drm/drm_mode.h>
-+#include <drm/drm_crtc.h>
-+#include <drm/drm_crtc_helper.h>
+ #if defined(CONFIG_DRM_OMAP) || (CONFIG_DRM_OMAP_MODULE)
  
- #define to_omap_crtc(x) container_of(x, struct omap_crtc, base)
+@@ -56,7 +57,7 @@
+ 			oh->name);
+ 	}
+ 
+-	platform_data.omaprev = GET_OMAP_REVISION();
++	platform_data.omaprev = GET_OMAP_TYPE;
+ 
+ 	return platform_device_register(&omap_drm_device);
+ 
+diff --git a/drivers/staging/omapdrm/Makefile b/drivers/staging/omapdrm/Makefile
+index 1ca0e00..d85e058 100644
+--- a/drivers/staging/omapdrm/Makefile
++++ b/drivers/staging/omapdrm/Makefile
+@@ -5,6 +5,7 @@ 
+ 
+ ccflags-y := -Iinclude/drm -Werror
+ omapdrm-y := omap_drv.o \
++	omap_irq.o \
+ 	omap_debugfs.o \
+ 	omap_crtc.o \
+ 	omap_plane.o \
+diff --git a/drivers/staging/omapdrm/TODO b/drivers/staging/omapdrm/TODO
+index 938c788..abeeb00 100644
+--- a/drivers/staging/omapdrm/TODO
++++ b/drivers/staging/omapdrm/TODO
+@@ -17,9 +17,6 @@  TODO
+ . Revisit GEM sync object infrastructure.. TTM has some framework for this
+   already.  Possibly this could be refactored out and made more common?
+   There should be some way to do this with less wheel-reinvention.
+-. Review DSS vs KMS mismatches.  The omap_dss_device is sort of part encoder,
+-  part connector.  Which results in a bit of duct tape to fwd calls from
+-  encoder to connector.  Possibly this could be done a bit better.
+ . Solve PM sequencing on resume.  DMM/TILER must be reloaded before any
+   access is made from any component in the system.  Which means on suspend
+   CRTC's should be disabled, and on resume the LUT should be reprogrammed
+diff --git a/drivers/staging/omapdrm/omap_connector.c b/drivers/staging/omapdrm/omap_connector.c
+index 91edb3f..4cc9ee7 100644
+--- a/drivers/staging/omapdrm/omap_connector.c
++++ b/drivers/staging/omapdrm/omap_connector.c
+@@ -31,9 +31,10 @@ 
+ struct omap_connector {
+ 	struct drm_connector base;
+ 	struct omap_dss_device *dssdev;
++	struct drm_encoder *encoder;
+ };
+ 
+-static inline void copy_timings_omap_to_drm(struct drm_display_mode *mode,
++void copy_timings_omap_to_drm(struct drm_display_mode *mode,
+ 		struct omap_video_timings *timings)
+ {
+ 	mode->clock = timings->pixel_clock;
+@@ -64,7 +65,7 @@  static inline void copy_timings_omap_to_drm(struct drm_display_mode *mode,
+ 		mode->flags |= DRM_MODE_FLAG_NVSYNC;
+ }
+ 
+-static inline void copy_timings_drm_to_omap(struct omap_video_timings *timings,
++void copy_timings_drm_to_omap(struct omap_video_timings *timings,
+ 		struct drm_display_mode *mode)
+ {
+ 	timings->pixel_clock = mode->clock;
+@@ -96,48 +97,7 @@  static inline void copy_timings_drm_to_omap(struct omap_video_timings *timings,
+ 	timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES;
+ }
+ 
+-static void omap_connector_dpms(struct drm_connector *connector, int mode)
+-{
+-	struct omap_connector *omap_connector = to_omap_connector(connector);
+-	struct omap_dss_device *dssdev = omap_connector->dssdev;
+-	int old_dpms;
+-
+-	DBG("%s: %d", dssdev->name, mode);
+-
+-	old_dpms = connector->dpms;
+-
+-	/* from off to on, do from crtc to connector */
+-	if (mode < old_dpms)
+-		drm_helper_connector_dpms(connector, mode);
+-
+-	if (mode == DRM_MODE_DPMS_ON) {
+-		/* store resume info for suspended displays */
+-		switch (dssdev->state) {
+-		case OMAP_DSS_DISPLAY_SUSPENDED:
+-			dssdev->activate_after_resume = true;
+-			break;
+-		case OMAP_DSS_DISPLAY_DISABLED: {
+-			int ret = dssdev->driver->enable(dssdev);
+-			if (ret) {
+-				DBG("%s: failed to enable: %d",
+-						dssdev->name, ret);
+-				dssdev->driver->disable(dssdev);
+-			}
+-			break;
+-		}
+-		default:
+-			break;
+-		}
+-	} else {
+-		/* TODO */
+-	}
+-
+-	/* from on to off, do from connector to crtc */
+-	if (mode > old_dpms)
+-		drm_helper_connector_dpms(connector, mode);
+-}
+-
+-enum drm_connector_status omap_connector_detect(
++static enum drm_connector_status omap_connector_detect(
+ 		struct drm_connector *connector, bool force)
+ {
+ 	struct omap_connector *omap_connector = to_omap_connector(connector);
+@@ -164,8 +124,6 @@  static void omap_connector_destroy(struct drm_connector *connector)
+ 	struct omap_connector *omap_connector = to_omap_connector(connector);
+ 	struct omap_dss_device *dssdev = omap_connector->dssdev;
+ 
+-	dssdev->driver->disable(dssdev);
+-
+ 	DBG("%s", omap_connector->dssdev->name);
+ 	drm_sysfs_connector_remove(connector);
+ 	drm_connector_cleanup(connector);
+@@ -261,36 +219,12 @@  static int omap_connector_mode_valid(struct drm_connector *connector,
+ struct drm_encoder *omap_connector_attached_encoder(
+ 		struct drm_connector *connector)
+ {
+-	int i;
+ 	struct omap_connector *omap_connector = to_omap_connector(connector);
+-
+-	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+-		struct drm_mode_object *obj;
+-
+-		if (connector->encoder_ids[i] == 0)
+-			break;
+-
+-		obj = drm_mode_object_find(connector->dev,
+-				connector->encoder_ids[i],
+-				DRM_MODE_OBJECT_ENCODER);
+-
+-		if (obj) {
+-			struct drm_encoder *encoder = obj_to_encoder(obj);
+-			struct omap_overlay_manager *mgr =
+-					omap_encoder_get_manager(encoder);
+-			DBG("%s: found %s", omap_connector->dssdev->name,
+-					mgr->name);
+-			return encoder;
+-		}
+-	}
+-
+-	DBG("%s: no encoder", omap_connector->dssdev->name);
+-
+-	return NULL;
++	return omap_connector->encoder;
+ }
+ 
+ static const struct drm_connector_funcs omap_connector_funcs = {
+-	.dpms = omap_connector_dpms,
++	.dpms = drm_helper_connector_dpms,
+ 	.detect = omap_connector_detect,
+ 	.fill_modes = drm_helper_probe_single_connector_modes,
+ 	.destroy = omap_connector_destroy,
+@@ -302,34 +236,6 @@  static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
+ 	.best_encoder = omap_connector_attached_encoder,
+ };
+ 
+-/* called from encoder when mode is set, to propagate settings to the dssdev */
+-void omap_connector_mode_set(struct drm_connector *connector,
+-		struct drm_display_mode *mode)
+-{
+-	struct drm_device *dev = connector->dev;
+-	struct omap_connector *omap_connector = to_omap_connector(connector);
+-	struct omap_dss_device *dssdev = omap_connector->dssdev;
+-	struct omap_dss_driver *dssdrv = dssdev->driver;
+-	struct omap_video_timings timings = {0};
+-
+-	copy_timings_drm_to_omap(&timings, mode);
+-
+-	DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
+-			omap_connector->dssdev->name,
+-			mode->base.id, mode->name, mode->vrefresh, mode->clock,
+-			mode->hdisplay, mode->hsync_start,
+-			mode->hsync_end, mode->htotal,
+-			mode->vdisplay, mode->vsync_start,
+-			mode->vsync_end, mode->vtotal, mode->type, mode->flags);
+-
+-	if (dssdrv->check_timings(dssdev, &timings)) {
+-		dev_err(dev->dev, "could not set timings\n");
+-		return;
+-	}
+-
+-	dssdrv->set_timings(dssdev, &timings);
+-}
+-
+ /* flush an area of the framebuffer (in case of manual update display that
+  * is not automatically flushed)
+  */
+@@ -344,7 +250,8 @@  void omap_connector_flush(struct drm_connector *connector,
+ 
+ /* initialize connector */
+ struct drm_connector *omap_connector_init(struct drm_device *dev,
+-		int connector_type, struct omap_dss_device *dssdev)
++		int connector_type, struct omap_dss_device *dssdev,
++		struct drm_encoder *encoder)
+ {
+ 	struct drm_connector *connector = NULL;
+ 	struct omap_connector *omap_connector;
+@@ -360,6 +267,8 @@  struct drm_connector *omap_connector_init(struct drm_device *dev,
+ 	}
+ 
+ 	omap_connector->dssdev = dssdev;
++	omap_connector->encoder = encoder;
++
+ 	connector = &omap_connector->base;
+ 
+ 	drm_connector_init(dev, connector, &omap_connector_funcs,
+diff --git a/drivers/staging/omapdrm/omap_crtc.c b/drivers/staging/omapdrm/omap_crtc.c
+index d87bd84..5c6ed60 100644
+--- a/drivers/staging/omapdrm/omap_crtc.c
++++ b/drivers/staging/omapdrm/omap_crtc.c
+@@ -28,19 +28,131 @@ 
+ struct omap_crtc {
+ 	struct drm_crtc base;
+ 	struct drm_plane *plane;
++
+ 	const char *name;
+-	int id;
++	int pipe;
++	enum omap_channel channel;
++	struct omap_overlay_manager_info info;
++
++	/*
++	 * Temporary: eventually this will go away, but it is needed
++	 * for now to keep the output's happy.  (They only need
++	 * mgr->id.)  Eventually this will be replaced w/ something
++	 * more common-panel-framework-y
++	 */
++	struct omap_overlay_manager mgr;
++
++	struct omap_video_timings timings;
++	bool enabled;
++	bool full_update;
++
++	struct omap_drm_apply apply;
++
++	struct omap_drm_irq apply_irq;
++	struct omap_drm_irq error_irq;
++
++	/* list of in-progress apply's: */
++	struct list_head pending_applies;
++
++	/* list of queued apply's: */
++	struct list_head queued_applies;
++
++	/* 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;
++};
++
++/*
++ * Manager-ops, callbacks from output when they need to configure
++ * the upstream part of the video pipe.
++ *
++ * Most of these we can ignore until we add support for command-mode
++ * panels.. for video-mode the crtc-helpers already do an adequate
++ * job of sequencing the setup of the video pipe in the proper order
++ */
++
++/* we can probably ignore these until we support command-mode panels: */
++static void omap_crtc_start_update(struct omap_overlay_manager *mgr)
++{
++}
++
++static int omap_crtc_enable(struct omap_overlay_manager *mgr)
++{
++	return 0;
++}
++
++static void omap_crtc_disable(struct omap_overlay_manager *mgr)
++{
++}
++
++static void omap_crtc_set_timings(struct omap_overlay_manager *mgr,
++		const struct omap_video_timings *timings)
++{
++	struct omap_crtc *omap_crtc = container_of(mgr, struct omap_crtc, mgr);
++	DBG("%s", omap_crtc->name);
++	omap_crtc->timings = *timings;
++	omap_crtc->full_update = true;
++}
++
++static void omap_crtc_set_lcd_config(struct omap_overlay_manager *mgr,
++		const struct dss_lcd_mgr_config *config)
++{
++	struct omap_crtc *omap_crtc = container_of(mgr, struct omap_crtc, mgr);
++	DBG("%s", omap_crtc->name);
++	dispc_mgr_set_lcd_config(omap_crtc->channel, config);
++}
++
++static int omap_crtc_register_framedone_handler(
++		struct omap_overlay_manager *mgr,
++		void (*handler)(void *), void *data)
++{
++	return 0;
++}
++
++static void omap_crtc_unregister_framedone_handler(
++		struct omap_overlay_manager *mgr,
++		void (*handler)(void *), void *data)
++{
++}
++
++static const struct dss_mgr_ops mgr_ops = {
++		.start_update = omap_crtc_start_update,
++		.enable = omap_crtc_enable,
++		.disable = omap_crtc_disable,
++		.set_timings = omap_crtc_set_timings,
++		.set_lcd_config = omap_crtc_set_lcd_config,
++		.register_framedone_handler = omap_crtc_register_framedone_handler,
++		.unregister_framedone_handler = omap_crtc_unregister_framedone_handler,
+ };
+ 
++/*
++ * CRTC funcs:
++ */
++
+ static void omap_crtc_destroy(struct drm_crtc *crtc)
+ {
+ 	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
++
++	DBG("%s", omap_crtc->name);
++
++	WARN_ON(omap_crtc->apply_irq.registered);
++	omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
++
+ 	omap_crtc->plane->funcs->destroy(omap_crtc->plane);
+ 	drm_crtc_cleanup(crtc);
++
+ 	kfree(omap_crtc);
+ }
+ 
+@@ -48,14 +160,25 @@  static void omap_crtc_dpms(struct drm_crtc *crtc, int mode)
+ {
+ 	struct omap_drm_private *priv = crtc->dev->dev_private;
+ 	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
++	bool enabled = (mode == DRM_MODE_DPMS_ON);
+ 	int i;
+ 
+-	WARN_ON(omap_plane_dpms(omap_crtc->plane, mode));
++	DBG("%s: %d", omap_crtc->name, mode);
++
++	if (enabled != omap_crtc->enabled) {
++		omap_crtc->enabled = enabled;
++		omap_crtc->full_update = true;
++		omap_crtc_apply(crtc, &omap_crtc->apply);
+ 
+-	for (i = 0; i < priv->num_planes; i++) {
+-		struct drm_plane *plane = priv->planes[i];
+-		if (plane->crtc == crtc)
+-			WARN_ON(omap_plane_dpms(plane, mode));
++		/* also enable our private plane: */
++		WARN_ON(omap_plane_dpms(omap_crtc->plane, 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)
++				WARN_ON(omap_plane_dpms(plane, mode));
++		}
+ 	}
+ }
+ 
+@@ -73,12 +196,26 @@  static int omap_crtc_mode_set(struct drm_crtc *crtc,
+ 		struct drm_framebuffer *old_fb)
+ {
+ 	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+-	struct drm_plane *plane = omap_crtc->plane;
+ 
+-	return omap_plane_mode_set(plane, crtc, crtc->fb,
++	mode = adjusted_mode;
++
++	DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
++			omap_crtc->name, mode->base.id, mode->name,
++			mode->vrefresh, mode->clock,
++			mode->hdisplay, mode->hsync_start,
++			mode->hsync_end, mode->htotal,
++			mode->vdisplay, mode->vsync_start,
++			mode->vsync_end, mode->vtotal,
++			mode->type, mode->flags);
++
++	copy_timings_drm_to_omap(&omap_crtc->timings, mode);
++	omap_crtc->full_update = true;
++
++	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);
++			mode->hdisplay << 16, mode->vdisplay << 16,
++			NULL, NULL);
+ }
+ 
+ static void omap_crtc_prepare(struct drm_crtc *crtc)
+@@ -102,10 +239,11 @@  static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+ 	struct drm_plane *plane = omap_crtc->plane;
+ 	struct drm_display_mode *mode = &crtc->mode;
+ 
+-	return plane->funcs->update_plane(plane, crtc, crtc->fb,
++	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);
++			mode->hdisplay << 16, mode->vdisplay << 16,
++			NULL, NULL);
+ }
+ 
+ static void omap_crtc_load_lut(struct drm_crtc *crtc)
+@@ -114,63 +252,54 @@  static void omap_crtc_load_lut(struct drm_crtc *crtc)
+ 
+ static void vblank_cb(void *arg)
+ {
+-	static uint32_t sequence;
+ 	struct drm_crtc *crtc = arg;
+ 	struct drm_device *dev = crtc->dev;
+ 	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+-	struct drm_pending_vblank_event *event = omap_crtc->event;
+ 	unsigned long flags;
+-	struct timeval now;
+ 
+-	WARN_ON(!event);
++	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;
+ 
+-	/* wakeup userspace */
+-	if (event) {
+-		do_gettimeofday(&now);
+-
+-		spin_lock_irqsave(&dev->event_lock, flags);
+-		/* TODO: we can't yet use the vblank time accounting,
+-		 * because omapdss lower layer is the one that knows
+-		 * the irq # and registers the handler, which more or
+-		 * less defeats how drm_irq works.. for now just fake
+-		 * the sequence number and use gettimeofday..
+-		 *
+-		event->event.sequence = drm_vblank_count_and_time(
+-				dev, omap_crtc->id, &now);
+-		 */
+-		event->event.sequence = sequence++;
+-		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);
+-	}
++	spin_unlock_irqrestore(&dev->event_lock, flags);
+ }
+ 
+-static void page_flip_cb(void *arg)
++static void page_flip_worker(struct work_struct *work)
+ {
+-	struct drm_crtc *crtc = arg;
+-	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+-	struct drm_framebuffer *old_fb = omap_crtc->old_fb;
++	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_display_mode *mode = &crtc->mode;
+ 	struct drm_gem_object *bo;
+ 
+-	omap_crtc->old_fb = NULL;
+-
+-	omap_crtc_mode_set_base(crtc, crtc->x, crtc->y, old_fb);
+-
+-	/* really we'd like to setup the callback atomically w/ setting the
+-	 * new scanout buffer to avoid getting stuck waiting an extra vblank
+-	 * cycle.. for now go for correctness and later figure out speed..
+-	 */
+-	omap_plane_on_endwin(omap_crtc->plane, vblank_cb, crtc);
++	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);
+ }
+ 
++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)
+@@ -179,14 +308,14 @@  static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
+ 	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ 	struct drm_gem_object *bo;
+ 
+-	DBG("%d -> %d", crtc->fb ? crtc->fb->base.id : -1, fb->base.id);
++	DBG("%d -> %d (event=%p)", crtc->fb ? crtc->fb->base.id : -1,
++			fb->base.id, event);
+ 
+-	if (omap_crtc->event) {
++	if (omap_crtc->old_fb) {
+ 		dev_err(dev->dev, "already a pending flip\n");
+ 		return -EINVAL;
+ 	}
+ 
+-	omap_crtc->old_fb = crtc->fb;
+ 	omap_crtc->event = event;
+ 	crtc->fb = fb;
+ 
+@@ -234,14 +363,244 @@  static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
+ 	.load_lut = omap_crtc_load_lut,
+ };
+ 
++const struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc)
++{
++	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
++	return &omap_crtc->timings;
++}
++
++enum omap_channel omap_crtc_channel(struct drm_crtc *crtc)
++{
++	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
++	return omap_crtc->channel;
++}
++
++static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
++{
++	struct omap_crtc *omap_crtc =
++			container_of(irq, struct omap_crtc, error_irq);
++	struct drm_crtc *crtc = &omap_crtc->base;
++	DRM_ERROR("%s: errors: %08x\n", omap_crtc->name, irqstatus);
++	/* avoid getting in a flood, unregister the irq until next vblank */
++	omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
++}
++
++static void omap_crtc_apply_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
++{
++	struct omap_crtc *omap_crtc =
++			container_of(irq, struct omap_crtc, apply_irq);
++	struct drm_crtc *crtc = &omap_crtc->base;
++
++	if (!omap_crtc->error_irq.registered)
++		omap_irq_register(crtc->dev, &omap_crtc->error_irq);
++
++	if (!dispc_mgr_go_busy(omap_crtc->channel)) {
++		struct omap_drm_private *priv =
++				crtc->dev->dev_private;
++		DBG("%s: apply done", omap_crtc->name);
++		omap_irq_unregister(crtc->dev, &omap_crtc->apply_irq);
++		queue_work(priv->wq, &omap_crtc->apply_work);
++	}
++}
++
++static void apply_worker(struct work_struct *work)
++{
++	struct omap_crtc *omap_crtc =
++			container_of(work, struct omap_crtc, apply_work);
++	struct drm_crtc *crtc = &omap_crtc->base;
++	struct drm_device *dev = crtc->dev;
++	struct omap_drm_apply *apply, *n;
++	bool need_apply;
++
++	/*
++	 * Synchronize everything on mode_config.mutex, to keep
++	 * the callbacks and list modification all serialized
++	 * with respect to modesetting ioctls from userspace.
++	 */
++	mutex_lock(&dev->mode_config.mutex);
++	dispc_runtime_get();
++
++	/*
++	 * If we are still pending a previous update, wait.. when the
++	 * pending update completes, we get kicked again.
++	 */
++	if (omap_crtc->apply_irq.registered)
++		goto out;
++
++	/* finish up previous apply's: */
++	list_for_each_entry_safe(apply, n,
++			&omap_crtc->pending_applies, pending_node) {
++		apply->post_apply(apply);
++		list_del(&apply->pending_node);
++	}
++
++	need_apply = !list_empty(&omap_crtc->queued_applies);
++
++	/* then handle the next round of of queued apply's: */
++	list_for_each_entry_safe(apply, n,
++			&omap_crtc->queued_applies, queued_node) {
++		apply->pre_apply(apply);
++		list_del(&apply->queued_node);
++		apply->queued = false;
++		list_add_tail(&apply->pending_node,
++				&omap_crtc->pending_applies);
++	}
++
++	if (need_apply) {
++		enum omap_channel channel = omap_crtc->channel;
++
++		DBG("%s: GO", omap_crtc->name);
++
++		if (dispc_mgr_is_enabled(channel)) {
++			omap_irq_register(dev, &omap_crtc->apply_irq);
++			dispc_mgr_go(channel);
++		} else {
++			struct omap_drm_private *priv = dev->dev_private;
++			queue_work(priv->wq, &omap_crtc->apply_work);
++		}
++	}
++
++out:
++	dispc_runtime_put();
++	mutex_unlock(&dev->mode_config.mutex);
++}
++
++int omap_crtc_apply(struct drm_crtc *crtc,
++		struct omap_drm_apply *apply)
++{
++	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
++	struct drm_device *dev = crtc->dev;
++
++	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;
++
++	apply->queued = true;
++	list_add_tail(&apply->queued_node, &omap_crtc->queued_applies);
++
++	/*
++	 * If there are no currently pending updates, then go ahead and
++	 * kick the worker immediately, otherwise it will run again when
++	 * the current update finishes.
++	 */
++	if (list_empty(&omap_crtc->pending_applies)) {
++		struct omap_drm_private *priv = crtc->dev->dev_private;
++		queue_work(priv->wq, &omap_crtc->apply_work);
++	}
++
++	return 0;
++}
++
++/* called only from apply */
++static void set_enabled(struct drm_crtc *crtc, bool enable)
++{
++	struct drm_device *dev = crtc->dev;
++	struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
++	enum omap_channel channel = omap_crtc->channel;
++	struct omap_irq_wait *wait = NULL;
++
++	if (dispc_mgr_is_enabled(channel) == enable)
++		return;
++
++	/* ignore sync-lost irqs during enable/disable */
++	omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
++
++	if (dispc_mgr_get_framedone_irq(channel)) {
++		if (!enable) {
++			wait = omap_irq_wait_init(dev,
++					dispc_mgr_get_framedone_irq(channel), 1);
++		}
++	} else {
++		/*
++		 * When we disable digit output, we need to wait until fields
++		 * are done.  Otherwise the DSS is still working, and turning
++		 * off the clocks prevents DSS from going to OFF mode. And when
++		 * enabling, we need to wait for the extra sync losts
++		 */
++		wait = omap_irq_wait_init(dev,
++				dispc_mgr_get_vsync_irq(channel), 2);
++	}
++
++	dispc_mgr_enable(channel, enable);
++
++	if (wait) {
++		int ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100));
++		if (ret) {
++			dev_err(dev->dev, "%s: timeout waiting for %s\n",
++					omap_crtc->name, enable ? "enable" : "disable");
++		}
++	}
++
++	omap_irq_register(crtc->dev, &omap_crtc->error_irq);
++}
++
++static void omap_crtc_pre_apply(struct omap_drm_apply *apply)
++{
++	struct omap_crtc *omap_crtc =
++			container_of(apply, struct omap_crtc, apply);
++	struct drm_crtc *crtc = &omap_crtc->base;
++	struct drm_encoder *encoder = NULL;
++
++	DBG("%s: enabled=%d, full=%d", omap_crtc->name,
++			omap_crtc->enabled, omap_crtc->full_update);
++
++	if (omap_crtc->full_update) {
++		struct omap_drm_private *priv = crtc->dev->dev_private;
++		int i;
++		for (i = 0; i < priv->num_encoders; i++) {
++			if (priv->encoders[i]->crtc == crtc) {
++				encoder = priv->encoders[i];
++				break;
++			}
++		}
++	}
++
++	if (!omap_crtc->enabled) {
++		set_enabled(&omap_crtc->base, false);
++		if (encoder)
++			omap_encoder_set_enabled(encoder, false);
++	} else {
++		if (encoder) {
++			omap_encoder_set_enabled(encoder, false);
++			omap_encoder_update(encoder, &omap_crtc->mgr,
++					&omap_crtc->timings);
++			omap_encoder_set_enabled(encoder, true);
++			omap_crtc->full_update = false;
++		}
++
++		dispc_mgr_setup(omap_crtc->channel, &omap_crtc->info);
++		dispc_mgr_set_timings(omap_crtc->channel,
++				&omap_crtc->timings);
++		set_enabled(&omap_crtc->base, true);
++	}
++
++	omap_crtc->full_update = false;
++}
++
++static void omap_crtc_post_apply(struct omap_drm_apply *apply)
++{
++	/* nothing needed for post-apply */
++}
++
++static const char *channel_names[] = {
++		[OMAP_DSS_CHANNEL_LCD] = "lcd",
++		[OMAP_DSS_CHANNEL_DIGIT] = "tv",
++		[OMAP_DSS_CHANNEL_LCD2] = "lcd2",
++};
++
+ /* initialize crtc */
+ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
+-		struct omap_overlay *ovl, int id)
++		struct drm_plane *plane, enum omap_channel channel, int id)
+ {
+ 	struct drm_crtc *crtc = NULL;
+-	struct omap_crtc *omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL);
++	struct omap_crtc *omap_crtc;
++	struct omap_overlay_manager_info *info;
++
++	DBG("%s", channel_names[channel]);
+ 
+-	DBG("%s", ovl->name);
++	omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL);
+ 
+ 	if (!omap_crtc) {
+ 		dev_err(dev->dev, "could not allocate CRTC\n");
+@@ -250,10 +609,40 @@  struct drm_crtc *omap_crtc_init(struct drm_device *dev,
+ 
+ 	crtc = &omap_crtc->base;
+ 
+-	omap_crtc->plane = omap_plane_init(dev, ovl, (1 << id), true);
++	INIT_WORK(&omap_crtc->page_flip_work, page_flip_worker);
++	INIT_WORK(&omap_crtc->apply_work, apply_worker);
++
++	INIT_LIST_HEAD(&omap_crtc->pending_applies);
++	INIT_LIST_HEAD(&omap_crtc->queued_applies);
++
++	omap_crtc->apply.pre_apply  = omap_crtc_pre_apply;
++	omap_crtc->apply.post_apply = omap_crtc_post_apply;
++
++	omap_crtc->apply_irq.irqmask = pipe2vbl(id);
++	omap_crtc->apply_irq.irq = omap_crtc_apply_irq;
++
++	omap_crtc->error_irq.irqmask =
++			dispc_mgr_get_sync_lost_irq(channel);
++	omap_crtc->error_irq.irq = omap_crtc_error_irq;
++	omap_irq_register(dev, &omap_crtc->error_irq);
++
++	omap_crtc->channel = channel;
++	omap_crtc->plane = plane;
+ 	omap_crtc->plane->crtc = crtc;
+-	omap_crtc->name = ovl->name;
+-	omap_crtc->id = id;
++	omap_crtc->name = channel_names[channel];
++	omap_crtc->pipe = id;
++
++	/* temporary: */
++	omap_crtc->mgr.id = channel;
++
++	dss_install_mgr_ops(&mgr_ops);
++
++	/* TODO: fix hard-coded setup.. add properties! */
++	info = &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;
+ 
+ 	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 84943e5..ae5ecc2 100644
+--- a/drivers/staging/omapdrm/omap_drv.c
++++ b/drivers/staging/omapdrm/omap_drv.c
+@@ -74,320 +74,99 @@  static int get_connector_type(struct omap_dss_device *dssdev)
+ 	}
+ }
+ 
+-#if 0 /* enable when dss2 supports hotplug */
+-static int omap_drm_notifier(struct notifier_block *nb,
+-		unsigned long evt, void *arg)
+-{
+-	switch (evt) {
+-	case OMAP_DSS_SIZE_CHANGE:
+-	case OMAP_DSS_HOTPLUG_CONNECT:
+-	case OMAP_DSS_HOTPLUG_DISCONNECT: {
+-		struct drm_device *dev = drm_device;
+-		DBG("hotplug event: evt=%d, dev=%p", evt, dev);
+-		if (dev)
+-			drm_sysfs_hotplug_event(dev);
+-
+-		return NOTIFY_OK;
+-	}
+-	default:  /* don't care about other events for now */
+-		return NOTIFY_DONE;
+-	}
+-}
+-#endif
+-
+-static void dump_video_chains(void)
+-{
+-	int i;
+-
+-	DBG("dumping video chains: ");
+-	for (i = 0; i < omap_dss_get_num_overlays(); i++) {
+-		struct omap_overlay *ovl = omap_dss_get_overlay(i);
+-		struct omap_overlay_manager *mgr = ovl->manager;
+-		struct omap_dss_device *dssdev = mgr ?
+-					mgr->get_device(mgr) : NULL;
+-		if (dssdev) {
+-			DBG("%d: %s -> %s -> %s", i, ovl->name, mgr->name,
+-						dssdev->name);
+-		} else if (mgr) {
+-			DBG("%d: %s -> %s", i, ovl->name, mgr->name);
+-		} else {
+-			DBG("%d: %s", i, ovl->name);
+-		}
+-	}
+-}
+-
+-/* create encoders for each manager */
+-static int create_encoder(struct drm_device *dev,
+-		struct omap_overlay_manager *mgr)
+-{
+-	struct omap_drm_private *priv = dev->dev_private;
+-	struct drm_encoder *encoder = omap_encoder_init(dev, mgr);
+-
+-	if (!encoder) {
+-		dev_err(dev->dev, "could not create encoder: %s\n",
+-				mgr->name);
+-		return -ENOMEM;
+-	}
+-
+-	BUG_ON(priv->num_encoders >= ARRAY_SIZE(priv->encoders));
+-
+-	priv->encoders[priv->num_encoders++] = encoder;
+-
+-	return 0;
+-}
+-
+-/* create connectors for each display device */
+-static int create_connector(struct drm_device *dev,
+-		struct omap_dss_device *dssdev)
++static int omap_modeset_init(struct drm_device *dev)
+ {
+ 	struct omap_drm_private *priv = dev->dev_private;
+-	static struct notifier_block *notifier;
+-	struct drm_connector *connector;
+-	int j;
+-
+-	if (!dssdev->driver) {
+-		dev_warn(dev->dev, "%s has no driver.. skipping it\n",
+-				dssdev->name);
+-		return 0;
+-	}
++	struct omap_dss_device *dssdev = NULL;
++	int num_ovls = dss_feat_get_num_ovls();
++	int id;
+ 
+-	if (!(dssdev->driver->get_timings ||
+-				dssdev->driver->read_edid)) {
+-		dev_warn(dev->dev, "%s driver does not support "
+-			"get_timings or read_edid.. skipping it!\n",
+-			dssdev->name);
+-		return 0;
+-	}
++	drm_mode_config_init(dev);
+ 
+-	connector = omap_connector_init(dev,
+-			get_connector_type(dssdev), dssdev);
++	omap_drm_irq_install(dev);
+ 
+-	if (!connector) {
+-		dev_err(dev->dev, "could not create connector: %s\n",
+-				dssdev->name);
+-		return -ENOMEM;
+-	}
+-
+-	BUG_ON(priv->num_connectors >= ARRAY_SIZE(priv->connectors));
++	/*
++	 * Create private planes and CRTCs for the last NUM_CRTCs overlay
++	 * plus manager:
++	 */
++	for (id = 0; id < min(num_crtc, num_ovls); id++) {
++		struct drm_plane *plane;
++		struct drm_crtc *crtc;
+ 
+-	priv->connectors[priv->num_connectors++] = connector;
++		plane = omap_plane_init(dev, id, true);
++		crtc = omap_crtc_init(dev, plane, pipe2chan(id), id);
+ 
+-#if 0 /* enable when dss2 supports hotplug */
+-	notifier = kzalloc(sizeof(struct notifier_block), GFP_KERNEL);
+-	notifier->notifier_call = omap_drm_notifier;
+-	omap_dss_add_notify(dssdev, notifier);
+-#else
+-	notifier = NULL;
+-#endif
++		BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs));
++		priv->crtcs[id] = crtc;
++		priv->num_crtcs++;
+ 
+-	for (j = 0; j < priv->num_encoders; j++) {
+-		struct omap_overlay_manager *mgr =
+-			omap_encoder_get_manager(priv->encoders[j]);
+-		if (mgr->get_device(mgr) == dssdev) {
+-			drm_mode_connector_attach_encoder(connector,
+-					priv->encoders[j]);
+-		}
++		priv->planes[id] = plane;
++		priv->num_planes++;
+ 	}
+ 
+-	return 0;
+-}
+-
+-/* create up to max_overlays CRTCs mapping to overlays.. by default,
+- * connect the overlays to different managers/encoders, giving priority
+- * to encoders connected to connectors with a detected connection
+- */
+-static int create_crtc(struct drm_device *dev, struct omap_overlay *ovl,
+-		int *j, unsigned int connected_connectors)
+-{
+-	struct omap_drm_private *priv = dev->dev_private;
+-	struct omap_overlay_manager *mgr = NULL;
+-	struct drm_crtc *crtc;
+-
+-	/* find next best connector, ones with detected connection first
++	/*
++	 * Create normal planes for the remaining overlays:
+ 	 */
+-	while (*j < priv->num_connectors && !mgr) {
+-		if (connected_connectors & (1 << *j)) {
+-			struct drm_encoder *encoder =
+-				omap_connector_attached_encoder(
+-						priv->connectors[*j]);
+-			if (encoder)
+-				mgr = omap_encoder_get_manager(encoder);
++	for (; id < num_ovls; id++) {
++		struct drm_plane *plane = omap_plane_init(dev, id, false);
+ 
+-		}
+-		(*j)++;
++		BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes));
++		priv->planes[priv->num_planes++] = plane;
+ 	}
+ 
+-	/* if we couldn't find another connected connector, lets start
+-	 * looking at the unconnected connectors:
+-	 *
+-	 * note: it might not be immediately apparent, but thanks to
+-	 * the !mgr check in both this loop and the one above, the only
+-	 * way to enter this loop is with *j == priv->num_connectors,
+-	 * so idx can never go negative.
+-	 */
+-	while (*j < 2 * priv->num_connectors && !mgr) {
+-		int idx = *j - priv->num_connectors;
+-		if (!(connected_connectors & (1 << idx))) {
+-			struct drm_encoder *encoder =
+-				omap_connector_attached_encoder(
+-						priv->connectors[idx]);
+-			if (encoder)
+-				mgr = omap_encoder_get_manager(encoder);
++	for_each_dss_dev(dssdev) {
++		struct drm_connector *connector;
++		struct drm_encoder *encoder;
+ 
++		if (!dssdev->driver) {
++			dev_warn(dev->dev, "%s has no driver.. skipping it\n",
++					dssdev->name);
++			return 0;
+ 		}
+-		(*j)++;
+-	}
+-
+-	crtc = omap_crtc_init(dev, ovl, priv->num_crtcs);
+-
+-	if (!crtc) {
+-		dev_err(dev->dev, "could not create CRTC: %s\n",
+-				ovl->name);
+-		return -ENOMEM;
+-	}
+ 
+-	BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs));
+-
+-	priv->crtcs[priv->num_crtcs++] = crtc;
+-
+-	return 0;
+-}
+-
+-static int create_plane(struct drm_device *dev, struct omap_overlay *ovl,
+-		unsigned int possible_crtcs)
+-{
+-	struct omap_drm_private *priv = dev->dev_private;
+-	struct drm_plane *plane =
+-			omap_plane_init(dev, ovl, possible_crtcs, false);
+-
+-	if (!plane) {
+-		dev_err(dev->dev, "could not create plane: %s\n",
+-				ovl->name);
+-		return -ENOMEM;
+-	}
+-
+-	BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes));
+-
+-	priv->planes[priv->num_planes++] = plane;
+-
+-	return 0;
+-}
+-
+-static int match_dev_name(struct omap_dss_device *dssdev, void *data)
+-{
+-	return !strcmp(dssdev->name, data);
+-}
+-
+-static unsigned int detect_connectors(struct drm_device *dev)
+-{
+-	struct omap_drm_private *priv = dev->dev_private;
+-	unsigned int connected_connectors = 0;
+-	int i;
+-
+-	for (i = 0; i < priv->num_connectors; i++) {
+-		struct drm_connector *connector = priv->connectors[i];
+-		if (omap_connector_detect(connector, true) ==
+-				connector_status_connected) {
+-			connected_connectors |= (1 << i);
++		if (!(dssdev->driver->get_timings ||
++					dssdev->driver->read_edid)) {
++			dev_warn(dev->dev, "%s driver does not support "
++				"get_timings or read_edid.. skipping it!\n",
++				dssdev->name);
++			return 0;
+ 		}
+-	}
+-
+-	return connected_connectors;
+-}
+ 
+-static int omap_modeset_init(struct drm_device *dev)
+-{
+-	const struct omap_drm_platform_data *pdata = dev->dev->platform_data;
+-	struct omap_kms_platform_data *kms_pdata = NULL;
+-	struct omap_drm_private *priv = dev->dev_private;
+-	struct omap_dss_device *dssdev = NULL;
+-	int i, j;
+-	unsigned int connected_connectors = 0;
++		encoder = omap_encoder_init(dev, dssdev);
+ 
+-	drm_mode_config_init(dev);
+-
+-	if (pdata && pdata->kms_pdata) {
+-		kms_pdata = pdata->kms_pdata;
+-
+-		/* if platform data is provided by the board file, use it to
+-		 * control which overlays, managers, and devices we own.
+-		 */
+-		for (i = 0; i < kms_pdata->mgr_cnt; i++) {
+-			struct omap_overlay_manager *mgr =
+-				omap_dss_get_overlay_manager(
+-						kms_pdata->mgr_ids[i]);
+-			create_encoder(dev, mgr);
+-		}
+-
+-		for (i = 0; i < kms_pdata->dev_cnt; i++) {
+-			struct omap_dss_device *dssdev =
+-				omap_dss_find_device(
+-					(void *)kms_pdata->dev_names[i],
+-					match_dev_name);
+-			if (!dssdev) {
+-				dev_warn(dev->dev, "no such dssdev: %s\n",
+-						kms_pdata->dev_names[i]);
+-				continue;
+-			}
+-			create_connector(dev, dssdev);
++		if (!encoder) {
++			dev_err(dev->dev, "could not create encoder: %s\n",
++					dssdev->name);
++			return -ENOMEM;
+ 		}
+ 
+-		connected_connectors = detect_connectors(dev);
++		connector = omap_connector_init(dev,
++				get_connector_type(dssdev), dssdev, encoder);
+ 
+-		j = 0;
+-		for (i = 0; i < kms_pdata->ovl_cnt; i++) {
+-			struct omap_overlay *ovl =
+-				omap_dss_get_overlay(kms_pdata->ovl_ids[i]);
+-			create_crtc(dev, ovl, &j, connected_connectors);
++		if (!connector) {
++			dev_err(dev->dev, "could not create connector: %s\n",
++					dssdev->name);
++			return -ENOMEM;
+ 		}
+ 
+-		for (i = 0; i < kms_pdata->pln_cnt; i++) {
+-			struct omap_overlay *ovl =
+-				omap_dss_get_overlay(kms_pdata->pln_ids[i]);
+-			create_plane(dev, ovl, (1 << priv->num_crtcs) - 1);
+-		}
+-	} else {
+-		/* otherwise just grab up to CONFIG_DRM_OMAP_NUM_CRTCS and try
+-		 * to make educated guesses about everything else
+-		 */
+-		int max_overlays = min(omap_dss_get_num_overlays(), num_crtc);
++		BUG_ON(priv->num_encoders >= ARRAY_SIZE(priv->encoders));
++		BUG_ON(priv->num_connectors >= ARRAY_SIZE(priv->connectors));
+ 
+-		for (i = 0; i < omap_dss_get_num_overlay_managers(); i++)
+-			create_encoder(dev, omap_dss_get_overlay_manager(i));
+-
+-		for_each_dss_dev(dssdev) {
+-			create_connector(dev, dssdev);
+-		}
++		priv->encoders[priv->num_encoders++] = encoder;
++		priv->connectors[priv->num_connectors++] = connector;
+ 
+-		connected_connectors = detect_connectors(dev);
++		drm_mode_connector_attach_encoder(connector, encoder);
+ 
+-		j = 0;
+-		for (i = 0; i < max_overlays; i++) {
+-			create_crtc(dev, omap_dss_get_overlay(i),
+-					&j, connected_connectors);
+-		}
+-
+-		/* use any remaining overlays as drm planes */
+-		for (; i < omap_dss_get_num_overlays(); i++) {
+-			struct omap_overlay *ovl = omap_dss_get_overlay(i);
+-			create_plane(dev, ovl, (1 << priv->num_crtcs) - 1);
++		/* figure out which crtc's we can connect the encoder to: */
++		encoder->possible_crtcs = 0;
++		for (id = 0; id < priv->num_crtcs; id++) {
++			enum omap_dss_output_id supported_outputs =
++					dss_feat_get_supported_outputs(pipe2chan(id));
++			if (supported_outputs & dssdev->output->id)
++				encoder->possible_crtcs |= (1 << id);
+ 		}
+ 	}
+ 
+-	/* for now keep the mapping of CRTCs and encoders static.. */
+-	for (i = 0; i < priv->num_encoders; i++) {
+-		struct drm_encoder *encoder = priv->encoders[i];
+-		struct omap_overlay_manager *mgr =
+-				omap_encoder_get_manager(encoder);
+-
+-		encoder->possible_crtcs = (1 << priv->num_crtcs) - 1;
+-
+-		DBG("%s: possible_crtcs=%08x", mgr->name,
+-					encoder->possible_crtcs);
+-	}
+-
+-	dump_video_chains();
+-
+ 	dev->mode_config.min_width = 32;
+ 	dev->mode_config.min_height = 32;
+ 
+@@ -450,7 +229,7 @@  static int ioctl_gem_new(struct drm_device *dev, void *data,
+ 		struct drm_file *file_priv)
+ {
+ 	struct drm_omap_gem_new *args = data;
+-	DBG("%p:%p: size=0x%08x, flags=%08x", dev, file_priv,
++	VERB("%p:%p: size=0x%08x, flags=%08x", dev, file_priv,
+ 			args->size.bytes, args->flags);
+ 	return omap_gem_new_handle(dev, file_priv, args->size,
+ 			args->flags, &args->handle);
+@@ -510,7 +289,7 @@  static int ioctl_gem_info(struct drm_device *dev, void *data,
+ 	struct drm_gem_object *obj;
+ 	int ret = 0;
+ 
+-	DBG("%p:%p: handle=%d", dev, file_priv, args->handle);
++	VERB("%p:%p: handle=%d", dev, file_priv, args->handle);
+ 
+ 	obj = drm_gem_object_lookup(dev, file_priv, args->handle);
+ 	if (!obj)
+@@ -565,14 +344,6 @@  static int dev_load(struct drm_device *dev, unsigned long flags)
+ 
+ 	dev->dev_private = priv;
+ 
+-	ret = omapdss_compat_init();
+-	if (ret) {
+-		dev_err(dev->dev, "coult not init omapdss\n");
+-		dev->dev_private = NULL;
+-		kfree(priv);
+-		return ret;
+-	}
+-
+ 	priv->wq = alloc_ordered_workqueue("omapdrm", 0);
+ 
+ 	INIT_LIST_HEAD(&priv->obj_list);
+@@ -584,10 +355,13 @@  static int dev_load(struct drm_device *dev, unsigned long flags)
+ 		dev_err(dev->dev, "omap_modeset_init failed: ret=%d\n", ret);
+ 		dev->dev_private = NULL;
+ 		kfree(priv);
+-		omapdss_compat_uninit();
+ 		return ret;
+ 	}
+ 
++	ret = drm_vblank_init(dev, priv->num_crtcs);
++	if (ret)
++		dev_warn(dev->dev, "could not init vblank\n");
++
+ 	priv->fbdev = omap_fbdev_init(dev);
+ 	if (!priv->fbdev) {
+ 		dev_warn(dev->dev, "omap_fbdev_init failed\n");
+@@ -596,10 +370,6 @@  static int dev_load(struct drm_device *dev, unsigned long flags)
+ 
+ 	drm_kms_helper_poll_init(dev);
+ 
+-	ret = drm_vblank_init(dev, priv->num_crtcs);
+-	if (ret)
+-		dev_warn(dev->dev, "could not init vblank\n");
+-
+ 	return 0;
+ }
+ 
+@@ -609,8 +379,9 @@  static int dev_unload(struct drm_device *dev)
+ 
+ 	DBG("unload: dev=%p", dev);
+ 
+-	drm_vblank_cleanup(dev);
+ 	drm_kms_helper_poll_fini(dev);
++	drm_vblank_cleanup(dev);
++	omap_drm_irq_uninstall(dev);
+ 
+ 	omap_fbdev_free(dev);
+ 	omap_modeset_free(dev);
+@@ -619,8 +390,6 @@  static int dev_unload(struct drm_device *dev)
+ 	flush_workqueue(priv->wq);
+ 	destroy_workqueue(priv->wq);
+ 
+-	omapdss_compat_uninit();
+-
+ 	kfree(dev->dev_private);
+ 	dev->dev_private = NULL;
+ 
+@@ -680,7 +449,9 @@  static void dev_lastclose(struct drm_device *dev)
+ 		}
+ 	}
+ 
++	mutex_lock(&dev->mode_config.mutex);
+ 	ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev);
++	mutex_unlock(&dev->mode_config.mutex);
+ 	if (ret)
+ 		DBG("failed to restore crtc mode");
+ }
+@@ -695,60 +466,6 @@  static void dev_postclose(struct drm_device *dev, struct drm_file *file)
+ 	DBG("postclose: dev=%p, file=%p", dev, file);
+ }
+ 
+-/**
+- * enable_vblank - enable vblank interrupt events
+- * @dev: DRM device
+- * @crtc: which irq to enable
+- *
+- * Enable vblank interrupts for @crtc.  If the device doesn't have
+- * a hardware vblank counter, this routine should be a no-op, since
+- * interrupts will have to stay on to keep the count accurate.
+- *
+- * RETURNS
+- * Zero on success, appropriate errno if the given @crtc's vblank
+- * interrupt cannot be enabled.
+- */
+-static int dev_enable_vblank(struct drm_device *dev, int crtc)
+-{
+-	DBG("enable_vblank: dev=%p, crtc=%d", dev, crtc);
+-	return 0;
+-}
+-
+-/**
+- * disable_vblank - disable vblank interrupt events
+- * @dev: DRM device
+- * @crtc: which irq to enable
+- *
+- * Disable vblank interrupts for @crtc.  If the device doesn't have
+- * a hardware vblank counter, this routine should be a no-op, since
+- * interrupts will have to stay on to keep the count accurate.
+- */
+-static void dev_disable_vblank(struct drm_device *dev, int crtc)
+-{
+-	DBG("disable_vblank: dev=%p, crtc=%d", dev, crtc);
+-}
+-
+-static irqreturn_t dev_irq_handler(DRM_IRQ_ARGS)
+-{
+-	return IRQ_HANDLED;
+-}
+-
+-static void dev_irq_preinstall(struct drm_device *dev)
+-{
+-	DBG("irq_preinstall: dev=%p", dev);
+-}
+-
+-static int dev_irq_postinstall(struct drm_device *dev)
+-{
+-	DBG("irq_postinstall: dev=%p", dev);
+-	return 0;
+-}
+-
+-static void dev_irq_uninstall(struct drm_device *dev)
+-{
+-	DBG("irq_uninstall: dev=%p", dev);
+-}
+-
+ static const struct vm_operations_struct omap_gem_vm_ops = {
+ 	.fault = omap_gem_fault,
+ 	.open = drm_gem_vm_open,
+@@ -778,12 +495,12 @@  static struct drm_driver omap_drm_driver = {
+ 		.preclose = dev_preclose,
+ 		.postclose = dev_postclose,
+ 		.get_vblank_counter = drm_vblank_count,
+-		.enable_vblank = dev_enable_vblank,
+-		.disable_vblank = dev_disable_vblank,
+-		.irq_preinstall = dev_irq_preinstall,
+-		.irq_postinstall = dev_irq_postinstall,
+-		.irq_uninstall = dev_irq_uninstall,
+-		.irq_handler = dev_irq_handler,
++		.enable_vblank = omap_irq_enable_vblank,
++		.disable_vblank = omap_irq_disable_vblank,
++		.irq_preinstall = omap_irq_preinstall,
++		.irq_postinstall = omap_irq_postinstall,
++		.irq_uninstall = omap_irq_uninstall,
++		.irq_handler = omap_irq_handler,
+ #ifdef CONFIG_DEBUG_FS
+ 		.debugfs_init = omap_debugfs_init,
+ 		.debugfs_cleanup = omap_debugfs_cleanup,
+diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/staging/omapdrm/omap_drv.h
+index 1d4aea5..cd1f22b 100644
+--- a/drivers/staging/omapdrm/omap_drv.h
++++ b/drivers/staging/omapdrm/omap_drv.h
+@@ -28,6 +28,7 @@ 
+ #include <linux/platform_data/omap_drm.h>
+ #include "omap_drm.h"
+ 
++
+ #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
+ #define VERB(fmt, ...) if (0) DRM_DEBUG(fmt, ##__VA_ARGS__) /* verbose debug */
+ 
+@@ -39,6 +40,51 @@ 
+  */
+ #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
++ * which is called before setting GO bit, and post_apply that is called
++ * after GO bit is cleared.  The crtc manages the queuing, and everyone
++ * else goes thru omap_crtc_apply() using these callbacks so that the
++ * code which has to deal w/ GO bit state is centralized.
++ */
++struct omap_drm_apply {
++	struct list_head pending_node, queued_node;
++	bool queued;
++	void (*pre_apply)(struct omap_drm_apply *apply);
++	void (*post_apply)(struct omap_drm_apply *apply);
++};
++
++/* For transiently registering for different DSS irqs that various parts
++ * of the KMS code need during setup/configuration.  We these are not
++ * necessarily the same as what drm_vblank_get/put() are requesting, and
++ * the hysteresis in drm_vblank_put() is not necessarily desirable for
++ * internal housekeeping related irq usage.
++ */
++struct omap_drm_irq {
++	struct list_head node;
++	uint32_t irqmask;
++	bool registered;
++	void (*irq)(struct omap_drm_irq *irq, uint32_t irqstatus);
++};
++
++/* For KMS code that needs to wait for a certain # of IRQs:
++ */
++struct omap_irq_wait;
++struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev,
++		uint32_t irqmask, int count);
++int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait,
++		unsigned long timeout);
++
+ struct omap_drm_private {
+ 	uint32_t omaprev;
+ 
+@@ -58,6 +104,7 @@  struct omap_drm_private {
+ 
+ 	struct workqueue_struct *wq;
+ 
++	/* list of GEM objects: */
+ 	struct list_head obj_list;
+ 
+ 	bool has_dmm;
+@@ -65,6 +112,11 @@  struct omap_drm_private {
+ 	/* properties: */
+ 	struct drm_property *rotation_prop;
+ 	struct drm_property *zorder_prop;
++
++	/* irq handling: */
++	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;
+ };
+ 
+ /* this should probably be in drm-core to standardize amongst drivers */
+@@ -75,15 +127,6 @@  struct omap_drm_private {
+ #define DRM_REFLECT_X	4
+ #define DRM_REFLECT_Y	5
+ 
+-/* 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;
+-};
+-
+ #ifdef CONFIG_DEBUG_FS
+ int omap_debugfs_init(struct drm_minor *minor);
+ void omap_debugfs_cleanup(struct drm_minor *minor);
+@@ -92,23 +135,36 @@  void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m);
+ void omap_gem_describe_objects(struct list_head *list, struct seq_file *m);
+ #endif
+ 
++int omap_irq_enable_vblank(struct drm_device *dev, int crtc);
++void omap_irq_disable_vblank(struct drm_device *dev, int crtc);
++irqreturn_t omap_irq_handler(DRM_IRQ_ARGS);
++void omap_irq_preinstall(struct drm_device *dev);
++int omap_irq_postinstall(struct drm_device *dev);
++void omap_irq_uninstall(struct drm_device *dev);
++void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq);
++void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq);
++int omap_drm_irq_uninstall(struct drm_device *dev);
++int omap_drm_irq_install(struct drm_device *dev);
++
+ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev);
+ 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_apply(struct drm_crtc *crtc,
++		struct omap_drm_apply *apply);
+ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
+-		struct omap_overlay *ovl, int id);
++		struct drm_plane *plane, enum omap_channel channel, int id);
+ 
+ struct drm_plane *omap_plane_init(struct drm_device *dev,
+-		struct omap_overlay *ovl, unsigned int possible_crtcs,
+-		bool priv);
++		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 omap_plane_on_endwin(struct drm_plane *plane,
++		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);
+@@ -116,21 +172,25 @@  int omap_plane_set_property(struct drm_plane *plane,
+ 		struct drm_property *property, uint64_t val);
+ 
+ struct drm_encoder *omap_encoder_init(struct drm_device *dev,
+-		struct omap_overlay_manager *mgr);
+-struct omap_overlay_manager *omap_encoder_get_manager(
++		struct omap_dss_device *dssdev);
++int omap_encoder_set_enabled(struct drm_encoder *encoder, bool enabled);
++int omap_encoder_update(struct drm_encoder *encoder,
++		struct omap_overlay_manager *mgr,
++		struct omap_video_timings *timings);
++
++struct drm_connector *omap_connector_init(struct drm_device *dev,
++		int connector_type, struct omap_dss_device *dssdev,
+ 		struct drm_encoder *encoder);
+ struct drm_encoder *omap_connector_attached_encoder(
+ 		struct drm_connector *connector);
+-enum drm_connector_status omap_connector_detect(
+-		struct drm_connector *connector, bool force);
+-
+-struct drm_connector *omap_connector_init(struct drm_device *dev,
+-		int connector_type, struct omap_dss_device *dssdev);
+-void omap_connector_mode_set(struct drm_connector *connector,
+-		struct drm_display_mode *mode);
+ void omap_connector_flush(struct drm_connector *connector,
+ 		int x, int y, int w, int h);
+ 
++void copy_timings_omap_to_drm(struct drm_display_mode *mode,
++		struct omap_video_timings *timings);
++void copy_timings_drm_to_omap(struct omap_video_timings *timings,
++		struct drm_display_mode *mode);
++
+ uint32_t omap_framebuffer_get_formats(uint32_t *pixel_formats,
+ 		uint32_t max_formats, enum omap_color_mode supported_modes);
+ struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev,
+@@ -207,6 +267,40 @@  static inline int align_pitch(int pitch, int width, int bpp)
+ 	return ALIGN(pitch, 8 * bytespp);
+ }
+ 
++static inline enum omap_channel pipe2chan(int pipe)
++{
++	int num_mgrs = dss_feat_get_num_mgrs();
++
++	/*
++	 * We usually don't want to create a CRTC for each manager,
++	 * at least not until we have a way to expose private planes
++	 * to userspace.  Otherwise there would not be enough video
++	 * pipes left for drm planes.  The higher #'d managers tend
++	 * to have more features so start in reverse order.
++	 */
++	return num_mgrs - pipe - 1;
++}
++
++/* map crtc to vblank mask */
++static inline uint32_t pipe2vbl(int crtc)
++{
++	enum omap_channel channel = pipe2chan(crtc);
++	return dispc_mgr_get_vsync_irq(channel);
++}
++
++static inline 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;
++}
++
+ /* should these be made into common util helpers?
+  */
+ 
+diff --git a/drivers/staging/omapdrm/omap_encoder.c b/drivers/staging/omapdrm/omap_encoder.c
+index 5341d5e..e053160 100644
+--- a/drivers/staging/omapdrm/omap_encoder.c
++++ b/drivers/staging/omapdrm/omap_encoder.c
+@@ -22,37 +22,56 @@ 
+ #include "drm_crtc.h"
+ #include "drm_crtc_helper.h"
+ 
++#include <linux/list.h>
++
++
+ /*
+  * encoder funcs
+  */
+ 
+ #define to_omap_encoder(x) container_of(x, struct omap_encoder, base)
+ 
++/* The encoder and connector both map to same dssdev.. the encoder
++ * handles the 'active' parts, ie. anything the modifies the state
++ * of the hw, and the connector handles the 'read-only' parts, like
++ * detecting connection and reading edid.
++ */
+ struct omap_encoder {
+ 	struct drm_encoder base;
+-	struct omap_overlay_manager *mgr;
++	struct omap_dss_device *dssdev;
+ };
+ 
+ static void omap_encoder_destroy(struct drm_encoder *encoder)
+ {
+ 	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+-	DBG("%s", omap_encoder->mgr->name);
+ 	drm_encoder_cleanup(encoder);
+ 	kfree(omap_encoder);
+ }
+ 
++static const struct drm_encoder_funcs omap_encoder_funcs = {
++	.destroy = omap_encoder_destroy,
++};
++
++/*
++ * The CRTC drm_crtc_helper_set_mode() doesn't really give us the right
++ * order.. the easiest way to work around this for now is to make all
++ * the encoder-helper's no-op's and have the omap_crtc code take care
++ * of the sequencing and call us in the right points.
++ *
++ * Eventually to handle connecting CRTCs to different encoders properly,
++ * either the CRTC helpers need to change or we need to replace
++ * drm_crtc_helper_set_mode(), but lets wait until atomic-modeset for
++ * that.
++ */
++
+ static void omap_encoder_dpms(struct drm_encoder *encoder, int mode)
+ {
+-	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+-	DBG("%s: %d", omap_encoder->mgr->name, mode);
+ }
+ 
+ static bool omap_encoder_mode_fixup(struct drm_encoder *encoder,
+ 				  const struct drm_display_mode *mode,
+ 				  struct drm_display_mode *adjusted_mode)
+ {
+-	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+-	DBG("%s", omap_encoder->mgr->name);
+ 	return true;
+ }
+ 
+@@ -60,47 +79,16 @@  static void omap_encoder_mode_set(struct drm_encoder *encoder,
+ 				struct drm_display_mode *mode,
+ 				struct drm_display_mode *adjusted_mode)
+ {
+-	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+-	struct drm_device *dev = encoder->dev;
+-	struct omap_drm_private *priv = dev->dev_private;
+-	int i;
+-
+-	mode = adjusted_mode;
+-
+-	DBG("%s: set mode: %dx%d", omap_encoder->mgr->name,
+-			mode->hdisplay, mode->vdisplay);
+-
+-	for (i = 0; i < priv->num_connectors; i++) {
+-		struct drm_connector *connector = priv->connectors[i];
+-		if (connector->encoder == encoder)
+-			omap_connector_mode_set(connector, mode);
+-
+-	}
+ }
+ 
+ static void omap_encoder_prepare(struct drm_encoder *encoder)
+ {
+-	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+-	struct drm_encoder_helper_funcs *encoder_funcs =
+-				encoder->helper_private;
+-	DBG("%s", omap_encoder->mgr->name);
+-	encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
+ }
+ 
+ static void omap_encoder_commit(struct drm_encoder *encoder)
+ {
+-	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+-	struct drm_encoder_helper_funcs *encoder_funcs =
+-				encoder->helper_private;
+-	DBG("%s", omap_encoder->mgr->name);
+-	omap_encoder->mgr->apply(omap_encoder->mgr);
+-	encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
+ }
+ 
+-static const struct drm_encoder_funcs omap_encoder_funcs = {
+-	.destroy = omap_encoder_destroy,
+-};
+-
+ static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = {
+ 	.dpms = omap_encoder_dpms,
+ 	.mode_fixup = omap_encoder_mode_fixup,
+@@ -109,23 +97,54 @@  static const struct drm_encoder_helper_funcs omap_encoder_helper_funcs = {
+ 	.commit = omap_encoder_commit,
+ };
+ 
+-struct omap_overlay_manager *omap_encoder_get_manager(
+-		struct drm_encoder *encoder)
++/*
++ * Instead of relying on the helpers for modeset, the omap_crtc code
++ * calls these functions in the proper sequence.
++ */
++
++int omap_encoder_set_enabled(struct drm_encoder *encoder, bool enabled)
+ {
+ 	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
+-	return omap_encoder->mgr;
++	struct omap_dss_device *dssdev = omap_encoder->dssdev;
++	struct omap_dss_driver *dssdrv = dssdev->driver;
++
++	if (enabled) {
++		return dssdrv->enable(dssdev);
++	} else {
++		dssdrv->disable(dssdev);
++		return 0;
++	}
++}
++
++int omap_encoder_update(struct drm_encoder *encoder,
++		struct omap_overlay_manager *mgr,
++		struct omap_video_timings *timings)
++{
++	struct drm_device *dev = encoder->dev;
++	struct omap_encoder *omap_encoder = to_omap_encoder(encoder);
++	struct omap_dss_device *dssdev = omap_encoder->dssdev;
++	struct omap_dss_driver *dssdrv = dssdev->driver;
++	int ret;
++
++	dssdev->output->manager = mgr;
++
++	ret = dssdrv->check_timings(dssdev, timings);
++	if (ret) {
++		dev_err(dev->dev, "could not set timings: %d\n", ret);
++		return ret;
++	}
++
++	dssdrv->set_timings(dssdev, timings);
++
++	return 0;
+ }
+ 
+ /* initialize encoder */
+ struct drm_encoder *omap_encoder_init(struct drm_device *dev,
+-		struct omap_overlay_manager *mgr)
++		struct omap_dss_device *dssdev)
+ {
+ 	struct drm_encoder *encoder = NULL;
+ 	struct omap_encoder *omap_encoder;
+-	struct omap_overlay_manager_info info;
+-	int ret;
+-
+-	DBG("%s", mgr->name);
+ 
+ 	omap_encoder = kzalloc(sizeof(*omap_encoder), GFP_KERNEL);
+ 	if (!omap_encoder) {
+@@ -133,33 +152,14 @@  struct drm_encoder *omap_encoder_init(struct drm_device *dev,
+ 		goto fail;
+ 	}
+ 
+-	omap_encoder->mgr = mgr;
++	omap_encoder->dssdev = dssdev;
++
+ 	encoder = &omap_encoder->base;
+ 
+ 	drm_encoder_init(dev, encoder, &omap_encoder_funcs,
+ 			 DRM_MODE_ENCODER_TMDS);
+ 	drm_encoder_helper_add(encoder, &omap_encoder_helper_funcs);
+ 
+-	mgr->get_manager_info(mgr, &info);
+-
+-	/* TODO: fix hard-coded setup.. */
+-	info.default_color = 0x00000000;
+-	info.trans_key = 0x00000000;
+-	info.trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
+-	info.trans_enabled = false;
+-
+-	ret = mgr->set_manager_info(mgr, &info);
+-	if (ret) {
+-		dev_err(dev->dev, "could not set manager info\n");
+-		goto fail;
+-	}
+-
+-	ret = mgr->apply(mgr);
+-	if (ret) {
+-		dev_err(dev->dev, "could not apply\n");
+-		goto fail;
+-	}
+-
+ 	return encoder;
+ 
+ fail:
+diff --git a/drivers/staging/omapdrm/omap_irq.c b/drivers/staging/omapdrm/omap_irq.c
+new file mode 100644
+index 0000000..2629ba7
+--- /dev/null
++++ b/drivers/staging/omapdrm/omap_irq.c
+@@ -0,0 +1,322 @@ 
++/*
++ * drivers/staging/omapdrm/omap_irq.c
++ *
++ * Copyright (C) 2012 Texas Instruments
++ * Author: Rob Clark <rob.clark at 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"
++
++static DEFINE_SPINLOCK(list_lock);
++
++static void omap_irq_error_handler(struct omap_drm_irq *irq,
++		uint32_t irqstatus)
++{
++	DRM_ERROR("errors: %08x\n", irqstatus);
++}
++
++/* call with list_lock and dispc runtime held */
++static void omap_irq_update(struct drm_device *dev)
++{
++	struct omap_drm_private *priv = dev->dev_private;
++	struct omap_drm_irq *irq;
++	uint32_t irqmask = priv->vblank_mask;
++
++	BUG_ON(!spin_is_locked(&list_lock));
++
++	list_for_each_entry(irq, &priv->irq_list, node)
++		irqmask |= irq->irqmask;
++
++	DBG("irqmask=%08x", irqmask);
++
++	dispc_write_irqenable(irqmask);
++	dispc_read_irqenable();        /* flush posted write */
++}
++
++void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq)
++{
++	struct omap_drm_private *priv = dev->dev_private;
++	unsigned long flags;
++
++	dispc_runtime_get();
++	spin_lock_irqsave(&list_lock, flags);
++
++	if (!WARN_ON(irq->registered)) {
++		irq->registered = true;
++		list_add(&irq->node, &priv->irq_list);
++		omap_irq_update(dev);
++	}
++
++	spin_unlock_irqrestore(&list_lock, flags);
++	dispc_runtime_put();
++}
++
++void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq)
++{
++	unsigned long flags;
++
++	dispc_runtime_get();
++	spin_lock_irqsave(&list_lock, flags);
++
++	if (!WARN_ON(!irq->registered)) {
++		irq->registered = false;
++		list_del(&irq->node);
++		omap_irq_update(dev);
++	}
++
++	spin_unlock_irqrestore(&list_lock, flags);
++	dispc_runtime_put();
++}
++
++struct omap_irq_wait {
++	struct omap_drm_irq irq;
++	int count;
++};
++
++static DECLARE_WAIT_QUEUE_HEAD(wait_event);
++
++static void wait_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
++{
++	struct omap_irq_wait *wait =
++			container_of(irq, struct omap_irq_wait, irq);
++	wait->count--;
++	wake_up_all(&wait_event);
++}
++
++struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev,
++		uint32_t irqmask, int count)
++{
++	struct omap_irq_wait *wait = kzalloc(sizeof(*wait), GFP_KERNEL);
++	wait->irq.irq = wait_irq;
++	wait->irq.irqmask = irqmask;
++	wait->count = count;
++	omap_irq_register(dev, &wait->irq);
++	return wait;
++}
++
++int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait,
++		unsigned long timeout)
++{
++	int ret = wait_event_timeout(wait_event, (wait->count <= 0), timeout);
++	omap_irq_unregister(dev, &wait->irq);
++	kfree(wait);
++	if (ret == 0)
++		return -1;
++	return 0;
++}
++
++/**
++ * enable_vblank - enable vblank interrupt events
++ * @dev: DRM device
++ * @crtc: which irq to enable
++ *
++ * Enable vblank interrupts for @crtc.  If the device doesn't have
++ * a hardware vblank counter, this routine should be a no-op, since
++ * interrupts will have to stay on to keep the count accurate.
++ *
++ * RETURNS
++ * Zero on success, appropriate errno if the given @crtc's vblank
++ * interrupt cannot be enabled.
++ */
++int omap_irq_enable_vblank(struct drm_device *dev, int crtc)
++{
++	struct omap_drm_private *priv = dev->dev_private;
++	unsigned long flags;
++
++	DBG("dev=%p, crtc=%d", dev, crtc);
++
++	dispc_runtime_get();
++	spin_lock_irqsave(&list_lock, flags);
++	priv->vblank_mask |= pipe2vbl(crtc);
++	omap_irq_update(dev);
++	spin_unlock_irqrestore(&list_lock, flags);
++	dispc_runtime_put();
++
++	return 0;
++}
++
++/**
++ * disable_vblank - disable vblank interrupt events
++ * @dev: DRM device
++ * @crtc: which irq to enable
++ *
++ * Disable vblank interrupts for @crtc.  If the device doesn't have
++ * a hardware vblank counter, this routine should be a no-op, since
++ * interrupts will have to stay on to keep the count accurate.
++ */
++void omap_irq_disable_vblank(struct drm_device *dev, int crtc)
++{
++	struct omap_drm_private *priv = dev->dev_private;
++	unsigned long flags;
++
++	DBG("dev=%p, crtc=%d", dev, crtc);
++
++	dispc_runtime_get();
++	spin_lock_irqsave(&list_lock, flags);
++	priv->vblank_mask &= ~pipe2vbl(crtc);
++	omap_irq_update(dev);
++	spin_unlock_irqrestore(&list_lock, flags);
++	dispc_runtime_put();
++}
++
++irqreturn_t omap_irq_handler(DRM_IRQ_ARGS)
++{
++	struct drm_device *dev = (struct drm_device *) arg;
++	struct omap_drm_private *priv = dev->dev_private;
++	struct omap_drm_irq *handler, *n;
++	unsigned long flags;
++	unsigned int id;
++	u32 irqstatus;
++
++	irqstatus = dispc_read_irqstatus();
++	dispc_clear_irqstatus(irqstatus);
++	dispc_read_irqstatus();        /* flush posted write */
++
++	VERB("irqs: %08x", irqstatus);
++
++	for (id = 0; id < priv->num_crtcs; id++)
++		if (irqstatus & pipe2vbl(id))
++			drm_handle_vblank(dev, id);
++
++	spin_lock_irqsave(&list_lock, flags);
++	list_for_each_entry_safe(handler, n, &priv->irq_list, node) {
++		if (handler->irqmask & irqstatus) {
++			spin_unlock_irqrestore(&list_lock, flags);
++			handler->irq(handler, handler->irqmask & irqstatus);
++			spin_lock_irqsave(&list_lock, flags);
++		}
++	}
++	spin_unlock_irqrestore(&list_lock, flags);
++
++	return IRQ_HANDLED;
++}
++
++void omap_irq_preinstall(struct drm_device *dev)
++{
++	DBG("dev=%p", dev);
++	dispc_runtime_get();
++	dispc_clear_irqstatus(0xffffffff);
++	dispc_runtime_put();
++}
++
++int omap_irq_postinstall(struct drm_device *dev)
++{
++	struct omap_drm_private *priv = dev->dev_private;
++	struct omap_drm_irq *error_handler = &priv->error_handler;
++
++	DBG("dev=%p", dev);
++
++	INIT_LIST_HEAD(&priv->irq_list);
++
++	error_handler->irq = omap_irq_error_handler;
++	error_handler->irqmask = DISPC_IRQ_OCP_ERR;
++
++	/* for now ignore DISPC_IRQ_SYNC_LOST_DIGIT.. really I think
++	 * we just need to ignore it while enabling tv-out
++	 */
++	error_handler->irqmask &= ~DISPC_IRQ_SYNC_LOST_DIGIT;
++
++	omap_irq_register(dev, error_handler);
++
++	return 0;
++}
++
++void omap_irq_uninstall(struct drm_device *dev)
++{
++	DBG("dev=%p", dev);
++	// TODO prolly need to call drm_irq_uninstall() somewhere too
++}
++
++/*
++ * We need a special version, instead of just using drm_irq_install(),
++ * because we need to register the irq via omapdss.  Once omapdss and
++ * omapdrm are merged together we can assign the dispc hwmod data to
++ * ourselves and drop these and just use drm_irq_{install,uninstall}()
++ */
++
++int omap_drm_irq_install(struct drm_device *dev)
++{
++	int ret;
++
++	mutex_lock(&dev->struct_mutex);
++
++	if (dev->irq_enabled) {
++		mutex_unlock(&dev->struct_mutex);
++		return -EBUSY;
++	}
++	dev->irq_enabled = 1;
++	mutex_unlock(&dev->struct_mutex);
++
++	/* Before installing handler */
++	if (dev->driver->irq_preinstall)
++		dev->driver->irq_preinstall(dev);
++
++	ret = dispc_request_irq(dev->driver->irq_handler, dev);
++
++	if (ret < 0) {
++		mutex_lock(&dev->struct_mutex);
++		dev->irq_enabled = 0;
++		mutex_unlock(&dev->struct_mutex);
++		return ret;
++	}
++
++	/* After installing handler */
++	if (dev->driver->irq_postinstall)
++		ret = dev->driver->irq_postinstall(dev);
++
++	if (ret < 0) {
++		mutex_lock(&dev->struct_mutex);
++		dev->irq_enabled = 0;
++		mutex_unlock(&dev->struct_mutex);
++		dispc_free_irq(dev);
++	}
++
++	return ret;
++}
++
++int omap_drm_irq_uninstall(struct drm_device *dev)
++{
++	unsigned long irqflags;
++	int irq_enabled, i;
++
++	mutex_lock(&dev->struct_mutex);
++	irq_enabled = dev->irq_enabled;
++	dev->irq_enabled = 0;
++	mutex_unlock(&dev->struct_mutex);
++
++	/*
++	 * Wake up any waiters so they don't hang.
++	 */
++	if (dev->num_crtcs) {
++		spin_lock_irqsave(&dev->vbl_lock, irqflags);
++		for (i = 0; i < dev->num_crtcs; i++) {
++			DRM_WAKEUP(&dev->vbl_queue[i]);
++			dev->vblank_enabled[i] = 0;
++			dev->last_vblank[i] =
++				dev->driver->get_vblank_counter(dev, i);
++		}
++		spin_unlock_irqrestore(&dev->vbl_lock, irqflags);
++	}
++
++	if (!irq_enabled)
++		return -EINVAL;
++
++	if (dev->driver->irq_uninstall)
++		dev->driver->irq_uninstall(dev);
++
++	dispc_free_irq(dev);
++
++	return 0;
++}
+diff --git a/drivers/staging/omapdrm/omap_plane.c b/drivers/staging/omapdrm/omap_plane.c
+index 2a8e5ba..bb989d7 100644
+--- a/drivers/staging/omapdrm/omap_plane.c
++++ b/drivers/staging/omapdrm/omap_plane.c
+@@ -41,12 +41,14 @@  struct callback {
+ 
+ struct omap_plane {
+ 	struct drm_plane base;
+-	struct omap_overlay *ovl;
++	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;
+@@ -54,189 +56,15 @@  struct omap_plane {
+ 	uint32_t nformats;
+ 	uint32_t formats[32];
+ 
+-	/* for synchronizing access to unpins fifo */
+-	struct mutex unpin_mutex;
++	struct omap_drm_irq error_irq;
+ 
+-	/* set of bo's pending unpin until next END_WIN irq */
++	/* set of bo's pending unpin until next post_apply() */
+ 	DECLARE_KFIFO_PTR(unpin_fifo, struct drm_gem_object *);
+-	int num_unpins, pending_num_unpins;
+-
+-	/* for deferred unpin when we need to wait for scanout complete irq */
+-	struct work_struct work;
+-
+-	/* callback on next endwin irq */
+-	struct callback endwin;
+-};
+ 
+-/* map from ovl->id to the irq we are interested in for scanout-done */
+-static const uint32_t id2irq[] = {
+-		[OMAP_DSS_GFX]    = DISPC_IRQ_GFX_END_WIN,
+-		[OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_END_WIN,
+-		[OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_END_WIN,
+-		[OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_END_WIN,
++	// XXX maybe get rid of this and handle vblank in crtc too?
++	struct callback apply_done_cb;
+ };
+ 
+-static void dispc_isr(void *arg, uint32_t mask)
+-{
+-	struct drm_plane *plane = arg;
+-	struct omap_plane *omap_plane = to_omap_plane(plane);
+-	struct omap_drm_private *priv = plane->dev->dev_private;
+-
+-	omap_dispc_unregister_isr(dispc_isr, plane,
+-			id2irq[omap_plane->ovl->id]);
+-
+-	queue_work(priv->wq, &omap_plane->work);
+-}
+-
+-static void unpin_worker(struct work_struct *work)
+-{
+-	struct omap_plane *omap_plane =
+-			container_of(work, struct omap_plane, work);
+-	struct callback endwin;
+-
+-	mutex_lock(&omap_plane->unpin_mutex);
+-	DBG("unpinning %d of %d", omap_plane->num_unpins,
+-			omap_plane->num_unpins + omap_plane->pending_num_unpins);
+-	while (omap_plane->num_unpins > 0) {
+-		struct drm_gem_object *bo = NULL;
+-		int ret = kfifo_get(&omap_plane->unpin_fifo, &bo);
+-		WARN_ON(!ret);
+-		omap_gem_put_paddr(bo);
+-		drm_gem_object_unreference_unlocked(bo);
+-		omap_plane->num_unpins--;
+-	}
+-	endwin = omap_plane->endwin;
+-	omap_plane->endwin.fxn = NULL;
+-	mutex_unlock(&omap_plane->unpin_mutex);
+-
+-	if (endwin.fxn)
+-		endwin.fxn(endwin.arg);
+-}
+-
+-static void install_irq(struct drm_plane *plane)
+-{
+-	struct omap_plane *omap_plane = to_omap_plane(plane);
+-	struct omap_overlay *ovl = omap_plane->ovl;
+-	int ret;
+-
+-	ret = omap_dispc_register_isr(dispc_isr, plane, id2irq[ovl->id]);
+-
+-	/*
+-	 * omapdss has upper limit on # of registered irq handlers,
+-	 * which we shouldn't hit.. but if we do the limit should
+-	 * be raised or bad things happen:
+-	 */
+-	WARN_ON(ret == -EBUSY);
+-}
+-
+-/* push changes down to dss2 */
+-static int commit(struct drm_plane *plane)
+-{
+-	struct drm_device *dev = plane->dev;
+-	struct omap_plane *omap_plane = to_omap_plane(plane);
+-	struct omap_overlay *ovl = omap_plane->ovl;
+-	struct omap_overlay_info *info = &omap_plane->info;
+-	int ret;
+-
+-	DBG("%s", ovl->name);
+-	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);
+-
+-	/* NOTE: do we want to do this at all here, or just wait
+-	 * for dpms(ON) since other CRTC's may not have their mode
+-	 * set yet, so fb dimensions may still change..
+-	 */
+-	ret = ovl->set_overlay_info(ovl, info);
+-	if (ret) {
+-		dev_err(dev->dev, "could not set overlay info\n");
+-		return ret;
+-	}
+-
+-	mutex_lock(&omap_plane->unpin_mutex);
+-	omap_plane->num_unpins += omap_plane->pending_num_unpins;
+-	omap_plane->pending_num_unpins = 0;
+-	mutex_unlock(&omap_plane->unpin_mutex);
+-
+-	/* our encoder doesn't necessarily get a commit() after this, in
+-	 * particular in the dpms() and mode_set_base() cases, so force the
+-	 * manager to update:
+-	 *
+-	 * could this be in the encoder somehow?
+-	 */
+-	if (ovl->manager) {
+-		ret = ovl->manager->apply(ovl->manager);
+-		if (ret) {
+-			dev_err(dev->dev, "could not apply settings\n");
+-			return ret;
+-		}
+-
+-		/*
+-		 * NOTE: really this should be atomic w/ mgr->apply() but
+-		 * omapdss does not expose such an API
+-		 */
+-		if (omap_plane->num_unpins > 0)
+-			install_irq(plane);
+-
+-	} else {
+-		struct omap_drm_private *priv = dev->dev_private;
+-		queue_work(priv->wq, &omap_plane->work);
+-	}
+-
+-
+-	if (ovl->is_enabled(ovl)) {
+-		omap_framebuffer_flush(plane->fb, info->pos_x, info->pos_y,
+-				info->out_width, info->out_height);
+-	}
+-
+-	return 0;
+-}
+-
+-/* when CRTC that we are attached to has potentially changed, this checks
+- * if we are attached to proper manager, and if necessary updates.
+- */
+-static void update_manager(struct drm_plane *plane)
+-{
+-	struct omap_drm_private *priv = plane->dev->dev_private;
+-	struct omap_plane *omap_plane = to_omap_plane(plane);
+-	struct omap_overlay *ovl = omap_plane->ovl;
+-	struct omap_overlay_manager *mgr = NULL;
+-	int i;
+-
+-	if (plane->crtc) {
+-		for (i = 0; i < priv->num_encoders; i++) {
+-			struct drm_encoder *encoder = priv->encoders[i];
+-			if (encoder->crtc == plane->crtc) {
+-				mgr = omap_encoder_get_manager(encoder);
+-				break;
+-			}
+-		}
+-	}
+-
+-	if (ovl->manager != mgr) {
+-		bool enabled = ovl->is_enabled(ovl);
+-
+-		/* don't switch things around with enabled overlays: */
+-		if (enabled)
+-			omap_plane_dpms(plane, DRM_MODE_DPMS_OFF);
+-
+-		if (ovl->manager) {
+-			DBG("disconnecting %s from %s", ovl->name,
+-					ovl->manager->name);
+-			ovl->unset_manager(ovl);
+-		}
+-
+-		if (mgr) {
+-			DBG("connecting %s to %s", ovl->name, mgr->name);
+-			ovl->set_manager(ovl, mgr);
+-		}
+-
+-		if (enabled && mgr)
+-			omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
+-	}
+-}
+-
+ static void unpin(void *arg, struct drm_gem_object *bo)
+ {
+ 	struct drm_plane *plane = arg;
+@@ -244,7 +72,6 @@  static void unpin(void *arg, struct drm_gem_object *bo)
+ 
+ 	if (kfifo_put(&omap_plane->unpin_fifo,
+ 			(const struct drm_gem_object **)&bo)) {
+-		omap_plane->pending_num_unpins++;
+ 		/* also hold a ref so it isn't free'd while pinned */
+ 		drm_gem_object_reference(bo);
+ 	} else {
+@@ -264,13 +91,19 @@  static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb)
+ 
+ 		DBG("%p -> %p", pinned_fb, fb);
+ 
+-		mutex_lock(&omap_plane->unpin_mutex);
++		if (fb)
++			drm_framebuffer_reference(fb);
++
+ 		ret = omap_framebuffer_replace(pinned_fb, fb, plane, unpin);
+-		mutex_unlock(&omap_plane->unpin_mutex);
++
++		if (pinned_fb)
++			drm_framebuffer_unreference(pinned_fb);
+ 
+ 		if (ret) {
+ 			dev_err(plane->dev->dev, "could not swap %p -> %p\n",
+ 					omap_plane->pinned_fb, fb);
++			if (fb)
++				drm_framebuffer_unreference(fb);
+ 			omap_plane->pinned_fb = NULL;
+ 			return ret;
+ 		}
+@@ -281,31 +114,90 @@  static int update_pin(struct drm_plane *plane, struct drm_framebuffer *fb)
+ 	return 0;
+ }
+ 
+-/* update parameters that are dependent on the framebuffer dimensions and
+- * position within the fb that this plane scans out from. This is called
+- * when framebuffer or x,y base may have changed.
+- */
+-static void update_scanout(struct drm_plane *plane)
++static void omap_plane_pre_apply(struct omap_drm_apply *apply)
+ {
+-	struct omap_plane *omap_plane = to_omap_plane(plane);
+-	struct omap_overlay_info *info = &omap_plane->info;
++	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;
++	enum omap_channel channel;
++	bool enabled = omap_plane->enabled && crtc;
++	bool ilace, replication;
+ 	int ret;
+ 
+-	ret = update_pin(plane, plane->fb);
+-	if (ret) {
+-		dev_err(plane->dev->dev,
+-			"could not pin fb: %d\n", ret);
+-		omap_plane_dpms(plane, DRM_MODE_DPMS_OFF);
++	DBG("%s, enabled=%d", omap_plane->name, enabled);
++
++	/* if fb has changed, pin new fb: */
++	update_pin(plane, enabled ? plane->fb : NULL);
++
++	if (!enabled) {
++		dispc_ovl_enable(omap_plane->id, false);
+ 		return;
+ 	}
+ 
++	channel = omap_crtc_channel(crtc);
++
++	/* update scanout: */
+ 	omap_framebuffer_update_scanout(plane->fb, win, info);
+ 
+-	DBG("%s: %d,%d: %08x %08x (%d)", omap_plane->ovl->name,
+-			win->src_x, win->src_y,
+-			(u32)info->paddr, (u32)info->p_uv_addr,
++	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);
++
++	/* TODO: */
++	ilace = false;
++	replication = false;
++
++	/* and finally, update omapdss: */
++	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);
++		return;
++	}
++
++	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)
++{
++	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->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);
++	}
++	return 0;
+ }
+ 
+ int omap_plane_mode_set(struct drm_plane *plane,
+@@ -313,7 +205,8 @@  int omap_plane_mode_set(struct drm_plane *plane,
+ 		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)
++		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;
+@@ -329,17 +222,20 @@  int omap_plane_mode_set(struct drm_plane *plane,
+ 	win->src_w = src_w >> 16;
+ 	win->src_h = src_h >> 16;
+ 
+-	/* note: this is done after this fxn returns.. but if we need
+-	 * to do a commit/update_scanout, etc before this returns we
+-	 * need the current value.
+-	 */
++	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;
+ 
+-	update_scanout(plane);
+-	update_manager(plane);
+-
+-	return 0;
++	return apply(plane);
+ }
+ 
+ static int omap_plane_update(struct drm_plane *plane,
+@@ -349,9 +245,12 @@  static int omap_plane_update(struct drm_plane *plane,
+ 		uint32_t src_x, uint32_t src_y,
+ 		uint32_t src_w, uint32_t src_h)
+ {
+-	omap_plane_mode_set(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h,
+-			src_x, src_y, src_w, src_h);
+-	return omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
++	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)
+@@ -364,48 +263,32 @@  static int omap_plane_disable(struct drm_plane *plane)
+ static void omap_plane_destroy(struct drm_plane *plane)
+ {
+ 	struct omap_plane *omap_plane = to_omap_plane(plane);
+-	DBG("%s", omap_plane->ovl->name);
++
++	DBG("%s", omap_plane->name);
++
++	omap_irq_unregister(plane->dev, &omap_plane->error_irq);
++
+ 	omap_plane_disable(plane);
+ 	drm_plane_cleanup(plane);
+-	WARN_ON(omap_plane->pending_num_unpins + omap_plane->num_unpins > 0);
++
++	WARN_ON(!kfifo_is_empty(&omap_plane->unpin_fifo));
+ 	kfifo_free(&omap_plane->unpin_fifo);
++
+ 	kfree(omap_plane);
+ }
+ 
+ int omap_plane_dpms(struct drm_plane *plane, int mode)
+ {
+ 	struct omap_plane *omap_plane = to_omap_plane(plane);
+-	struct omap_overlay *ovl = omap_plane->ovl;
+-	int r;
++	bool enabled = (mode == DRM_MODE_DPMS_ON);
++	int ret = 0;
+ 
+-	DBG("%s: %d", omap_plane->ovl->name, mode);
+-
+-	if (mode == DRM_MODE_DPMS_ON) {
+-		update_scanout(plane);
+-		r = commit(plane);
+-		if (!r)
+-			r = ovl->enable(ovl);
+-	} else {
+-		struct omap_drm_private *priv = plane->dev->dev_private;
+-		r = ovl->disable(ovl);
+-		update_pin(plane, NULL);
+-		queue_work(priv->wq, &omap_plane->work);
++	if (enabled != omap_plane->enabled) {
++		omap_plane->enabled = enabled;
++		ret = apply(plane);
+ 	}
+ 
+-	return r;
+-}
+-
+-void omap_plane_on_endwin(struct drm_plane *plane,
+-		void (*fxn)(void *), void *arg)
+-{
+-	struct omap_plane *omap_plane = to_omap_plane(plane);
+-
+-	mutex_lock(&omap_plane->unpin_mutex);
+-	omap_plane->endwin.fxn = fxn;
+-	omap_plane->endwin.arg = arg;
+-	mutex_unlock(&omap_plane->unpin_mutex);
+-
+-	install_irq(plane);
++	return ret;
+ }
+ 
+ /* helper to install properties which are common to planes and crtcs */
+@@ -454,25 +337,13 @@  int omap_plane_set_property(struct drm_plane *plane,
+ 	int ret = -EINVAL;
+ 
+ 	if (property == priv->rotation_prop) {
+-		struct omap_overlay *ovl = omap_plane->ovl;
+-
+-		DBG("%s: rotation: %02x", ovl->name, (uint32_t)val);
++		DBG("%s: rotation: %02x", omap_plane->name, (uint32_t)val);
+ 		omap_plane->win.rotation = val;
+-
+-		if (ovl->is_enabled(ovl))
+-			ret = omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
+-		else
+-			ret = 0;
++		ret = apply(plane);
+ 	} else if (property == priv->zorder_prop) {
+-		struct omap_overlay *ovl = omap_plane->ovl;
+-
+-		DBG("%s: zorder: %d", ovl->name, (uint32_t)val);
++		DBG("%s: zorder: %02x", omap_plane->name, (uint32_t)val);
+ 		omap_plane->info.zorder = val;
+-
+-		if (ovl->is_enabled(ovl))
+-			ret = omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
+-		else
+-			ret = 0;
++		ret = apply(plane);
+ 	}
+ 
+ 	return ret;
+@@ -485,20 +356,38 @@  static const struct drm_plane_funcs omap_plane_funcs = {
+ 		.set_property = omap_plane_set_property,
+ };
+ 
++static void omap_plane_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
++{
++	struct omap_plane *omap_plane =
++			container_of(irq, struct omap_plane, error_irq);
++	DRM_ERROR("%s: errors: %08x\n", omap_plane->name, irqstatus);
++}
++
++static const char *plane_names[] = {
++		[OMAP_DSS_GFX] = "gfx",
++		[OMAP_DSS_VIDEO1] = "vid1",
++		[OMAP_DSS_VIDEO2] = "vid2",
++		[OMAP_DSS_VIDEO3] = "vid3",
++};
++
++static const uint32_t error_irqs[] = {
++		[OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW,
++		[OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW,
++		[OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW,
++		[OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW,
++};
++
+ /* initialize plane */
+ struct drm_plane *omap_plane_init(struct drm_device *dev,
+-		struct omap_overlay *ovl, unsigned int possible_crtcs,
+-		bool priv)
++		int id, bool private_plane)
+ {
++	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: possible_crtcs=%08x, priv=%d", ovl->name,
+-			possible_crtcs, priv);
+-
+-	/* friendly reminder to update table for future hw: */
+-	WARN_ON(ovl->id >= ARRAY_SIZE(id2irq));
++	DBG("%s: priv=%d", plane_names[id], private_plane);
+ 
+ 	omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
+ 	if (!omap_plane) {
+@@ -506,47 +395,50 @@  struct drm_plane *omap_plane_init(struct drm_device *dev,
+ 		goto fail;
+ 	}
+ 
+-	mutex_init(&omap_plane->unpin_mutex);
+-
+ 	ret = kfifo_alloc(&omap_plane->unpin_fifo, 16, GFP_KERNEL);
+ 	if (ret) {
+ 		dev_err(dev->dev, "could not allocate unpin FIFO\n");
+ 		goto fail;
+ 	}
+ 
+-	INIT_WORK(&omap_plane->work, unpin_worker);
+-
+ 	omap_plane->nformats = omap_framebuffer_get_formats(
+ 			omap_plane->formats, ARRAY_SIZE(omap_plane->formats),
+-			ovl->supported_modes);
+-	omap_plane->ovl = ovl;
++			dss_feat_get_supported_color_modes(id));
++	omap_plane->id = id;
++	omap_plane->name = plane_names[id];
++
+ 	plane = &omap_plane->base;
+ 
+-	drm_plane_init(dev, plane, possible_crtcs, &omap_plane_funcs,
+-			omap_plane->formats, omap_plane->nformats, priv);
++	omap_plane->apply.pre_apply  = omap_plane_pre_apply;
++	omap_plane->apply.post_apply = omap_plane_post_apply;
++
++	omap_plane->error_irq.irqmask = error_irqs[id];
++	omap_plane->error_irq.irq = omap_plane_error_irq;
++	omap_irq_register(dev, &omap_plane->error_irq);
++
++	drm_plane_init(dev, plane, (1 << priv->num_crtcs) - 1, &omap_plane_funcs,
++			omap_plane->formats, omap_plane->nformats, private_plane);
+ 
+ 	omap_plane_install_properties(plane, &plane->base);
+ 
+ 	/* get our starting configuration, set defaults for parameters
+ 	 * we don't currently use, etc:
+ 	 */
+-	ovl->get_overlay_info(ovl, &omap_plane->info);
+-	omap_plane->info.rotation_type = OMAP_DSS_ROT_DMA;
+-	omap_plane->info.rotation = OMAP_DSS_ROT_0;
+-	omap_plane->info.global_alpha = 0xff;
+-	omap_plane->info.mirror = 0;
++	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 (priv)
++	if (private_plane)
+ 		omap_plane->info.zorder = 0;
+ 	else
+-		omap_plane->info.zorder = ovl->id;
+-
+-	update_manager(plane);
++		omap_plane->info.zorder = id;
+ 
+ 	return plane;
  
diff --git a/arm-tegra-nvec-kconfig.patch b/arm-tegra-nvec-kconfig.patch
index a3f568c..64aa9f8 100644
--- a/arm-tegra-nvec-kconfig.patch
+++ b/arm-tegra-nvec-kconfig.patch
@@ -1,10 +1,10 @@
---- linux-2.6.42.noarch/drivers/staging/nvec/Kconfig.orig	2012-02-02 08:16:12.512727480 -0600
-+++ linux-2.6.42.noarch/drivers/staging/nvec/Kconfig	2012-02-01 18:44:56.674990109 -0600
+--- linux-3.8.0-0.rc2.git1.1.fc19.x86_64/drivers/staging/nvec/Kconfig.orig	2013-01-07 11:04:43.493510550 +0000
++++ linux-3.8.0-0.rc2.git1.1.fc19.x86_64/drivers/staging/nvec/Kconfig	2013-01-07 11:14:18.186033211 +0000
 @@ -1,6 +1,6 @@
  config MFD_NVEC
  	bool "NV Tegra Embedded Controller SMBus Interface"
 -	depends on I2C && GPIOLIB && ARCH_TEGRA
-+	depends on I2C && GPIOLIB && ARCH_TEGRA && MFD_CORE=y
++	depends on I2C && GPIOLIB && ARCH_TEGRA && MFD_CORE
+ 	select MFD_CORE
  	help
  	    Say Y here to enable support for a nVidia compliant embedded
- 	    controller.
diff --git a/config-arm-generic b/config-arm-generic
index f1806ef..ac77026 100644
--- a/config-arm-generic
+++ b/config-arm-generic
@@ -293,6 +293,7 @@ CONFIG_GPIO_MCP23S08=m
 CONFIG_GPIO_ADNP=m
 CONFIG_PL310_ERRATA_753970=y
 
+CONFIG_MFD_CORE=m
 CONFIG_MFD_88PM800=m
 CONFIG_MFD_88PM805=m
 CONFIG_MFD_SYSCON=y
diff --git a/config-arm-omap b/config-arm-omap
index 71e4002..d020c68 100644
--- a/config-arm-omap
+++ b/config-arm-omap
@@ -306,8 +306,8 @@ CONFIG_PWM_TIEHRPWM=m
 CONFIG_PWM_TWL=m
 CONFIG_PWM_TWL_LED=m
 
-CONFIG_IR_RX51=m
-CONFIG_BATTERY_RX51=m
+# CONFIG_IR_RX51 is not set
+# CONFIG_BATTERY_RX51 is not set
 
 # CONFIG_TIDSPBRIDGE is not set
 # CONFIG_TIDSPBRIDGE_MEMPOOL_SIZE=0x600000
diff --git a/config-arm-tegra b/config-arm-tegra
index e4d1fe1..1738aed 100644
--- a/config-arm-tegra
+++ b/config-arm-tegra
@@ -75,9 +75,9 @@ CONFIG_SND_SOC_TEGRA_TRIMSLICE=m
 # CONFIG_SND_SOC_TEGRA30_AHUB is not set
 # CONFIG_SND_SOC_TEGRA30_I2S is not set
 
-CONFIG_MFD_NVEC=y
+# AC100 (PAZ00)
+# CONFIG_MFD_NVEC is not set
 CONFIG_MFD_TPS80031=y
-
 CONFIG_KEYBOARD_NVEC=y
 CONFIG_SERIO_NVEC_PS2=y
 CONFIG_NVEC_POWER=y
diff --git a/config-armv7 b/config-armv7
index 20c3c4a..2ff23c1 100644
--- a/config-armv7
+++ b/config-armv7
@@ -172,7 +172,7 @@ CONFIG_FB_ARMCLCD=m
 CONFIG_I2C_VERSATILE=m
 CONFIG_OC_ETM=y
 CONFIG_ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA=y
-CONFIG_SENSORS_VEXPRESS=m
+# CONFIG_SENSORS_VEXPRESS is not set
 
 # unknown and needs review
 CONFIG_ARM_AMBA=y
@@ -186,14 +186,50 @@ CONFIG_I2C_MV64XXX=m
 CONFIG_PINCTRL_MVEBU=y
 CONFIG_MVNETA=m
 
+# Allwinner a1x
+# CONFIG_SUNXI_RFKILL=y
+# CONFIG_SUNXI_NAND=y
+# CONFIG_SUNXI_DBGREG=m
+# CONFIG_WEMAC_SUN4I=y
+# CONFIG_KEYBOARD_SUN4IKEYPAD=m
+# CONFIG_KEYBOARD_SUN4I_KEYBOARD=m
+# CONFIG_IR_SUN4I=m
+# CONFIG_TOUCHSCREEN_SUN4I_TS=m
+# CONFIG_SUN4I_G2D=y
+# CONFIG_I2C_SUN4I=y
+# CONFIG_DRM_MALI=m
+# CONFIG_MALI=m
+# CONFIG_FB_SUNXI=m
+# CONFIG_FB_SUNXI_UMP=y
+# CONFIG_FB_SUNXI_LCD=m
+# CONFIG_FB_SUNXI_HDMI=m
+# CONFIG_SOUND_SUN4I=y
+# CONFIG_SND_SUN4I_SOC_CODEC=y
+# CONFIG_SND_SUN4I_SOC_HDMIAUDIO=y
+# CONFIG_SND_SUN4I_SOC_SPDIF=m
+# CONFIG_SND_SUN4I_SOC_I2S_INTERFACE=m
+# CONFIG_SND_SOC_I2C_AND_SPI=y
+# CONFIG_USB_SW_SUN4I_HCD=y
+# CONFIG_USB_SW_SUN4I_HCD0=y
+# CONFIG_USB_SW_SUN4I_HCI=y
+# CONFIG_USB_SW_SUN4I_EHCI0=y
+# CONFIG_USB_SW_SUN4I_EHCI1=y
+# CONFIG_USB_SW_SUN4I_OHCI0=y
+# CONFIG_USB_SW_SUN4I_OHCI1=y
+# CONFIG_USB_SW_SUN4I_USB=y
+# CONFIG_USB_SW_SUN4I_USB_MANAGER=y
+# CONFIG_MMC_SUNXI_POWER_CONTROL=y
+# CONFIG_MMC_SUNXI=y
+# CONFIG_RTC_DRV_SUN4I=y
+
 # imx 
 CONFIG_BACKLIGHT_PWM=m
-CONFIG_DRM_IMX=m
-CONFIG_DRM_IMX_FB_HELPER=m
-CONFIG_DRM_IMX_PARALLEL_DISPLAY=m
-CONFIG_DRM_IMX_IPUV3_CORE=m
-CONFIG_DRM_IMX_IPUV3=m
-CONFIG_VIDEO_CODA=m
+# CONFIG_DRM_IMX is not set
+# CONFIG_DRM_IMX_FB_HELPER=m
+# CONFIG_DRM_IMX_PARALLEL_DISPLAY=m
+# CONFIG_DRM_IMX_IPUV3_CORE=m
+# CONFIG_DRM_IMX_IPUV3=m
+# CONFIG_VIDEO_CODA is not set
 
 CONFIG_INPUT_PWM_BEEPER=m
 
@@ -275,6 +311,7 @@ CONFIG_EDAC_LEGACY_SYSFS=y
 CONFIG_MPCORE_WATCHDOG=m
 
 # Multi function devices
+CONFIG_MFD_CORE=m
 CONFIG_MFD_T7L66XB=y
 CONFIG_MFD_TC6387XB=y
 CONFIG_MFD_SYSCON=y
diff --git a/kernel.spec b/kernel.spec
index 5163e07..a4507fb 100644
--- a/kernel.spec
+++ b/kernel.spec
@@ -717,12 +717,16 @@ Patch21000: arm-export-read_current_timer.patch
 Patch21001: arm-allnoconfig-error-__LINUX_ARM_ARCH__-undeclared.patch
 
 # OMAP
+# https://patchwork.kernel.org/patch/1721241/
+# https://patchwork.kernel.org/patch/1839401/
 Patch21003: arm-omapdrm-fixinc.patch
 
 # ARM tegra
 Patch21004: arm-tegra-nvec-kconfig.patch
 Patch21005: arm-tegra-usb-no-reset-linux33.patch
-Patch21006: arm-tegra-sdhci-module-fix.patch
+
+# https://patchwork.kernel.org/patch/1909111/
+Patch21010: namei-include.patch
 
 #rhbz 754518
 Patch21235: scsi-sd_revalidate_disk-prevent-NULL-ptr-deref.patch
@@ -1304,10 +1308,10 @@ ApplyPatch vmbugon-warnon.patch
 #
 ApplyPatch arm-export-read_current_timer.patch
 ApplyPatch arm-allnoconfig-error-__LINUX_ARM_ARCH__-undeclared.patch
-# ApplyPatch arm-omapdrm-fixinc.patch
+ApplyPatch arm-omapdrm-fixinc.patch
 # ApplyPatch arm-tegra-nvec-kconfig.patch
 ApplyPatch arm-tegra-usb-no-reset-linux33.patch
-# ApplyPatch arm-tegra-sdhci-module-fix.patch
+ApplyPatch namei-include.patch
 
 #
 # bugfixes to drivers and filesystems
@@ -2295,6 +2299,10 @@ fi
 #                 ||----w |
 #                 ||     ||
 %changelog
+* Mon Jan  7 2013 Peter Robinson <pbrobinson at fedoraproject.org>
+- Further ARM config updates
+- Add patch to fix building omapdrm
+
 * Mon Jan 07 2013 Justin M. Forbes <jforbes at redhat.com>
 - Bye sparc
 


More information about the scm-commits mailing list