[PATCH] drm/omap: use omapdss low level API

Sumit Semwal sumit.semwal at linaro.org
Mon Dec 10 00:35:50 PST 2012


On Friday 07 December 2012 04:15 PM, Archit Taneja wrote:
> On Wednesday 05 December 2012 01:29 AM, Rob Clark wrote:
>> This patch changes the omapdrm KMS to bypass the omapdss "compat"
>> layer and use the core omapdss API directly.  This solves some layering
>> issues that would cause unpin confusion vs GO bit status, because we
>> would not know whether a particular pageflip or overlay update has hit
>> the screen or not.  Now instead we explicitly manage the GO bits in
>> dispc and handle the vblank/framedone interrupts ourself so that we
>> always know which buffers are being scanned out at any given time, and
>> so on.
>>
>> As an added bonus, we no longer leave the last overlay buffer pinned
>> when the display is disabled, and have been able to add the previously
>> missing vblank event handling.
Very welcome patch; looks good to me.

Please feel free to add:
Reviewed-by: Sumit Semwal <sumit.semwal at ti.com>
>
> Looks good to me.
>
> Reviewed-by: Archit Taneja <archit at ti.com>
>
>>
>> v1: original
>> v2: rebased on latest staging-next and omapdss patches from Tomi and
>>      review comments from Archit Taneja
>>
>> Signed-off-by: Rob Clark <robdclark at gmail.com>
>> ---
>>   drivers/staging/omapdrm/Makefile         |   1 +
>>   drivers/staging/omapdrm/TODO             |   3 -
>>   drivers/staging/omapdrm/omap_connector.c | 111 +------
>>   drivers/staging/omapdrm/omap_crtc.c      | 507
>> +++++++++++++++++++++++++++----
>>   drivers/staging/omapdrm/omap_drv.c       | 439
>> +++++---------------------
>>   drivers/staging/omapdrm/omap_drv.h       | 140 +++++++--
>>   drivers/staging/omapdrm/omap_encoder.c   | 132 ++++----
>>   drivers/staging/omapdrm/omap_irq.c       | 322 ++++++++++++++++++++
>>   drivers/staging/omapdrm/omap_plane.c     | 452
>> +++++++++++----------------
>>   9 files changed, 1214 insertions(+), 893 deletions(-)
>>   create mode 100644 drivers/staging/omapdrm/omap_irq.c
>>
>> 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;
>>
>>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>



More information about the dri-devel mailing list