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 >