From 2a77172b41701302fbe820c58ec73844f2c962d9 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 15 Feb 2012 15:02:38 +0200 Subject: [PATCH] gfx: overlay: Synchronize overlay updates with CRTC page flips MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Tie the overlay updates to the CRTC page flips. Whenever the overlay is updated, we simply "arm" the overlay, and when the next page flip happens on the associated CRTC, we push the page flip and the pending overlay updates to the hardware simultaneosly. This will guarantee that the page flip and overlay updates happen on the same vblank, and are thus perfectly synchronized with each other. Issue: ANDROID-1574 Signed-off-by: Ville Syrjälä Reviewed-by: Imre Deak Signed-off-by: Kirill A. Shutemov --- drivers/staging/mrst/drv/mdfld_overlay.c | 257 ++++++++++++++++++++++++++++++- drivers/staging/mrst/drv/psb_intel_drv.h | 2 + drivers/staging/mrst/drv/psb_page_flip.c | 35 +++++ 3 files changed, 289 insertions(+), 5 deletions(-) diff --git a/drivers/staging/mrst/drv/mdfld_overlay.c b/drivers/staging/mrst/drv/mdfld_overlay.c index 0005729..572b9fe 100644 --- a/drivers/staging/mrst/drv/mdfld_overlay.c +++ b/drivers/staging/mrst/drv/mdfld_overlay.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Intel Corporation + * Copyright (C) 2011-2012 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -19,6 +19,9 @@ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. + * + * Authors: + * Ville Syrjälä */ #include @@ -32,9 +35,12 @@ #include "psb_drm.h" #include "psb_gtt.h" #include "psb_fb.h" +#include "psb_pvr_glue.h" #include "fp_trig.h" +#include "drm_flip.h" + enum { OVL_DIRTY_REGS = 0x1, OVL_DIRTY_COEFS = 0x2, @@ -133,6 +139,12 @@ struct mfld_overlay { #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_dentry; #endif + + struct drm_flip_helper flip_helper; + bool atomic; + PVRSRV_KERNEL_MEM_INFO *mem_info; + PVRSRV_KERNEL_MEM_INFO *old_mem_info; + u32 tgid; }; #define to_mfld_overlay(plane) container_of(plane, struct mfld_overlay, base) @@ -242,6 +254,10 @@ static void write_ovadd(struct mfld_overlay *ovl) ovadd |= OVL_OVADD_COEF; OVL_REG_WRITE(ovl, OVL_OVADD, ovadd); + + if (ovl->atomic) + return; + OVL_REG_READ(ovl, OVL_OVADD); /* @@ -283,14 +299,16 @@ static int ovl_wait(struct mfld_overlay *ovl) return 0; } -static void ovl_update_regs(struct mfld_overlay *ovl) +static void ovl_update_regs(struct mfld_overlay *ovl, + const struct mfld_overlay_regs *regs, + int pipe) { struct mfld_overlay_regs_page *page = &ovl->page[!ovl->curr_page]; static const u8 pipe_map[] = { 0, 2, 1, }; - page->ovadd = page->gtt | (pipe_map[ovl->pipe] << 6); + page->ovadd = page->gtt | (pipe_map[pipe] << 6); - memcpy(page->virt, &ovl->regs, sizeof ovl->regs); + memcpy(page->virt, regs, sizeof *regs); /* * Guarantee ordering between shadow register memory and write to OVADD. @@ -306,13 +324,20 @@ static void ovl_commit(struct mfld_overlay *ovl, unsigned int dirty) if (!dirty) return; + if (ovl->atomic) { + spin_lock_irq(&ovl->regs_lock); + ovl->dirty |= dirty; + spin_unlock_irq(&ovl->regs_lock); + return; + } + /* * Can be done safely outside the spinlock since we know * curr_page will only be modified just below, and ovl_commit() * won't be called from two threads simultaneosly due to locking * in upper layers. */ - ovl_update_regs(ovl); + ovl_update_regs(ovl, &ovl->regs, ovl->pipe); spin_lock_irq(&ovl->regs_lock); @@ -327,6 +352,7 @@ static void ovl_commit(struct mfld_overlay *ovl, unsigned int dirty) spin_lock_irq(&ovl->regs_lock); + /* FIXME cook up something like this when using atomic flips. */ if (ovl->dirty & OVL_DIRTY_COEFS && !(dirty & OVL_DIRTY_COEFS)) { /* No pending updates, coefficients have been loaded for sure. */ if (OVL_REG_READ(ovl, OVL_DOVSTA) & OVL_DOVSTA_OVR_UPDT) @@ -808,6 +834,51 @@ static void ovl_disable(struct mfld_overlay *ovl) ovl_commit(ovl, OVL_DIRTY_REGS); } +static int ref_fbs(struct drm_plane *plane, + struct drm_framebuffer *fb) +{ + struct drm_device *dev = plane->dev; + struct mfld_overlay *ovl = to_mfld_overlay(plane); + PVRSRV_KERNEL_MEM_INFO *mem_info = NULL; + PVRSRV_KERNEL_MEM_INFO *old_mem_info = NULL; + int r; + u32 tgid = psb_get_tgid(); + + /* Skip ref counting w/o atomic flips, for now. */ + if (!ovl->atomic) + goto skip; + + if (fb) + mem_info = to_psb_fb(fb)->pvrBO; + + r = psb_fb_gtt_ref(dev, mem_info); + if (r) + return r; + + if (plane->fb && plane->fb != fb) { + old_mem_info = to_psb_fb(plane->fb)->pvrBO; + + /* + * Need to keep a reference to the old fb so + * that we can manipulate the reads ops + * counters during page flip. + */ + psb_fb_ref(old_mem_info); + } + + skip: + /* Release old references */ + psb_fb_gtt_unref(dev, ovl->mem_info, ovl->tgid); + ovl->mem_info = mem_info; + + psb_fb_unref(ovl->old_mem_info); + ovl->old_mem_info = old_mem_info; + + ovl->tgid = tgid; + + return 0; +} + static int mfld_overlay_update_plane(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, @@ -837,6 +908,10 @@ mfld_overlay_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, struct if (r) return r; + r = ref_fbs(plane, fb); + if (r) + return r; + ovl_calc_scale_factors(&c); visible = drm_region_clip_scaled(&c.src, &c.dst, &c.clip, @@ -1001,6 +1076,11 @@ static int mfld_overlay_disable_plane(struct drm_plane *plane) { struct mfld_overlay *ovl = to_mfld_overlay(plane); + int r; + + r = ref_fbs(plane, NULL); + if (r) + return r; ovl_disable(ovl); @@ -1505,6 +1585,110 @@ static void ovl_debugfs_init(struct drm_device *dev, struct mfld_overlay *ovl) { static void ovl_debugfs_fini(struct mfld_overlay *ovl) {} #endif +struct mfld_overlay_flip { + struct drm_flip base; + struct drm_plane *plane; + PVRSRV_KERNEL_MEM_INFO *mem_info; + PVRSRV_KERNEL_MEM_INFO *old_mem_info; + int pipe; + u32 tgid; + struct mfld_overlay_regs regs; + bool vblank_ref; +}; + +static void ovl_prepare(struct drm_flip *flip) +{ + struct mfld_overlay_flip *oflip = + container_of(flip, struct mfld_overlay_flip, base); + struct drm_plane *plane = oflip->plane; + struct mfld_overlay *ovl = to_mfld_overlay(plane); + + ovl_update_regs(ovl, &oflip->regs, oflip->pipe); +} + +static bool ovl_flip(struct drm_flip *flip, + struct drm_flip *pending_flip) +{ + struct mfld_overlay_flip *oflip = + container_of(flip, struct mfld_overlay_flip, base); + struct drm_plane *plane = oflip->plane; + struct mfld_overlay *ovl = to_mfld_overlay(plane); + struct drm_device *dev = plane->dev; + u32 dovsta; + unsigned long flags; + + oflip->vblank_ref = drm_vblank_get(dev, oflip->pipe) == 0; + + spin_lock_irqsave(&ovl->regs_lock, flags); + + ovl->curr_page = !ovl->curr_page; + + dovsta = OVL_REG_READ(ovl, OVL_DOVSTA); + + write_ovadd(ovl); + + spin_unlock_irqrestore(&ovl->regs_lock, flags); + + if (pending_flip) + return (dovsta & OVL_DOVSTA_OVR_UPDT) != 0; + + return false; +} + +static bool ovl_flip_vblank(struct drm_flip *pending_flip) +{ + struct mfld_overlay *ovl = + container_of(pending_flip->helper, struct mfld_overlay, flip_helper); + struct drm_plane *plane = &ovl->base; + struct drm_device *dev = plane->dev; + + return (OVL_REG_READ(ovl, OVL_DOVSTA) & OVL_DOVSTA_OVR_UPDT) != 0; +} + +static void ovl_flip_complete(struct drm_flip *pending_flip) +{ + struct mfld_overlay_flip *oflip = + container_of(pending_flip, struct mfld_overlay_flip, base); + struct drm_device *dev = oflip->plane->dev; + int pipe = oflip->pipe; + + if (oflip->vblank_ref) + drm_vblank_put(dev, pipe); + + psb_fb_increase_read_ops_completed(oflip->old_mem_info); + PVRSRVScheduleDeviceCallbacks(); +} + +static void ovl_flip_finish(struct drm_flip *pending_flip) +{ + struct mfld_overlay_flip *oflip = + container_of(pending_flip, struct mfld_overlay_flip, base); + + psb_fb_unref(oflip->old_mem_info); +} + +static void ovl_flip_cleanup(struct drm_flip *pending_flip) +{ + struct mfld_overlay_flip *oflip = + container_of(pending_flip, struct mfld_overlay_flip, base); + struct drm_device *dev = oflip->plane->dev; + + mutex_lock(&dev->mode_config.mutex); + psb_fb_gtt_unref(dev, oflip->mem_info, oflip->tgid); + mutex_unlock(&dev->mode_config.mutex); + + kfree(oflip); +} + +static const struct drm_flip_helper_funcs ovl_flip_funcs = { + .prepare = ovl_prepare, + .flip = ovl_flip, + .vblank = ovl_flip_vblank, + .complete = ovl_flip_complete, + .finish = ovl_flip_finish, + .cleanup = ovl_flip_cleanup, +}; + int mdfld_overlay_init(struct drm_device *dev, int id) { struct drm_psb_private *dev_priv = dev->dev_private; @@ -1562,6 +1746,9 @@ int mdfld_overlay_init(struct drm_device *dev, int id) ovl_set_src_key(ovl, &ovl->base.opts); ovl_set_dst_key(ovl, &ovl->base.opts); + drm_flip_helper_init(&ovl->flip_helper, &dev_priv->flip_driver, &ovl_flip_funcs); + ovl->atomic = true; + drm_plane_init(dev, &ovl->base, possible_crtcs, &mfld_overlay_funcs, mfld_overlay_formats, ARRAY_SIZE(mfld_overlay_formats)); @@ -1582,6 +1769,8 @@ static void mfld_overlay_destroy(struct drm_plane *plane) struct mfld_overlay *ovl = to_mfld_overlay(plane); struct drm_device *dev = ovl->dev; + drm_flip_helper_fini(&ovl->flip_helper); + ovl_debugfs_fini(ovl); ovl_wait(ovl); @@ -1620,3 +1809,61 @@ void mdfld_overlay_resume(struct drm_plane *plane) spin_unlock_irqrestore(&ovl->regs_lock, flags); } + +void mdfld_overlay_process_vblank(struct drm_plane *plane, int pipe) +{ + struct mfld_overlay *ovl = to_mfld_overlay(plane); + + if (ovl->pipe != pipe) + return; + + drm_flip_helper_vblank(&ovl->flip_helper); +} + +struct drm_flip *mdfld_overlay_atomic_flip(struct drm_plane *plane, int pipe) +{ + struct mfld_overlay *ovl = to_mfld_overlay(plane); + struct mfld_overlay_flip *oflip; + + if (ovl->pipe != pipe || !ovl->atomic) + return NULL; + + spin_lock_irq(&ovl->regs_lock); + + if (!(ovl->dirty & OVL_DIRTY_REGS)) { + spin_unlock_irq(&ovl->regs_lock); + return NULL; + } + + ovl->dirty &= ~OVL_DIRTY_REGS; + + spin_unlock_irq(&ovl->regs_lock); + + oflip = kmalloc(sizeof *oflip, GFP_KERNEL); + if (!oflip) + return NULL; + + drm_flip_init(&oflip->base, &ovl->flip_helper); + + oflip->mem_info = ovl->mem_info; + /* reference ownership moved to the flip */ + ovl->mem_info = NULL; + + oflip->old_mem_info = ovl->old_mem_info; + /* reference ownership moved to the flip */ + ovl->old_mem_info = NULL; + + oflip->tgid = ovl->tgid; + + oflip->vblank_ref = false; + + psb_fb_increase_read_ops_pending(oflip->old_mem_info); + + /* we must pipeline the register changes */ + memcpy(&oflip->regs, &ovl->regs, sizeof ovl->regs); + + oflip->plane = plane; + oflip->pipe = pipe; + + return &oflip->base; +} diff --git a/drivers/staging/mrst/drv/psb_intel_drv.h b/drivers/staging/mrst/drv/psb_intel_drv.h index ec96a74..306fc67 100644 --- a/drivers/staging/mrst/drv/psb_intel_drv.h +++ b/drivers/staging/mrst/drv/psb_intel_drv.h @@ -255,5 +255,7 @@ extern void mdfldWaitForPipeEnable(struct drm_device *dev, int pipe); extern int mdfld_overlay_init(struct drm_device *dev, int id); extern void mdfld_overlay_suspend(struct drm_plane *plane); extern void mdfld_overlay_resume(struct drm_plane *plane); +extern struct drm_flip *mdfld_overlay_atomic_flip(struct drm_plane *plane, int pipe); +extern void mdfld_overlay_process_vblank(struct drm_plane *plane, int pipe); #endif /* __INTEL_DRV_H__ */ diff --git a/drivers/staging/mrst/drv/psb_page_flip.c b/drivers/staging/mrst/drv/psb_page_flip.c index 48af835..3ed6f41 100644 --- a/drivers/staging/mrst/drv/psb_page_flip.c +++ b/drivers/staging/mrst/drv/psb_page_flip.c @@ -52,6 +52,7 @@ struct pending_flip { struct pvr_pending_sync pending_sync; struct drm_flip base; u32 vbl_count; + struct list_head companions; u32 tgid; bool vblank_ref; }; @@ -348,11 +349,21 @@ void psb_page_flip_crtc_fini(struct psb_intel_crtc *psb_intel_crtc) void psb_intel_crtc_process_vblank(struct drm_crtc *crtc) { + struct drm_psb_private *dev_priv = crtc->dev->dev_private; struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + int pipe = psb_intel_crtc->pipe; + int i; psb_intel_crtc->vbl_received = true; wake_up(&psb_intel_crtc->vbl_wait); + for (i = 0; i < ARRAY_SIZE(dev_priv->overlays); i++) { + if (!dev_priv->overlays[i]) + continue; + + mdfld_overlay_process_vblank(dev_priv->overlays[i], pipe); + } + drm_flip_helper_vblank(&psb_intel_crtc->flip_helper); } @@ -364,11 +375,17 @@ sync_callback(struct pvr_pending_sync *pending_sync, bool from_misr) struct drm_crtc* crtc = pending_flip->crtc; struct drm_device *dev = crtc->dev; struct drm_psb_private *dev_priv = dev->dev_private; + struct drm_flip *flip, *next; LIST_HEAD(flips); bool pipe_enabled = false; list_add_tail(&pending_flip->base.list, &flips); + list_for_each_entry_safe(flip, next, &pending_flip->companions, list) + list_move_tail(&flip->list, &flips); + + WARN_ON(!list_empty(&pending_flip->companions)); + /* prevent DPMS and whatnot from shooting us in the foot */ if (from_misr) mutex_lock(&dev->mode_config.mutex); @@ -420,6 +437,7 @@ psb_intel_crtc_page_flip(struct drm_crtc *crtc, struct drm_pending_vblank_event *event) { struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; struct psb_framebuffer *psbfb = to_psb_fb(fb); PVRSRV_KERNEL_MEM_INFO *current_fb_mem_info; struct pending_flip *new_pending_flip; @@ -429,6 +447,8 @@ psb_intel_crtc_page_flip(struct drm_crtc *crtc, struct pvr_trcmd_flpreq *fltrace; u32 tgid = psb_get_tgid(); int ret; + int pipe = to_psb_intel_crtc(crtc)->pipe; + int i; if (!psbfb->pvrBO) return -EINVAL; @@ -491,6 +511,21 @@ psb_intel_crtc_page_flip(struct drm_crtc *crtc, new_pending_flip->tgid = tgid; + INIT_LIST_HEAD(&new_pending_flip->companions); + + for (i = 0; i < ARRAY_SIZE(dev_priv->overlays); i++) { + struct drm_flip *flip; + + if (!dev_priv->overlays[i]) + continue; + + flip = mdfld_overlay_atomic_flip(dev_priv->overlays[i], pipe); + if (!flip) + continue; + + list_add_tail(&flip->list, &new_pending_flip->companions); + } + PVRSRVCallbackOnSync(psbfb->pvrBO->psKernelSyncInfo, PVRSRV_SYNC_WRITE, sync_callback, &new_pending_flip->pending_sync); -- 2.7.4