static void omap_crtc_destroy(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); omap_crtc->plane->funcs->destroy(omap_crtc->plane); drm_crtc_cleanup(crtc); kfree(omap_crtc); }
static void page_flip_cb(void *arg) { 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; struct timeval now; unsigned long flags; WARN_ON(!event); omap_crtc->event = NULL; update_scanout(crtc); WARN_ON(commit(crtc)); /* wakeup userspace */ /* TODO: this should happen *after* flip in vsync IRQ handler */ if (event) { spin_lock_irqsave(&dev->event_lock, flags); event->event.sequence = drm_vblank_count_and_time( dev, omap_crtc->id, &now); event->event.tv_sec = now.tv_sec; event->event.tv_usec = now.tv_usec; list_add_tail(&event->base.link, &event->base.file_priv->event_list); wake_up_interruptible(&event->base.file_priv->event_wait); spin_unlock_irqrestore(&dev->event_lock, flags); } }
static void omap_crtc_destroy(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); DBG("%s", omap_crtc->ovl->name); drm_crtc_cleanup(crtc); kfree(omap_crtc); }
static void vblank_cb(void *arg) { static uint32_t sequence = 0; 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); omap_crtc->event = NULL; if (event) { do_gettimeofday(&now); spin_lock_irqsave(&dev->event_lock, flags); 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); } }
static bool omap_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); DBG("%s", omap_crtc->ovl->name); return true; }
static void omap_crtc_disable(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); DBG("%s", omap_crtc->name); drm_crtc_vblank_off(crtc); }
int omap_crtc_wait_pending(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); return wait_event_timeout(omap_crtc->pending_wait, !omap_crtc->pending, msecs_to_jiffies(50)); }
/* Called only from the encoder enable/disable and suspend/resume handlers. */ static void omap_crtc_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; u32 framedone_irq, vsync_irq; int ret; if (dispc_mgr_is_enabled(channel) == enable) return; if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) { /* * Digit output produces some sync lost interrupts during the * first frame when enabling, so we need to ignore those. */ omap_crtc->ignore_digit_sync_lost = true; } framedone_irq = dispc_mgr_get_framedone_irq(channel); vsync_irq = dispc_mgr_get_vsync_irq(channel); if (enable) { wait = omap_irq_wait_init(dev, vsync_irq, 1); } else { /* * When we disable the digit output, we need to wait for * FRAMEDONE to know that DISPC has finished with the output. * * OMAP2/3 does not have FRAMEDONE irq for digit output, and in * that case we need to use vsync interrupt, and wait for both * even and odd frames. */ if (framedone_irq) wait = omap_irq_wait_init(dev, framedone_irq, 1); else wait = omap_irq_wait_init(dev, vsync_irq, 2); } dispc_mgr_enable(channel, enable); 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"); } if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) { omap_crtc->ignore_digit_sync_lost = false; /* make sure the irq handler sees the value above */ mb(); } }
static void omap_crtc_prepare(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct omap_overlay *ovl = omap_crtc->ovl; DBG("%s", omap_crtc->ovl->name); ovl->get_overlay_info(ovl, &omap_crtc->info); omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); }
static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); DBG("%s %d,%d: fb=%p", omap_crtc->ovl->name, x, y, old_fb); update_scanout(crtc); return commit(crtc); }
static int omap_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct drm_plane *plane = omap_crtc->plane; struct drm_display_mode *mode = &crtc->mode; return plane->funcs->update_plane(plane, crtc, crtc->fb, 0, 0, mode->hdisplay, mode->vdisplay, x << 16, y << 16, mode->hdisplay << 16, mode->vdisplay << 16); }
static void page_flip_cb(void *arg) { struct drm_crtc *crtc = arg; struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct drm_framebuffer *old_fb = omap_crtc->old_fb; omap_crtc->old_fb = NULL; omap_crtc_mode_set_base(crtc, crtc->x, crtc->y, old_fb); omap_plane_on_endwin(omap_crtc->plane, vblank_cb, crtc); }
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->vblank_irq.registered); omap_irq_unregister(crtc->dev, &omap_crtc->error_irq); drm_crtc_cleanup(crtc); kfree(omap_crtc); }
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); int i; WARN_ON(omap_plane_dpms(omap_crtc->plane, mode)); 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)); } }
static int omap_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, int x, int y, 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, 0, 0, mode->hdisplay, mode->vdisplay, x << 16, y << 16, mode->hdisplay << 16, mode->vdisplay << 16); }
static void omap_crtc_mode_set_nofb(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct drm_display_mode *mode = &crtc->state->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); }
static void omap_crtc_dpms(struct drm_crtc *crtc, int mode) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); DBG("%s: %d", omap_crtc->ovl->name, mode); if (mode == DRM_MODE_DPMS_ON) { update_scanout(crtc); omap_crtc->info.enabled = true; } else { omap_crtc->info.enabled = false; } WARN_ON(commit(crtc)); }
static void omap_crtc_enable(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); DBG("%s", omap_crtc->name); rmb(); WARN_ON(omap_crtc->pending); omap_crtc->pending = true; wmb(); omap_irq_register(crtc->dev, &omap_crtc->vblank_irq); drm_crtc_vblank_on(crtc); }
/* update parameters that are dependent on the framebuffer dimensions and * position within the fb that this crtc scans out from. This is called * when framebuffer dimensions or x,y base may have changed, either due * to our mode, or a change in another crtc that is scanning out of the * same fb. */ static void update_scanout(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); dma_addr_t paddr; unsigned int screen_width; omap_framebuffer_get_buffer(crtc->fb, crtc->x, crtc->y, NULL, &paddr, &screen_width); DBG("%s: %d,%d: %08x (%d)", omap_crtc->ovl->name, crtc->x, crtc->y, (u32)paddr, screen_width); omap_crtc->info.paddr = paddr; omap_crtc->info.screen_width = screen_width; }
static void page_flip_cb(void *arg) { struct drm_crtc *crtc = arg; struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct drm_framebuffer *old_fb = omap_crtc->old_fb; 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); }
/* push changes down to dss2 */ static int commit(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct omap_overlay *ovl = omap_crtc->ovl; struct omap_overlay_info *info = &omap_crtc->info; int ret; DBG("%s", omap_crtc->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", info->pos_x, info->pos_y, info->paddr); /* 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; } /* 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; } } if (info->enabled) { omap_framebuffer_flush(crtc->fb, crtc->x, crtc->y, crtc->fb->width, crtc->fb->height); } return 0; }
static void omap_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); WARN_ON(omap_crtc->vblank_irq.registered); if (dispc_mgr_is_enabled(omap_crtc->channel)) { DBG("%s: GO", omap_crtc->name); rmb(); WARN_ON(omap_crtc->pending); omap_crtc->pending = true; wmb(); dispc_mgr_go(omap_crtc->channel); omap_irq_register(crtc->dev, &omap_crtc->vblank_irq); } }
static int omap_crtc_page_flip_locked(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event) { struct drm_device *dev = crtc->dev; struct omap_crtc *omap_crtc = to_omap_crtc(crtc); DBG("%d -> %d", crtc->fb ? crtc->fb->base.id : -1, fb->base.id); if (omap_crtc->event) { dev_err(dev->dev, "already a pending flip\n"); return -EINVAL; } crtc->fb = fb; omap_crtc->event = event; omap_gem_op_async(omap_framebuffer_bo(fb), OMAP_GEM_READ, page_flip_cb, crtc); return 0; }
static int omap_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, int x, int y, struct drm_framebuffer *old_fb) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); DBG("%s: %d,%d: %dx%d", omap_crtc->ovl->name, x, y, mode->hdisplay, mode->vdisplay); /* just use adjusted mode */ mode = adjusted_mode; omap_crtc->info.width = mode->hdisplay; omap_crtc->info.height = mode->vdisplay; omap_crtc->info.out_width = mode->hdisplay; omap_crtc->info.out_height = mode->vdisplay; omap_crtc->info.color_mode = OMAP_DSS_COLOR_RGB24U; omap_crtc->info.rotation_type = OMAP_DSS_ROT_DMA; omap_crtc->info.rotation = OMAP_DSS_ROT_0; omap_crtc->info.global_alpha = 0xff; omap_crtc->info.mirror = 0; omap_crtc->info.mirror = 0; omap_crtc->info.pos_x = 0; omap_crtc->info.pos_y = 0; #if 0 /* re-enable when these are available in DSS2 driver */ omap_crtc->info.zorder = 3; /* GUI in the front, video behind */ omap_crtc->info.min_x_decim = 1; omap_crtc->info.max_x_decim = 1; omap_crtc->info.min_y_decim = 1; omap_crtc->info.max_y_decim = 1; #endif update_scanout(crtc); return 0; }
static void vblank_cb(void *arg) { static uint32_t sequence = 0; 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); omap_crtc->event = 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); } }
static void omap_crtc_commit(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); DBG("%s", omap_crtc->name); omap_crtc_dpms(crtc, DRM_MODE_DPMS_ON); }
static void omap_crtc_prepare(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); DBG("%s", omap_crtc->name); omap_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); }
struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); return &omap_crtc->timings; }
uint32_t pipe2vbl(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); return dispc_mgr_get_vsync_irq(omap_crtc->channel); }
enum omap_channel omap_crtc_channel(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); return omap_crtc->channel; }