diff mbox series

[RFC,1/1] drm/gma500: add atomic support

Message ID 20190606202032.29748-1-james.hilliard1@gmail.com (mailing list archive)
State New, archived
Headers show
Series [RFC,1/1] drm/gma500: add atomic support | expand

Commit Message

James Hilliard June 6, 2019, 8:20 p.m. UTC
Imported from https://git.tizen.org/cgit/kernel/kernel-mfld-blackbay/

This is currently not functional and based off of an older version of
the gma500 driver. From the commit log this was written by Intel, is
anyone aware of a more up to date version?

I'm looking to see if this would be a decent starting point for adding
gma500 atomic support to mainline and how best to approach that.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/gpu/drm/gma500/Makefile        |   1 +
 drivers/gpu/drm/gma500/psb_page_flip.c | 600 +++++++++++++++++++++++++
 drivers/gpu/drm/gma500/psb_page_flip.h |  38 ++
 3 files changed, 639 insertions(+)
 create mode 100644 drivers/gpu/drm/gma500/psb_page_flip.c
 create mode 100644 drivers/gpu/drm/gma500/psb_page_flip.h

Comments

Sam Ravnborg June 6, 2019, 8:31 p.m. UTC | #1
Hi James.

I have no clue on the origin of this patch.
But just to mke sure we do not hit the tree with some trivial issues
here goes...

	Sam

On Thu, Jun 06, 2019 at 02:20:32PM -0600, James Hilliard wrote:
> Imported from https://git.tizen.org/cgit/kernel/kernel-mfld-blackbay/
> 
> This is currently not functional and based off of an older version of
> the gma500 driver. From the commit log this was written by Intel, is
> anyone aware of a more up to date version?
> 
> I'm looking to see if this would be a decent starting point for adding
> gma500 atomic support to mainline and how best to approach that.
> 
> Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
> ---
>  drivers/gpu/drm/gma500/Makefile        |   1 +
>  drivers/gpu/drm/gma500/psb_page_flip.c | 600 +++++++++++++++++++++++++
>  drivers/gpu/drm/gma500/psb_page_flip.h |  38 ++
>  3 files changed, 639 insertions(+)
>  create mode 100644 drivers/gpu/drm/gma500/psb_page_flip.c
>  create mode 100644 drivers/gpu/drm/gma500/psb_page_flip.h
> 
> diff --git a/drivers/gpu/drm/gma500/Makefile b/drivers/gpu/drm/gma500/Makefile
> index c8f2c89be99d..3db535ef51cb 100644
> --- a/drivers/gpu/drm/gma500/Makefile
> +++ b/drivers/gpu/drm/gma500/Makefile
> @@ -24,6 +24,7 @@ gma500_gfx-y += \
>  	  psb_intel_sdvo.o \
>  	  psb_lid.o \
>  	  psb_irq.o \
> +	  psb_page_flip.o \
>  	  psb_device.o \
>  	  mid_bios.o
>  
> diff --git a/drivers/gpu/drm/gma500/psb_page_flip.c b/drivers/gpu/drm/gma500/psb_page_flip.c
> new file mode 100644
> index 000000000000..6a93727cf1c1
> --- /dev/null
> +++ b/drivers/gpu/drm/gma500/psb_page_flip.c
> @@ -0,0 +1,600 @@
> +/*
> + * Copyright (c) 2011-2012, Intel Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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, write to the Free Software Foundation, Inc.,·
> + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
For new files SPDX is preferred.

> + *
> + * Authors:
> + * Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
> + * Pauli Nieminen <pauli.nieminen@intel.com>
> + * Ville Syrjälä <ville.syrjala@linux.intel.com>
> + */
> +
> +#include <linux/spinlock.h>
> +#include <linux/list.h>
> +
> +#include <drm/drmP.h>
> +#include "psb_drv.h"
> +#include "framebuffer.h"
> +#include "psb_intel_reg.h"
> +#include "psb_page_flip.h"
> +
> +#include "mdfld_output.h"
> +#include "mdfld_dsi_output.h"
Dorp drmP.h - it is deprecated and no longer used by gma500 (since a week
ago or so)
Sort include files within their repsective blocks.

	Sam
Daniel Vetter June 6, 2019, 9:13 p.m. UTC | #2
On Thu, Jun 6, 2019 at 10:20 PM James Hilliard
<james.hilliard1@gmail.com> wrote:
>
> Imported from https://git.tizen.org/cgit/kernel/kernel-mfld-blackbay/
>
> This is currently not functional and based off of an older version of
> the gma500 driver. From the commit log this was written by Intel, is
> anyone aware of a more up to date version?
>
> I'm looking to see if this would be a decent starting point for adding
> gma500 atomic support to mainline and how best to approach that.
>
> Signed-off-by: James Hilliard <james.hilliard1@gmail.com>

This uses some drm_flip_driver infrastructure, which might have
floated around in some atomic patches once, but definitely never made
it to upstream. No idea whether that will help you or not, since the
upstream infrastructure for all this looks quite a bit different.
-Daniel

> ---
>  drivers/gpu/drm/gma500/Makefile        |   1 +
>  drivers/gpu/drm/gma500/psb_page_flip.c | 600 +++++++++++++++++++++++++
>  drivers/gpu/drm/gma500/psb_page_flip.h |  38 ++
>  3 files changed, 639 insertions(+)
>  create mode 100644 drivers/gpu/drm/gma500/psb_page_flip.c
>  create mode 100644 drivers/gpu/drm/gma500/psb_page_flip.h
>
> diff --git a/drivers/gpu/drm/gma500/Makefile b/drivers/gpu/drm/gma500/Makefile
> index c8f2c89be99d..3db535ef51cb 100644
> --- a/drivers/gpu/drm/gma500/Makefile
> +++ b/drivers/gpu/drm/gma500/Makefile
> @@ -24,6 +24,7 @@ gma500_gfx-y += \
>           psb_intel_sdvo.o \
>           psb_lid.o \
>           psb_irq.o \
> +         psb_page_flip.o \
>           psb_device.o \
>           mid_bios.o
>
> diff --git a/drivers/gpu/drm/gma500/psb_page_flip.c b/drivers/gpu/drm/gma500/psb_page_flip.c
> new file mode 100644
> index 000000000000..6a93727cf1c1
> --- /dev/null
> +++ b/drivers/gpu/drm/gma500/psb_page_flip.c
> @@ -0,0 +1,600 @@
> +/*
> + * Copyright (c) 2011-2012, Intel Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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, write to the Free Software Foundation, Inc.,·
> + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + * Authors:
> + * Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
> + * Pauli Nieminen <pauli.nieminen@intel.com>
> + * Ville Syrjälä <ville.syrjala@linux.intel.com>
> + */
> +
> +#include <linux/spinlock.h>
> +#include <linux/list.h>
> +
> +#include <drm/drmP.h>
> +#include "psb_drv.h"
> +#include "framebuffer.h"
> +#include "psb_intel_reg.h"
> +#include "psb_page_flip.h"
> +
> +#include "mdfld_output.h"
> +#include "mdfld_dsi_output.h"
> +
> +#if defined(CONFIG_MDFLD_DUAL_DSI_DPU)
> +#include "mdfld_dsi_dbi_dpu.h"
> +#elif defined(CONFIG_MDFLD_DSI_DSR)
> +#include "mdfld_dsi_dbi.h"
> +#endif
> +
> +struct pending_flip {
> +       struct drm_crtc *crtc;
> +       struct drm_pending_vblank_event *event;
> +       PVRSRV_KERNEL_MEM_INFO *mem_info;
> +       PVRSRV_KERNEL_MEM_INFO *old_mem_info;
> +       uint32_t offset;
> +       struct list_head uncompleted;
> +       struct pvr_pending_sync pending_sync;
> +       struct drm_flip base;
> +       u32 vbl_count;
> +       struct list_head companions;
> +       u32 tgid;
> +       bool vblank_ref;
> +       atomic_t refcnt;
> +       struct psb_pending_values pending_values;
> +};
> +
> +enum {
> +       /* somwehat arbitrary value */
> +       PSB_VBL_CNT_TIMEOUT = 5,
> +};
> +
> +static u32 get_vbl_count(struct drm_crtc *crtc)
> +{
> +       struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc);
> +       struct drm_psb_private *dev_priv = crtc->dev->dev_private;
> +       int pipe = psb_intel_crtc->pipe;
> +       u32 high, low1, low2, dsl;
> +       unsigned int timeout = 0;
> +
> +       /* All reads must be satisfied during the same frame */
> +       do {
> +               void __iomem *reg_pixel;
> +               void __iomem *reg_high;
> +
> +               reg_pixel = dev_priv->vdc_reg + PSB_PIPEFRAMEPIXEL(pipe);
> +               reg_high = dev_priv->vdc_reg + PSB_PIPEFRAMEHIGH(pipe);
> +               low1 = ioread32(reg_pixel) >> PIPE_FRAME_LOW_SHIFT;
> +               high = ioread32(reg_high) << 8;
> +               dsl = ioread32(dev_priv->vdc_reg + PSB_PIPE_DSL(pipe));
> +               low2 = ioread32(reg_pixel) >> PIPE_FRAME_LOW_SHIFT;
> +       } while (low1 != low2 && timeout++ < PSB_VBL_CNT_TIMEOUT);
> +
> +       if (timeout >= PSB_VBL_CNT_TIMEOUT)
> +               dev_warn(crtc->dev->dev,
> +                        "Timed out while determining VBL count for pipe %d\n",
> +                        psb_intel_crtc->pipe);
> +
> +       /*
> +        * The frame counter seems to increment at the beginning of the
> +        * last scanline. The hardware performs the flip at the start
> +        * of the vblank, so we want to count those events instead.
> +        * Cook up a vblank counter from the frame counter and scanline
> +        * counter.
> +        */
> +       return ((high | low2) +
> +               ((dsl >= crtc->hwmode.crtc_vdisplay) &&
> +                (dsl < crtc->hwmode.crtc_vtotal - 1))) & 0xffffff;
> +}
> +
> +static unsigned int usecs_to_scanlines(struct drm_crtc *crtc,
> +                                      unsigned int usecs)
> +{
> +       /* paranoia */
> +       if (!crtc->hwmode.crtc_htotal)
> +               return 1;
> +
> +       return DIV_ROUND_UP(usecs * crtc->hwmode.clock,
> +                           1000 * crtc->hwmode.crtc_htotal);
> +}
> +
> +/* Called with interrupts off. */
> +static void avoid_danger_zone(struct drm_crtc *crtc)
> +{
> +       struct drm_device *dev = crtc->dev;
> +       struct drm_psb_private *dev_priv = dev->dev_private;
> +       struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc);
> +       int pipe = psb_intel_crtc->pipe;
> +       /*
> +        * With CRTC and one overlay we're usually spending 30-40 usecs
> +        * between avoid_danger_zone() and psb_flip_driver_flush(). Leave
> +        * a small safety margin and avoid flipping while under 60 usec
> +        * from the vblank start.
> +        */
> +       u32 min = crtc->hwmode.crtc_vdisplay - usecs_to_scanlines(crtc, 60);
> +       u32 max = crtc->hwmode.crtc_vdisplay - 1;
> +       long timeout = msecs_to_jiffies(1);
> +       u32 val;
> +
> +       drm_vblank_get(dev, pipe);
> +
> +       psb_intel_crtc->vbl_received = false;
> +       val = ioread32(dev_priv->vdc_reg + PSB_PIPE_DSL(pipe));
> +
> +       while (val >= min && val <= max && timeout > 0) {
> +               local_irq_enable();
> +
> +               timeout = wait_event_timeout(psb_intel_crtc->vbl_wait,
> +                                            psb_intel_crtc->vbl_received,
> +                                            timeout);
> +
> +               local_irq_disable();
> +
> +               psb_intel_crtc->vbl_received = false;
> +               val = ioread32(dev_priv->vdc_reg + PSB_PIPE_DSL(pipe));
> +       }
> +
> +       drm_vblank_put(dev, pipe);
> +
> +       if (val >= min && val <= max)
> +               dev_warn(dev->dev,
> +                        "Page flipping close to vblank start (DSL=%u, VBL=%u)\n",
> +                        val, crtc->hwmode.crtc_vdisplay);
> +}
> +
> +void
> +psb_cleanup_pending_events(struct drm_device *dev, struct psb_fpriv *priv)
> +{
> +       struct drm_pending_vblank_event *e;
> +       struct pending_flip *pending_flip, *temp;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&dev->event_lock, flags);
> +       list_for_each_entry_safe(pending_flip, temp, &priv->pending_flips,
> +                       uncompleted) {
> +               e = pending_flip->event;
> +               pending_flip->event = NULL;
> +               e->base.destroy(&e->base);
> +               list_del_init(&pending_flip->uncompleted);
> +       }
> +       spin_unlock_irqrestore(&dev->event_lock, flags);
> +}
> +
> +static void
> +send_page_flip_event(struct drm_device *dev, int pipe,
> +                    struct pending_flip *pending_flip)
> +{
> +       struct drm_pending_vblank_event *e;
> +       struct timeval now;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&dev->event_lock, flags);
> +
> +       if (!pending_flip->event)
> +               goto unlock;
> +
> +       list_del(&pending_flip->uncompleted);
> +       e = pending_flip->event;
> +       do_gettimeofday(&now);
> +       e->event.sequence = drm_vblank_count(dev, pipe);
> +       e->event.tv_sec = now.tv_sec;
> +       e->event.tv_usec = now.tv_usec;
> +       list_add_tail(&e->base.link,
> +                       &e->base.file_priv->event_list);
> +       wake_up_interruptible(&e->base.file_priv->event_wait);
> +
> +unlock:
> +       spin_unlock_irqrestore(&dev->event_lock, flags);
> +}
> +
> +static void free_flip(struct pending_flip *crtc_flip)
> +{
> +       if (atomic_dec_and_test(&crtc_flip->refcnt)) {
> +               psb_fb_unref(crtc_flip->old_mem_info, crtc_flip->tgid);
> +               kfree(crtc_flip);
> +       }
> +}
> +
> +static void crtc_flip_cleanup(struct drm_flip *flip)
> +{
> +       struct pending_flip *crtc_flip =
> +               container_of(flip, struct pending_flip, base);
> +       struct drm_device *dev = crtc_flip->crtc->dev;
> +
> +       mutex_lock(&dev->mode_config.mutex);
> +       psb_fb_gtt_unref(dev, crtc_flip->mem_info, crtc_flip->tgid);
> +       mutex_unlock(&dev->mode_config.mutex);
> +
> +       free_flip(crtc_flip);
> +}
> +
> +static void crtc_flip_finish(struct drm_flip *flip)
> +{
> +}
> +
> +static void psb_flip_driver_flush(struct drm_flip_driver *driver)
> +{
> +       struct drm_psb_private *dev_priv =
> +               container_of(driver, struct drm_psb_private, flip_driver);
> +
> +       /* Flush posted writes */
> +       (void)ioread32(dev_priv->vdc_reg + PSB_PIPESTAT(PSB_PIPE_A));
> +}
> +
> +static void psb_flip_complete_sync_callback(struct pvr_pending_sync *sync,
> +               bool call_from_work)
> +{
> +       struct pending_flip *crtc_flip =
> +               container_of(sync, struct pending_flip, pending_sync);
> +
> +       if (psb_fb_increase_read_ops_completed(crtc_flip->old_mem_info,
> +                       &crtc_flip->pending_values, sync)) {
> +               WARN(true, "Sync callback called without completing operation");
> +               return;
> +       }
> +
> +       free_flip(crtc_flip);
> +
> +       if (call_from_work)
> +               mutex_lock(&gPVRSRVLock);
> +
> +       PVRSRVScheduleDeviceCallbacks();
> +
> +       if (call_from_work)
> +               mutex_unlock(&gPVRSRVLock);
> +}
> +
> +static void crtc_flip_complete(struct drm_flip *flip)
> +{
> +       struct pending_flip *crtc_flip =
> +               container_of(flip, struct pending_flip, base);
> +       struct drm_device *dev = crtc_flip->crtc->dev;
> +       int pipe = to_psb_intel_crtc(crtc_flip->crtc)->pipe;
> +
> +       send_page_flip_event(dev, pipe, crtc_flip);
> +
> +       if (crtc_flip->vblank_ref)
> +               drm_vblank_put(dev, pipe);
> +
> +       atomic_inc(&crtc_flip->refcnt);
> +       crtc_flip->pending_sync.callback = psb_flip_complete_sync_callback;
> +       if (psb_fb_increase_read_ops_completed(crtc_flip->old_mem_info,
> +                               &crtc_flip->pending_values,
> +                               &crtc_flip->pending_sync))
> +               return;
> +
> +       free_flip(crtc_flip);
> +       PVRSRVScheduleDeviceCallbacks();
> +}
> +
> +static const struct drm_flip_driver_funcs psb_flip_driver_funcs = {
> +       .flush = psb_flip_driver_flush,
> +};
> +
> +void psb_page_flip_init(struct drm_device *dev)
> +{
> +       struct drm_psb_private *dev_priv = dev->dev_private;
> +
> +       drm_flip_driver_init(&dev_priv->flip_driver,
> +                            &psb_flip_driver_funcs);
> +}
> +
> +void psb_page_flip_fini(struct drm_device *dev)
> +{
> +       struct drm_psb_private *dev_priv = dev->dev_private;
> +
> +       drm_flip_driver_fini(&dev_priv->flip_driver);
> +}
> +
> +static bool vbl_count_after_eq(u32 a, u32 b)
> +{
> +       return !((a - b) & 0x800000);
> +}
> +
> +static bool crtc_check(struct drm_flip *pending_flip,
> +                      u32 vbl_count)
> +{
> +       struct pending_flip *crtc_flip =
> +               container_of(pending_flip, struct pending_flip, base);
> +
> +       return vbl_count_after_eq(vbl_count, crtc_flip->vbl_count);
> +}
> +
> +static bool crtc_flip(struct drm_flip *flip,
> +                     struct drm_flip *pending_flip)
> +{
> +       struct pending_flip *crtc_flip = container_of(flip, struct pending_flip, base);
> +       struct drm_crtc *crtc = crtc_flip->crtc;
> +       struct drm_device *dev = crtc->dev;
> +       struct drm_psb_private *dev_priv = dev->dev_private;
> +       int pipe = to_psb_intel_crtc(crtc)->pipe;
> +       u32 vbl_count;
> +
> +       crtc_flip->vblank_ref = drm_vblank_get(dev, pipe) == 0;
> +
> +       vbl_count = get_vbl_count(crtc);
> +
> +       iowrite32(crtc_flip->offset, dev_priv->vdc_reg + PSB_DSPSURF(pipe));
> +
> +       /* This flip will happen on the next vblank */
> +       crtc_flip->vbl_count = (vbl_count + 1) & 0xffffff;
> +
> +       if (pending_flip) {
> +               struct pending_flip *old_crtc_flip =
> +                       container_of(pending_flip, struct pending_flip, base);
> +               bool flipped = crtc_check(pending_flip, vbl_count);
> +
> +               if (!flipped) {
> +                       swap(crtc_flip->old_mem_info, old_crtc_flip->old_mem_info);
> +                       swap(crtc_flip->pending_values, old_crtc_flip->pending_values);
> +               }
> +
> +               return flipped;
> +       }
> +
> +       return false;
> +}
> +
> +static bool crtc_vblank(struct drm_flip *pending_flip)
> +{
> +       struct pending_flip *crtc_flip =
> +               container_of(pending_flip, struct pending_flip, base);
> +       u32 vbl_count = get_vbl_count(crtc_flip->crtc);
> +
> +       return crtc_check(pending_flip, vbl_count);
> +}
> +
> +static const struct drm_flip_helper_funcs crtc_flip_funcs = {
> +       .flip = crtc_flip,
> +       .vblank = crtc_vblank,
> +       .complete = crtc_flip_complete,
> +       .finish = crtc_flip_finish,
> +       .cleanup = crtc_flip_cleanup,
> +};
> +
> +void psb_page_flip_crtc_init(struct psb_intel_crtc *psb_intel_crtc)
> +{
> +       struct drm_device *dev = psb_intel_crtc->base.dev;
> +       struct drm_psb_private *dev_priv = dev->dev_private;
> +
> +       drm_flip_helper_init(&psb_intel_crtc->flip_helper,
> +                            &dev_priv->flip_driver,
> +                            &crtc_flip_funcs);
> +}
> +
> +void psb_page_flip_crtc_fini(struct psb_intel_crtc *psb_intel_crtc)
> +{
> +       drm_flip_helper_fini(&psb_intel_crtc->flip_helper);
> +}
> +
> +static void
> +psb_intel_crtc_process_vblank_real(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);
> +}
> +
> +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;
> +       struct drm_connector *connector;
> +       struct drm_crtc *hdmicrtc = NULL;
> +
> +       psb_intel_crtc_process_vblank_real(crtc);
> +
> +       /* This following is to handle HDMI pending flips if unplug the cable
> +        * After HDMI is unpluged, there is no vblank interrupt anymore.
> +        * But there may be some pending flips exists in psb_intel_crtc.
> +        * We need complet the pending flips.
> +        * This can be REVERTED, if the pending flip issue fixed.
> +        */
> +       if (pipe != 0)
> +               return;
> +
> +       hdmicrtc = dev_priv->pipe_to_crtc_mapping[1];
> +       if (!hdmicrtc)
> +               return;
> +
> +       list_for_each_entry(connector,
> +                       &crtc->dev->mode_config.connector_list, head) {
> +               if (connector->connector_type == DRM_MODE_CONNECTOR_DVID &&
> +                       connector->status == connector_status_disconnected) {
> +                       psb_intel_crtc_process_vblank_real(hdmicrtc);
> +                       break;
> +               }
> +       }
> +}
> +
> +static void
> +sync_callback(struct pvr_pending_sync *pending_sync, bool from_misr)
> +{
> +       struct pending_flip *pending_flip =
> +               container_of(pending_sync, struct pending_flip, pending_sync);
> +       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);
> +
> +       if (ospm_power_using_hw_begin(OSPM_DISPLAY_ISLAND, false)) {
> +               int pipe = to_psb_intel_crtc(crtc)->pipe;
> +               u32 val;
> +
> +               val = ioread32(dev_priv->vdc_reg + PSB_PIPECONF(pipe));
> +
> +               pipe_enabled = val & PIPEACONF_ENABLE;
> +
> +               if (!pipe_enabled)
> +                       ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND);
> +       }
> +
> +       if (pipe_enabled) {
> +               drm_flip_driver_prepare_flips(&dev_priv->flip_driver, &flips);
> +
> +               /* Make sure we're not interrupted during the critical phase */
> +               local_irq_disable();
> +
> +               /*
> +                * If we cross into vblank while programming the flips, we
> +                * can't determine which flips have completed. Also when
> +                * trying to synchronize multiple flips, we can't be sure
> +                * that all flips will happen on the same vblank. So, if
> +                * we're close to the start of vblank, wait until we're
> +                * safely past it before proceeding any further.
> +                */
> +               avoid_danger_zone(crtc);
> +
> +               drm_flip_driver_schedule_flips(&dev_priv->flip_driver, &flips);
> +
> +               local_irq_enable();
> +
> +               ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND);
> +       } else
> +               /* powered off, just complete all pending and new flips */
> +               drm_flip_driver_complete_flips(&dev_priv->flip_driver, &flips);
> +
> +       if (from_misr)
> +               mutex_unlock(&dev->mode_config.mutex);
> +}
> +
> +int
> +psb_intel_crtc_page_flip(struct drm_crtc *crtc,
> +                         struct drm_framebuffer *fb,
> +                         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;
> +       struct psb_fpriv *priv;
> +       struct psb_fbdev *fbdev = NULL;
> +       unsigned long flags;
> +       u32 tgid = psb_get_tgid();
> +       int ret;
> +       int pipe = to_psb_intel_crtc(crtc)->pipe;
> +       int i;
> +
> +       if (!psbfb->pvrBO)
> +               return -EINVAL;
> +
> +       new_pending_flip = kzalloc(sizeof *new_pending_flip, GFP_KERNEL);
> +       if (!new_pending_flip)
> +               return -ENOMEM;
> +
> +       /* keep a reference to the new fb, until it's no longer scanned out. */
> +       ret = psb_fb_gtt_ref(dev, psbfb->pvrBO);
> +       if (ret) {
> +               kfree(new_pending_flip);
> +               return ret;
> +       }
> +
> +       current_fb_mem_info = to_psb_fb(crtc->fb)->pvrBO;
> +
> +       /* keep a reference to the old fb, for read ops manipulations */
> +       ret = psb_fb_ref(current_fb_mem_info);
> +       if (ret) {
> +               psb_fb_gtt_unref(dev, psbfb->pvrBO, tgid);
> +               kfree(new_pending_flip);
> +               return ret;
> +       }
> +
> +       drm_flip_init(&new_pending_flip->base, &to_psb_intel_crtc(crtc)->flip_helper);
> +
> +       atomic_set(&new_pending_flip->refcnt, 1);
> +       new_pending_flip->crtc = crtc;
> +       new_pending_flip->event = event;
> +       new_pending_flip->offset = psbfb->offset;
> +
> +       if (event) {
> +               spin_lock_irqsave(&crtc->dev->event_lock, flags);
> +               priv = psb_fpriv(event->base.file_priv);
> +               list_add(&new_pending_flip->uncompleted, &priv->pending_flips);
> +               spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
> +       } else {
> +               INIT_LIST_HEAD(&new_pending_flip->uncompleted);
> +       }
> +
> +       /* In page flip, change the psb_fb_helper.fb
> +        * and fbdev to the swapped fb.
> +        */
> +       if (dev->dev_private)
> +               fbdev = ((struct drm_psb_private *)dev->dev_private)->fbdev;
> +       if (fbdev) {
> +               fbdev->psb_fb_helper.fb = fb;
> +               fbdev->psb_fb_helper.fbdev = psbfb->fbdev;
> +       } else
> +               printk(KERN_ALERT "%s cannot find the fb\n", __func__);
> +
> +       crtc->fb = fb;
> +
> +       new_pending_flip->mem_info = psbfb->pvrBO;
> +       new_pending_flip->old_mem_info = current_fb_mem_info;
> +
> +       psb_fb_increase_read_ops_pending(current_fb_mem_info,
> +                       &new_pending_flip->pending_values);
> +       psb_fb_flip_trace(current_fb_mem_info, psbfb->pvrBO);
> +
> +       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);
> +
> +       return 0;
> +}
> diff --git a/drivers/gpu/drm/gma500/psb_page_flip.h b/drivers/gpu/drm/gma500/psb_page_flip.h
> new file mode 100644
> index 000000000000..60671201d2c8
> --- /dev/null
> +++ b/drivers/gpu/drm/gma500/psb_page_flip.h
> @@ -0,0 +1,38 @@
> +/*
> + * Copyright (c) 2011, Intel Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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, write to the Free Software Foundation, Inc.,·
> + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
> + *
> + * Authors:
> + * Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
> + *
> + */
> +
> +#ifndef _PSB_PAGE_FLIP_H_
> +#define _PSB_PAGE_FLIP_H_
> +
> +void psb_page_flip_init(struct drm_device *dev);
> +void psb_page_flip_fini(struct drm_device *dev);
> +
> +void psb_page_flip_crtc_init(struct psb_intel_crtc *psb_intel_crtc);
> +void psb_page_flip_crtc_fini(struct psb_intel_crtc *psb_intel_crtc);
> +
> +void psb_intel_crtc_process_vblank(struct drm_crtc *crtc);
> +int psb_intel_crtc_page_flip(struct drm_crtc *crtc,
> +                             struct drm_framebuffer *fb,
> +                             struct drm_pending_vblank_event *event);
> +void psb_cleanup_pending_events(struct drm_device *dev,
> +                               struct psb_fpriv *priv);
> +
> +#endif /* _PSB_PAGE_FLIP_H_ */
> --
> 2.20.1
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
diff mbox series

Patch

diff --git a/drivers/gpu/drm/gma500/Makefile b/drivers/gpu/drm/gma500/Makefile
index c8f2c89be99d..3db535ef51cb 100644
--- a/drivers/gpu/drm/gma500/Makefile
+++ b/drivers/gpu/drm/gma500/Makefile
@@ -24,6 +24,7 @@  gma500_gfx-y += \
 	  psb_intel_sdvo.o \
 	  psb_lid.o \
 	  psb_irq.o \
+	  psb_page_flip.o \
 	  psb_device.o \
 	  mid_bios.o
 
diff --git a/drivers/gpu/drm/gma500/psb_page_flip.c b/drivers/gpu/drm/gma500/psb_page_flip.c
new file mode 100644
index 000000000000..6a93727cf1c1
--- /dev/null
+++ b/drivers/gpu/drm/gma500/psb_page_flip.c
@@ -0,0 +1,600 @@ 
+/*
+ * Copyright (c) 2011-2012, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, write to the Free Software Foundation, Inc.,·
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
+ * Pauli Nieminen <pauli.nieminen@intel.com>
+ * Ville Syrjälä <ville.syrjala@linux.intel.com>
+ */
+
+#include <linux/spinlock.h>
+#include <linux/list.h>
+
+#include <drm/drmP.h>
+#include "psb_drv.h"
+#include "framebuffer.h"
+#include "psb_intel_reg.h"
+#include "psb_page_flip.h"
+
+#include "mdfld_output.h"
+#include "mdfld_dsi_output.h"
+
+#if defined(CONFIG_MDFLD_DUAL_DSI_DPU)
+#include "mdfld_dsi_dbi_dpu.h"
+#elif defined(CONFIG_MDFLD_DSI_DSR)
+#include "mdfld_dsi_dbi.h"
+#endif
+
+struct pending_flip {
+	struct drm_crtc *crtc;
+	struct drm_pending_vblank_event *event;
+	PVRSRV_KERNEL_MEM_INFO *mem_info;
+	PVRSRV_KERNEL_MEM_INFO *old_mem_info;
+	uint32_t offset;
+	struct list_head uncompleted;
+	struct pvr_pending_sync pending_sync;
+	struct drm_flip base;
+	u32 vbl_count;
+	struct list_head companions;
+	u32 tgid;
+	bool vblank_ref;
+	atomic_t refcnt;
+	struct psb_pending_values pending_values;
+};
+
+enum {
+	/* somwehat arbitrary value */
+	PSB_VBL_CNT_TIMEOUT = 5,
+};
+
+static u32 get_vbl_count(struct drm_crtc *crtc)
+{
+	struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc);
+	struct drm_psb_private *dev_priv = crtc->dev->dev_private;
+	int pipe = psb_intel_crtc->pipe;
+	u32 high, low1, low2, dsl;
+	unsigned int timeout = 0;
+
+	/* All reads must be satisfied during the same frame */
+	do {
+		void __iomem *reg_pixel;
+		void __iomem *reg_high;
+
+		reg_pixel = dev_priv->vdc_reg + PSB_PIPEFRAMEPIXEL(pipe);
+		reg_high = dev_priv->vdc_reg + PSB_PIPEFRAMEHIGH(pipe);
+		low1 = ioread32(reg_pixel) >> PIPE_FRAME_LOW_SHIFT;
+		high = ioread32(reg_high) << 8;
+		dsl = ioread32(dev_priv->vdc_reg + PSB_PIPE_DSL(pipe));
+		low2 = ioread32(reg_pixel) >> PIPE_FRAME_LOW_SHIFT;
+	} while (low1 != low2 && timeout++ < PSB_VBL_CNT_TIMEOUT);
+
+	if (timeout >= PSB_VBL_CNT_TIMEOUT)
+		dev_warn(crtc->dev->dev,
+			 "Timed out while determining VBL count for pipe %d\n",
+			 psb_intel_crtc->pipe);
+
+	/*
+	 * The frame counter seems to increment at the beginning of the
+	 * last scanline. The hardware performs the flip at the start
+	 * of the vblank, so we want to count those events instead.
+	 * Cook up a vblank counter from the frame counter and scanline
+	 * counter.
+	 */
+	return ((high | low2) +
+		((dsl >= crtc->hwmode.crtc_vdisplay) &&
+		 (dsl < crtc->hwmode.crtc_vtotal - 1))) & 0xffffff;
+}
+
+static unsigned int usecs_to_scanlines(struct drm_crtc *crtc,
+				       unsigned int usecs)
+{
+	/* paranoia */
+	if (!crtc->hwmode.crtc_htotal)
+		return 1;
+
+	return DIV_ROUND_UP(usecs * crtc->hwmode.clock,
+			    1000 * crtc->hwmode.crtc_htotal);
+}
+
+/* Called with interrupts off. */
+static void avoid_danger_zone(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_psb_private *dev_priv = dev->dev_private;
+	struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc);
+	int pipe = psb_intel_crtc->pipe;
+	/*
+	 * With CRTC and one overlay we're usually spending 30-40 usecs
+	 * between avoid_danger_zone() and psb_flip_driver_flush(). Leave
+	 * a small safety margin and avoid flipping while under 60 usec
+	 * from the vblank start.
+	 */
+	u32 min = crtc->hwmode.crtc_vdisplay - usecs_to_scanlines(crtc, 60);
+	u32 max = crtc->hwmode.crtc_vdisplay - 1;
+	long timeout = msecs_to_jiffies(1);
+	u32 val;
+
+	drm_vblank_get(dev, pipe);
+
+	psb_intel_crtc->vbl_received = false;
+	val = ioread32(dev_priv->vdc_reg + PSB_PIPE_DSL(pipe));
+
+	while (val >= min && val <= max && timeout > 0) {
+		local_irq_enable();
+
+		timeout = wait_event_timeout(psb_intel_crtc->vbl_wait,
+					     psb_intel_crtc->vbl_received,
+					     timeout);
+
+		local_irq_disable();
+
+		psb_intel_crtc->vbl_received = false;
+		val = ioread32(dev_priv->vdc_reg + PSB_PIPE_DSL(pipe));
+	}
+
+	drm_vblank_put(dev, pipe);
+
+	if (val >= min && val <= max)
+		dev_warn(dev->dev,
+			 "Page flipping close to vblank start (DSL=%u, VBL=%u)\n",
+			 val, crtc->hwmode.crtc_vdisplay);
+}
+
+void
+psb_cleanup_pending_events(struct drm_device *dev, struct psb_fpriv *priv)
+{
+	struct drm_pending_vblank_event *e;
+	struct pending_flip *pending_flip, *temp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	list_for_each_entry_safe(pending_flip, temp, &priv->pending_flips,
+			uncompleted) {
+		e = pending_flip->event;
+		pending_flip->event = NULL;
+		e->base.destroy(&e->base);
+		list_del_init(&pending_flip->uncompleted);
+	}
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static void
+send_page_flip_event(struct drm_device *dev, int pipe,
+		     struct pending_flip *pending_flip)
+{
+	struct drm_pending_vblank_event *e;
+	struct timeval now;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+
+	if (!pending_flip->event)
+		goto unlock;
+
+	list_del(&pending_flip->uncompleted);
+	e = pending_flip->event;
+	do_gettimeofday(&now);
+	e->event.sequence = drm_vblank_count(dev, pipe);
+	e->event.tv_sec = now.tv_sec;
+	e->event.tv_usec = now.tv_usec;
+	list_add_tail(&e->base.link,
+			&e->base.file_priv->event_list);
+	wake_up_interruptible(&e->base.file_priv->event_wait);
+
+unlock:
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static void free_flip(struct pending_flip *crtc_flip)
+{
+	if (atomic_dec_and_test(&crtc_flip->refcnt)) {
+		psb_fb_unref(crtc_flip->old_mem_info, crtc_flip->tgid);
+		kfree(crtc_flip);
+	}
+}
+
+static void crtc_flip_cleanup(struct drm_flip *flip)
+{
+	struct pending_flip *crtc_flip =
+		container_of(flip, struct pending_flip, base);
+	struct drm_device *dev = crtc_flip->crtc->dev;
+
+	mutex_lock(&dev->mode_config.mutex);
+	psb_fb_gtt_unref(dev, crtc_flip->mem_info, crtc_flip->tgid);
+	mutex_unlock(&dev->mode_config.mutex);
+
+	free_flip(crtc_flip);
+}
+
+static void crtc_flip_finish(struct drm_flip *flip)
+{
+}
+
+static void psb_flip_driver_flush(struct drm_flip_driver *driver)
+{
+	struct drm_psb_private *dev_priv =
+		container_of(driver, struct drm_psb_private, flip_driver);
+
+	/* Flush posted writes */
+	(void)ioread32(dev_priv->vdc_reg + PSB_PIPESTAT(PSB_PIPE_A));
+}
+
+static void psb_flip_complete_sync_callback(struct pvr_pending_sync *sync,
+		bool call_from_work)
+{
+	struct pending_flip *crtc_flip =
+		container_of(sync, struct pending_flip, pending_sync);
+
+	if (psb_fb_increase_read_ops_completed(crtc_flip->old_mem_info,
+			&crtc_flip->pending_values, sync)) {
+		WARN(true, "Sync callback called without completing operation");
+		return;
+	}
+
+	free_flip(crtc_flip);
+
+	if (call_from_work)
+		mutex_lock(&gPVRSRVLock);
+
+	PVRSRVScheduleDeviceCallbacks();
+
+	if (call_from_work)
+		mutex_unlock(&gPVRSRVLock);
+}
+
+static void crtc_flip_complete(struct drm_flip *flip)
+{
+	struct pending_flip *crtc_flip =
+		container_of(flip, struct pending_flip, base);
+	struct drm_device *dev = crtc_flip->crtc->dev;
+	int pipe = to_psb_intel_crtc(crtc_flip->crtc)->pipe;
+
+	send_page_flip_event(dev, pipe, crtc_flip);
+
+	if (crtc_flip->vblank_ref)
+		drm_vblank_put(dev, pipe);
+
+	atomic_inc(&crtc_flip->refcnt);
+	crtc_flip->pending_sync.callback = psb_flip_complete_sync_callback;
+	if (psb_fb_increase_read_ops_completed(crtc_flip->old_mem_info,
+				&crtc_flip->pending_values,
+				&crtc_flip->pending_sync))
+		return;
+
+	free_flip(crtc_flip);
+	PVRSRVScheduleDeviceCallbacks();
+}
+
+static const struct drm_flip_driver_funcs psb_flip_driver_funcs = {
+	.flush = psb_flip_driver_flush,
+};
+
+void psb_page_flip_init(struct drm_device *dev)
+{
+	struct drm_psb_private *dev_priv = dev->dev_private;
+
+	drm_flip_driver_init(&dev_priv->flip_driver,
+			     &psb_flip_driver_funcs);
+}
+
+void psb_page_flip_fini(struct drm_device *dev)
+{
+	struct drm_psb_private *dev_priv = dev->dev_private;
+
+	drm_flip_driver_fini(&dev_priv->flip_driver);
+}
+
+static bool vbl_count_after_eq(u32 a, u32 b)
+{
+	return !((a - b) & 0x800000);
+}
+
+static bool crtc_check(struct drm_flip *pending_flip,
+		       u32 vbl_count)
+{
+	struct pending_flip *crtc_flip =
+		container_of(pending_flip, struct pending_flip, base);
+
+	return vbl_count_after_eq(vbl_count, crtc_flip->vbl_count);
+}
+
+static bool crtc_flip(struct drm_flip *flip,
+		      struct drm_flip *pending_flip)
+{
+	struct pending_flip *crtc_flip = container_of(flip, struct pending_flip, base);
+	struct drm_crtc *crtc = crtc_flip->crtc;
+	struct drm_device *dev = crtc->dev;
+	struct drm_psb_private *dev_priv = dev->dev_private;
+	int pipe = to_psb_intel_crtc(crtc)->pipe;
+	u32 vbl_count;
+
+	crtc_flip->vblank_ref = drm_vblank_get(dev, pipe) == 0;
+
+	vbl_count = get_vbl_count(crtc);
+
+	iowrite32(crtc_flip->offset, dev_priv->vdc_reg + PSB_DSPSURF(pipe));
+
+	/* This flip will happen on the next vblank */
+	crtc_flip->vbl_count = (vbl_count + 1) & 0xffffff;
+
+	if (pending_flip) {
+		struct pending_flip *old_crtc_flip =
+			container_of(pending_flip, struct pending_flip, base);
+		bool flipped = crtc_check(pending_flip, vbl_count);
+
+		if (!flipped) {
+			swap(crtc_flip->old_mem_info, old_crtc_flip->old_mem_info);
+			swap(crtc_flip->pending_values, old_crtc_flip->pending_values);
+		}
+
+		return flipped;
+	}
+
+	return false;
+}
+
+static bool crtc_vblank(struct drm_flip *pending_flip)
+{
+	struct pending_flip *crtc_flip =
+		container_of(pending_flip, struct pending_flip, base);
+	u32 vbl_count = get_vbl_count(crtc_flip->crtc);
+
+	return crtc_check(pending_flip, vbl_count);
+}
+
+static const struct drm_flip_helper_funcs crtc_flip_funcs = {
+	.flip = crtc_flip,
+	.vblank = crtc_vblank,
+	.complete = crtc_flip_complete,
+	.finish = crtc_flip_finish,
+	.cleanup = crtc_flip_cleanup,
+};
+
+void psb_page_flip_crtc_init(struct psb_intel_crtc *psb_intel_crtc)
+{
+	struct drm_device *dev = psb_intel_crtc->base.dev;
+	struct drm_psb_private *dev_priv = dev->dev_private;
+
+	drm_flip_helper_init(&psb_intel_crtc->flip_helper,
+			     &dev_priv->flip_driver,
+			     &crtc_flip_funcs);
+}
+
+void psb_page_flip_crtc_fini(struct psb_intel_crtc *psb_intel_crtc)
+{
+	drm_flip_helper_fini(&psb_intel_crtc->flip_helper);
+}
+
+static void
+psb_intel_crtc_process_vblank_real(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);
+}
+
+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;
+	struct drm_connector *connector;
+	struct drm_crtc *hdmicrtc = NULL;
+
+	psb_intel_crtc_process_vblank_real(crtc);
+
+	/* This following is to handle HDMI pending flips if unplug the cable
+	 * After HDMI is unpluged, there is no vblank interrupt anymore.
+	 * But there may be some pending flips exists in psb_intel_crtc.
+	 * We need complet the pending flips.
+	 * This can be REVERTED, if the pending flip issue fixed.
+	 */
+	if (pipe != 0)
+		return;
+
+	hdmicrtc = dev_priv->pipe_to_crtc_mapping[1];
+	if (!hdmicrtc)
+		return;
+
+	list_for_each_entry(connector,
+			&crtc->dev->mode_config.connector_list, head) {
+		if (connector->connector_type == DRM_MODE_CONNECTOR_DVID &&
+			connector->status == connector_status_disconnected) {
+			psb_intel_crtc_process_vblank_real(hdmicrtc);
+			break;
+		}
+	}
+}
+
+static void
+sync_callback(struct pvr_pending_sync *pending_sync, bool from_misr)
+{
+	struct pending_flip *pending_flip =
+		container_of(pending_sync, struct pending_flip, pending_sync);
+	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);
+
+	if (ospm_power_using_hw_begin(OSPM_DISPLAY_ISLAND, false)) {
+		int pipe = to_psb_intel_crtc(crtc)->pipe;
+		u32 val;
+
+		val = ioread32(dev_priv->vdc_reg + PSB_PIPECONF(pipe));
+
+		pipe_enabled = val & PIPEACONF_ENABLE;
+
+		if (!pipe_enabled)
+			ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND);
+	}
+
+	if (pipe_enabled) {
+		drm_flip_driver_prepare_flips(&dev_priv->flip_driver, &flips);
+
+		/* Make sure we're not interrupted during the critical phase */
+		local_irq_disable();
+
+		/*
+		 * If we cross into vblank while programming the flips, we
+		 * can't determine which flips have completed. Also when
+		 * trying to synchronize multiple flips, we can't be sure
+		 * that all flips will happen on the same vblank. So, if
+		 * we're close to the start of vblank, wait until we're
+		 * safely past it before proceeding any further.
+		 */
+		avoid_danger_zone(crtc);
+
+		drm_flip_driver_schedule_flips(&dev_priv->flip_driver, &flips);
+
+		local_irq_enable();
+
+		ospm_power_using_hw_end(OSPM_DISPLAY_ISLAND);
+	} else
+		/* powered off, just complete all pending and new flips */
+		drm_flip_driver_complete_flips(&dev_priv->flip_driver, &flips);
+
+	if (from_misr)
+		mutex_unlock(&dev->mode_config.mutex);
+}
+
+int
+psb_intel_crtc_page_flip(struct drm_crtc *crtc,
+                         struct drm_framebuffer *fb,
+                         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;
+	struct psb_fpriv *priv;
+	struct psb_fbdev *fbdev = NULL;
+	unsigned long flags;
+	u32 tgid = psb_get_tgid();
+	int ret;
+	int pipe = to_psb_intel_crtc(crtc)->pipe;
+	int i;
+
+	if (!psbfb->pvrBO)
+		return -EINVAL;
+
+	new_pending_flip = kzalloc(sizeof *new_pending_flip, GFP_KERNEL);
+	if (!new_pending_flip)
+		return -ENOMEM;
+
+	/* keep a reference to the new fb, until it's no longer scanned out. */
+	ret = psb_fb_gtt_ref(dev, psbfb->pvrBO);
+	if (ret) {
+		kfree(new_pending_flip);
+		return ret;
+	}
+
+	current_fb_mem_info = to_psb_fb(crtc->fb)->pvrBO;
+
+	/* keep a reference to the old fb, for read ops manipulations */
+	ret = psb_fb_ref(current_fb_mem_info);
+	if (ret) {
+		psb_fb_gtt_unref(dev, psbfb->pvrBO, tgid);
+		kfree(new_pending_flip);
+		return ret;
+	}
+
+	drm_flip_init(&new_pending_flip->base, &to_psb_intel_crtc(crtc)->flip_helper);
+
+	atomic_set(&new_pending_flip->refcnt, 1);
+	new_pending_flip->crtc = crtc;
+	new_pending_flip->event = event;
+	new_pending_flip->offset = psbfb->offset;
+
+	if (event) {
+		spin_lock_irqsave(&crtc->dev->event_lock, flags);
+		priv = psb_fpriv(event->base.file_priv);
+		list_add(&new_pending_flip->uncompleted, &priv->pending_flips);
+		spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+	} else {
+		INIT_LIST_HEAD(&new_pending_flip->uncompleted);
+	}
+
+	/* In page flip, change the psb_fb_helper.fb
+	 * and fbdev to the swapped fb.
+	 */
+	if (dev->dev_private)
+		fbdev = ((struct drm_psb_private *)dev->dev_private)->fbdev;
+	if (fbdev) {
+		fbdev->psb_fb_helper.fb = fb;
+		fbdev->psb_fb_helper.fbdev = psbfb->fbdev;
+	} else
+		printk(KERN_ALERT "%s cannot find the fb\n", __func__);
+
+	crtc->fb = fb;
+
+	new_pending_flip->mem_info = psbfb->pvrBO;
+	new_pending_flip->old_mem_info = current_fb_mem_info;
+
+	psb_fb_increase_read_ops_pending(current_fb_mem_info,
+			&new_pending_flip->pending_values);
+	psb_fb_flip_trace(current_fb_mem_info, psbfb->pvrBO);
+
+	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);
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/gma500/psb_page_flip.h b/drivers/gpu/drm/gma500/psb_page_flip.h
new file mode 100644
index 000000000000..60671201d2c8
--- /dev/null
+++ b/drivers/gpu/drm/gma500/psb_page_flip.h
@@ -0,0 +1,38 @@ 
+/*
+ * Copyright (c) 2011, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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, write to the Free Software Foundation, Inc.,·
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
+ *
+ */
+
+#ifndef _PSB_PAGE_FLIP_H_
+#define _PSB_PAGE_FLIP_H_
+
+void psb_page_flip_init(struct drm_device *dev);
+void psb_page_flip_fini(struct drm_device *dev);
+
+void psb_page_flip_crtc_init(struct psb_intel_crtc *psb_intel_crtc);
+void psb_page_flip_crtc_fini(struct psb_intel_crtc *psb_intel_crtc);
+
+void psb_intel_crtc_process_vblank(struct drm_crtc *crtc);
+int psb_intel_crtc_page_flip(struct drm_crtc *crtc,
+                             struct drm_framebuffer *fb,
+                             struct drm_pending_vblank_event *event);
+void psb_cleanup_pending_events(struct drm_device *dev,
+				struct psb_fpriv *priv);
+
+#endif /* _PSB_PAGE_FLIP_H_ */