diff --git a/arm-omapdrm-fixinc.patch b/arm-omapdrm-fixinc.patch index 001b4c57f..6892655eb 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 -+#include -+#include + #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 + #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 ++ ++ + /* + * 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 ++ * ++ * 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 . ++ */ ++ ++#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 a3f568cd5..64aa9f8e3 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 f1806ef00..ac77026fb 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 71e400246..d020c68d8 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 e4d1fe12f..1738aedb9 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 20c3c4ac2..2ff23c17a 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 5163e0739..a4507fbc0 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 +- Further ARM config updates +- Add patch to fix building omapdrm + * Mon Jan 07 2013 Justin M. Forbes - Bye sparc