This adds support for dri2 pageflipping.

Signed-off-by: Alex Deucher <alexdeucher at gmail.com>
Signed-off-by: Jerome Glisse <jglisse at redhat.com>
---
 drivers/gpu/drm/radeon/evergreen.c      |   36 +++++++++++
 drivers/gpu/drm/radeon/evergreen_reg.h  |    3 +
 drivers/gpu/drm/radeon/evergreend.h     |   14 ++++
 drivers/gpu/drm/radeon/r100.c           |  104 ++++++++++++++++++++++++++++---
 drivers/gpu/drm/radeon/r500_reg.h       |    1 +
 drivers/gpu/drm/radeon/r600.c           |   23 +++++++
 drivers/gpu/drm/radeon/r600d.h          |   20 ++++++
 drivers/gpu/drm/radeon/radeon.h         |   12 ++++
 drivers/gpu/drm/radeon/radeon_asic.c    |   42 ++++++++++++
 drivers/gpu/drm/radeon/radeon_asic.h    |    7 ++
 drivers/gpu/drm/radeon/radeon_display.c |  101 ++++++++++++++++++++++++++++++
 drivers/gpu/drm/radeon/radeon_drv.c     |    2 +-
 drivers/gpu/drm/radeon/radeon_irq_kms.c |   62 ++++++++++++++++++
 drivers/gpu/drm/radeon/radeon_mode.h    |    7 ++
 drivers/gpu/drm/radeon/rs600.c          |   49 ++++++++++++---
 drivers/gpu/drm/radeon/rs600d.h         |   13 ++++
 drivers/gpu/drm/radeon/rv770.c          |   34 ++++++++++
 drivers/gpu/drm/radeon/rv770d.h         |   39 ++++++++++++
 18 files changed, 552 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/radeon/evergreen.c 
b/drivers/gpu/drm/radeon/evergreen.c
index 242055a..19d9a0c 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -40,6 +40,27 @@
 static void evergreen_gpu_init(struct radeon_device *rdev);
 void evergreen_fini(struct radeon_device *rdev);

+void evergreen_page_flip(struct drm_crtc *crtc, u64 crtc_base)
+{
+       struct drm_device *dev = crtc->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+#if 0
+       radeon_ring_write(rdev,
+                         PACKET0(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH 
+ radeon_crtc->crtc_offset, 0));
+       radeon_ring_write(rdev, upper_32_bits(crtc_base));
+       radeon_ring_write(rdev,
+                         PACKET0(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + 
radeon_crtc->crtc_offset, 0));
+       radeon_ring_write(rdev, (u32)crtc_base);
+#endif
+       radeon_ring_write(rdev,
+                         PACKET0(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + 
radeon_crtc->crtc_offset, 0));
+       radeon_ring_write(rdev, upper_32_bits(crtc_base));
+       radeon_ring_write(rdev,
+                         PACKET0(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + 
radeon_crtc->crtc_offset, 0));
+       radeon_ring_write(rdev, (u32)crtc_base);
+}
+
 /* get temperature in millidegrees */
 u32 evergreen_get_temp(struct radeon_device *rdev)
 {
@@ -2300,6 +2321,7 @@ int evergreen_irq_process(struct radeon_device *rdev)
        u32 ring_index;
        unsigned long flags;
        bool queue_hotplug = false;
+       bool queue_unpin = false;

        DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr);
        if (!rdev->ih.enabled)
@@ -2456,36 +2478,48 @@ restart_ih:
                        break;
                case 8: /* D1 pflip */
                        if (rdev->irq.stat_regs.evergreen.d1grph_int & 
GRPH_PFLIP_INT_OCCURRED) {
+                               radeon_crtc_handle_flip(rdev, 0);
+                               queue_unpin = true;
                                rdev->irq.stat_regs.evergreen.d1grph_int &= 
~GRPH_PFLIP_INT_OCCURRED;
                                DRM_DEBUG("IH: D1 pflip\n");
                        }
                        break;
                case 10: /* D2 pflip */
                        if (rdev->irq.stat_regs.evergreen.d2grph_int & 
GRPH_PFLIP_INT_OCCURRED) {
+                               radeon_crtc_handle_flip(rdev, 1);
+                               queue_unpin = true;
                                rdev->irq.stat_regs.evergreen.d2grph_int &= 
~GRPH_PFLIP_INT_OCCURRED;
                                DRM_DEBUG("IH: D2 pflip\n");
                        }
                        break;
                case 12: /* D3 pflip */
                        if (rdev->irq.stat_regs.evergreen.d3grph_int & 
GRPH_PFLIP_INT_OCCURRED) {
+                               radeon_crtc_handle_flip(rdev, 2);
+                               queue_unpin = true;
                                rdev->irq.stat_regs.evergreen.d3grph_int &= 
~GRPH_PFLIP_INT_OCCURRED;
                                DRM_DEBUG("IH: D3 pflip\n");
                        }
                        break;
                case 14: /* D4 pflip */
                        if (rdev->irq.stat_regs.evergreen.d4grph_int & 
GRPH_PFLIP_INT_OCCURRED) {
+                               radeon_crtc_handle_flip(rdev, 3);
+                               queue_unpin = true;
                                rdev->irq.stat_regs.evergreen.d4grph_int &= 
~GRPH_PFLIP_INT_OCCURRED;
                                DRM_DEBUG("IH: D4 pflip\n");
                        }
                        break;
                case 16: /* D5 pflip */
                        if (rdev->irq.stat_regs.evergreen.d5grph_int & 
GRPH_PFLIP_INT_OCCURRED) {
+                               radeon_crtc_handle_flip(rdev, 4);
+                               queue_unpin = true;
                                rdev->irq.stat_regs.evergreen.d5grph_int &= 
~GRPH_PFLIP_INT_OCCURRED;
                                DRM_DEBUG("IH: D5 pflip\n");
                        }
                        break;
                case 18: /* D6 pflip */
                        if (rdev->irq.stat_regs.evergreen.d6grph_int & 
GRPH_PFLIP_INT_OCCURRED) {
+                               radeon_crtc_handle_flip(rdev, 5);
+                               queue_unpin = true;
                                rdev->irq.stat_regs.evergreen.d6grph_int &= 
~GRPH_PFLIP_INT_OCCURRED;
                                DRM_DEBUG("IH: D6 pflip\n");
                        }
@@ -2569,6 +2603,8 @@ restart_ih:
                goto restart_ih;
        if (queue_hotplug)
                queue_work(rdev->wq, &rdev->hotplug_work);
+       if (queue_unpin)
+               queue_work(rdev->wq, &rdev->unpin_work);
        rdev->ih.rptr = rptr;
        WREG32(IH_RB_RPTR, rdev->ih.rptr);
        spin_unlock_irqrestore(&rdev->ih.lock, flags);
diff --git a/drivers/gpu/drm/radeon/evergreen_reg.h 
b/drivers/gpu/drm/radeon/evergreen_reg.h
index 2330f3a..e8c625b 100644
--- a/drivers/gpu/drm/radeon/evergreen_reg.h
+++ b/drivers/gpu/drm/radeon/evergreen_reg.h
@@ -105,6 +105,9 @@
 #define EVERGREEN_GRPH_Y_START                          0x6830
 #define EVERGREEN_GRPH_X_END                            0x6834
 #define EVERGREEN_GRPH_Y_END                            0x6838
+#define EVERGREEN_GRPH_UPDATE                           0x6844
+#       define EVERGREEN_GRPH_SURFACE_UPDATE_PENDING    (1 << 2)
+#       define EVERGREEN_GRPH_UPDATE_LOCK               (1 << 16)

 /* CUR blocks at 0x6998, 0x7598, 0x10198, 0x10d98, 0x11998, 0x12598 */
 #define EVERGREEN_CUR_CONTROL                           0x6998
diff --git a/drivers/gpu/drm/radeon/evergreend.h 
b/drivers/gpu/drm/radeon/evergreend.h
index 113c70c..5ffbc32 100644
--- a/drivers/gpu/drm/radeon/evergreend.h
+++ b/drivers/gpu/drm/radeon/evergreend.h
@@ -628,6 +628,20 @@
 #define        PACKET3_MEM_SEMAPHORE                           0x39
 #define        PACKET3_MPEG_INDEX                              0x3A
 #define        PACKET3_WAIT_REG_MEM                            0x3C
+#              define PACKET3_WAIT_REG_MEM_ENGINE(x)    ((x) << 8)
+#              define ENGINE_ME                         0
+#              define ENGINE_PFP                        1
+#              define PACKET3_WAIT_REG_MEM_MEM_SPACE(x) ((x) << 4)
+#              define MEM_SPACE_REG                     0
+#              define MEM_SPACE_MEM                     1
+#              define PACKET3_WAIT_REG_MEM_FUNCTION(x)  ((x) << 0)
+#              define FUNCTION_ALWAYS                   0
+#              define FUNCTION_LT                       1
+#              define FUNCTION_LTE                      2
+#              define FUNCTION_E                        3
+#              define FUNCTION_NE                       4
+#              define FUNCTION_GTE                      5
+#              define FUNCTION_GT                       6
 #define        PACKET3_MEM_WRITE                               0x3D
 #define        PACKET3_INDIRECT_BUFFER                         0x32
 #define        PACKET3_SURFACE_SYNC                            0x43
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index 6d1540c..cde67d1 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -68,6 +68,77 @@ MODULE_FIRMWARE(FIRMWARE_R520);
  * r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280
  */

+void r100_pre_page_flip(struct radeon_device *rdev, int crtc)
+{
+       /* enable the pflip int */
+       radeon_irq_kms_pflip_irq_get(rdev, crtc);
+}
+
+void r100_post_page_flip(struct radeon_device *rdev, int crtc)
+{
+       /* disable the pflip int */
+       radeon_irq_kms_pflip_irq_put(rdev, crtc);
+}
+
+void r100_page_flip(struct drm_crtc *crtc, u64 crtc_base)
+{
+       struct drm_device *dev = crtc->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+       struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(crtc->fb);
+       struct drm_framebuffer *target_fb = crtc->fb;
+       struct drm_gem_object *obj = radeon_fb->obj;
+       struct radeon_bo *rbo = obj->driver_private;
+       u32 tiling_flags, pitch_pixels;
+       int r;
+
+       /* crtc offset is from display base addr not FB location */
+       crtc_base -= radeon_crtc->legacy_display_base_addr;
+
+       r = radeon_bo_reserve(rbo, false);
+       if (likely(r == 0)) {
+               radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL);
+               radeon_bo_unreserve(rbo);
+       }
+
+       pitch_pixels = target_fb->pitch / (target_fb->bits_per_pixel / 8);
+
+       if (tiling_flags & RADEON_TILING_MACRO) {
+               if (ASIC_IS_R300(rdev)) {
+                       crtc_base &= ~0x7ff;
+               } else {
+                       int byteshift = target_fb->bits_per_pixel >> 4;
+                       int tile_addr = (((crtc->y >> 3) * pitch_pixels +  
crtc->x) >> (8 - byteshift)) << 11;
+                       crtc_base += tile_addr + ((crtc->x << byteshift) % 256) 
+ ((crtc->y % 8) << 8);
+               }
+       } else {
+               int offset = crtc->y * pitch_pixels + crtc->x;
+               switch (target_fb->bits_per_pixel) {
+               case 8:
+               default:
+                       offset *= 1;
+                       break;
+               case 15:
+               case 16:
+                       offset *= 2;
+                       break;
+               case 24:
+                       offset *= 3;
+                       break;
+               case 32:
+                       offset *= 4;
+                       break;
+               }
+               crtc_base += offset;
+       }
+       crtc_base &= ~7;
+
+       radeon_ring_write(rdev,
+                         PACKET0(RADEON_CRTC_OFFSET + 
radeon_crtc->crtc_offset, 0));
+       radeon_ring_write(rdev, (u32)crtc_base);
+
+}
+
 void r100_pm_get_dynpm_state(struct radeon_device *rdev)
 {
        int i;
@@ -526,10 +597,12 @@ int r100_irq_set(struct radeon_device *rdev)
        if (rdev->irq.gui_idle) {
                tmp |= RADEON_GUI_IDLE_MASK;
        }
-       if (rdev->irq.crtc_vblank_int[0]) {
+       if (rdev->irq.crtc_vblank_int[0]  ||
+           rdev->irq.pflip[0]) {
                tmp |= RADEON_CRTC_VBLANK_MASK;
        }
-       if (rdev->irq.crtc_vblank_int[1]) {
+       if (rdev->irq.crtc_vblank_int[1] ||
+           rdev->irq.pflip[1]) {
                tmp |= RADEON_CRTC2_VBLANK_MASK;
        }
        if (rdev->irq.hpd[0]) {
@@ -576,6 +649,7 @@ int r100_irq_process(struct radeon_device *rdev)
 {
        uint32_t status, msi_rearm;
        bool queue_hotplug = false;
+       bool queue_unpin = false;

        /* reset gui idle ack.  the status bit is broken */
        rdev->irq.gui_idle_acked = false;
@@ -600,14 +674,26 @@ int r100_irq_process(struct radeon_device *rdev)
                }
                /* Vertical blank interrupts */
                if (status & RADEON_CRTC_VBLANK_STAT) {
-                       drm_handle_vblank(rdev->ddev, 0);
-                       rdev->pm.vblank_sync = true;
-                       wake_up(&rdev->irq.vblank_queue);
+                       if (rdev->irq.crtc_vblank_int[0]) {
+                               drm_handle_vblank(rdev->ddev, 0);
+                               rdev->pm.vblank_sync = true;
+                               wake_up(&rdev->irq.vblank_queue);
+                       }
+                       if (rdev->irq.pflip[0]) {
+                               radeon_crtc_handle_flip(rdev, 0);
+                               queue_unpin = true;
+                       }
                }
                if (status & RADEON_CRTC2_VBLANK_STAT) {
-                       drm_handle_vblank(rdev->ddev, 1);
-                       rdev->pm.vblank_sync = true;
-                       wake_up(&rdev->irq.vblank_queue);
+                       if (rdev->irq.crtc_vblank_int[1]) {
+                               drm_handle_vblank(rdev->ddev, 1);
+                               rdev->pm.vblank_sync = true;
+                               wake_up(&rdev->irq.vblank_queue);
+                       }
+                       if (rdev->irq.pflip[1]) {
+                               radeon_crtc_handle_flip(rdev, 1);
+                               queue_unpin = true;
+                       }
                }
                if (status & RADEON_FP_DETECT_STAT) {
                        queue_hotplug = true;
@@ -623,6 +709,8 @@ int r100_irq_process(struct radeon_device *rdev)
        rdev->irq.gui_idle_acked = false;
        if (queue_hotplug)
                queue_work(rdev->wq, &rdev->hotplug_work);
+       if (queue_unpin)
+               queue_work(rdev->wq, &rdev->unpin_work);
        if (rdev->msi_enabled) {
                switch (rdev->family) {
                case CHIP_RS400:
diff --git a/drivers/gpu/drm/radeon/r500_reg.h 
b/drivers/gpu/drm/radeon/r500_reg.h
index 6ac1f60..bc1eea1 100644
--- a/drivers/gpu/drm/radeon/r500_reg.h
+++ b/drivers/gpu/drm/radeon/r500_reg.h
@@ -409,6 +409,7 @@
 #define AVIVO_D1GRPH_X_END                                      0x6134
 #define AVIVO_D1GRPH_Y_END                                      0x6138
 #define AVIVO_D1GRPH_UPDATE                                     0x6144
+#       define AVIVO_D1GRPH_SURFACE_UPDATE_PENDING              (1 << 2)
 #       define AVIVO_D1GRPH_UPDATE_LOCK                         (1 << 16)
 #define AVIVO_D1GRPH_FLIP_CONTROL                               0x6148

diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index d1d91e9..8a2fe86 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -92,6 +92,22 @@ void r600_gpu_init(struct radeon_device *rdev);
 void r600_fini(struct radeon_device *rdev);
 void r600_irq_disable(struct radeon_device *rdev);

+void r600_page_flip(struct drm_crtc *crtc, u64 crtc_base)
+{
+       struct drm_device *dev = crtc->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+#if 0
+       radeon_ring_write(rdev,
+                         PACKET0(D1GRPH_SECONDARY_SURFACE_ADDRESS + 
radeon_crtc->crtc_offset, 0));
+       radeon_ring_write(rdev, (u32)crtc_base);
+#endif
+       radeon_ring_write(rdev,
+                         PACKET0(D1GRPH_PRIMARY_SURFACE_ADDRESS + 
radeon_crtc->crtc_offset, 0));
+       radeon_ring_write(rdev, (u32)crtc_base);
+
+}
+
 /* get temperature in millidegrees */
 u32 rv6xx_get_temp(struct radeon_device *rdev)
 {
@@ -3274,6 +3290,7 @@ int r600_irq_process(struct radeon_device *rdev)
        u32 ring_index;
        unsigned long flags;
        bool queue_hotplug = false;
+       bool queue_unpin = false;

        DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr);
        if (!rdev->ih.enabled)
@@ -3348,12 +3365,16 @@ restart_ih:
                        break;
                case 9: /* D1 pflip */
                        if (rdev->irq.stat_regs.r600.d1grph_int & 
DxGRPH_PFLIP_INT_OCCURRED) {
+                               radeon_crtc_handle_flip(rdev, 0);
+                               queue_unpin = true;
                                rdev->irq.stat_regs.r600.d1grph_int &= 
~DxGRPH_PFLIP_INT_OCCURRED;
                                DRM_DEBUG("IH: D1 pflip\n");
                        }
                        break;
                case 11: /* D2 pflip */
                        if (rdev->irq.stat_regs.r600.d2grph_int & 
DxGRPH_PFLIP_INT_OCCURRED) {
+                               radeon_crtc_handle_flip(rdev, 1);
+                               queue_unpin = true;
                                rdev->irq.stat_regs.r600.d2grph_int &= 
~DxGRPH_PFLIP_INT_OCCURRED;
                                DRM_DEBUG("IH: D2 pflip\n");
                        }
@@ -3441,6 +3462,8 @@ restart_ih:
                goto restart_ih;
        if (queue_hotplug)
                queue_work(rdev->wq, &rdev->hotplug_work);
+       if (queue_unpin)
+               queue_work(rdev->wq, &rdev->unpin_work);
        rdev->ih.rptr = rptr;
        WREG32(IH_RB_RPTR, rdev->ih.rptr);
        spin_unlock_irqrestore(&rdev->ih.lock, flags);
diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h
index 45995b4..0e5b428 100644
--- a/drivers/gpu/drm/radeon/r600d.h
+++ b/drivers/gpu/drm/radeon/r600d.h
@@ -731,6 +731,12 @@
 #       define DxGRPH_PFLIP_INT_MASK                      (1 << 0)
 #       define DxGRPH_PFLIP_INT_TYPE                      (1 << 8)

+#define D1GRPH_PRIMARY_SURFACE_ADDRESS                    0x6110
+#define D1GRPH_SECONDARY_SURFACE_ADDRESS                  0x6118
+#define D1GRPH_UPDATE                                     0x6144
+#       define D1GRPH_SURFACE_UPDATE_PENDING              (1 << 2)
+#       define D1GRPH_UPDATE_LOCK                         (1 << 16)
+
 /*
  * PM4
  */
@@ -771,6 +777,20 @@
 #define        PACKET3_MEM_SEMAPHORE                           0x39
 #define        PACKET3_MPEG_INDEX                              0x3A
 #define        PACKET3_WAIT_REG_MEM                            0x3C
+#              define PACKET3_WAIT_REG_MEM_ENGINE(x)    ((x) << 8)
+#              define ENGINE_ME                         0
+#              define ENGINE_PFP                        1
+#              define PACKET3_WAIT_REG_MEM_MEM_SPACE(x) ((x) << 4)
+#              define MEM_SPACE_REG                     0
+#              define MEM_SPACE_MEM                     1
+#              define PACKET3_WAIT_REG_MEM_FUNCTION(x)  ((x) << 0)
+#              define FUNCTION_ALWAYS                   0
+#              define FUNCTION_LT                       1
+#              define FUNCTION_LTE                      2
+#              define FUNCTION_E                        3
+#              define FUNCTION_NE                       4
+#              define FUNCTION_GTE                      5
+#              define FUNCTION_GT                       6
 #define        PACKET3_MEM_WRITE                               0x3D
 #define        PACKET3_INDIRECT_BUFFER                         0x32
 #define        PACKET3_SURFACE_SYNC                            0x43
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 94474e6..be65735 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -427,12 +427,16 @@ struct radeon_irq {
        spinlock_t sw_lock;
        int sw_refcount;
        union radeon_irq_stat_regs stat_regs;
+       spinlock_t pflip_lock[6];
+       int pflip_refcount[6];
 };

 int radeon_irq_kms_init(struct radeon_device *rdev);
 void radeon_irq_kms_fini(struct radeon_device *rdev);
 void radeon_irq_kms_sw_irq_get(struct radeon_device *rdev);
 void radeon_irq_kms_sw_irq_put(struct radeon_device *rdev);
+void radeon_irq_kms_pflip_irq_get(struct radeon_device *rdev, int crtc);
+void radeon_irq_kms_pflip_irq_put(struct radeon_device *rdev, int crtc);

 /*
  * CP & ring.
@@ -916,6 +920,10 @@ struct radeon_asic {
        void (*pm_finish)(struct radeon_device *rdev);
        void (*pm_init_profile)(struct radeon_device *rdev);
        void (*pm_get_dynpm_state)(struct radeon_device *rdev);
+       /* pageflipping */
+       void (*pre_page_flip)(struct radeon_device *rdev, int crtc);
+       void (*page_flip)(struct drm_crtc *crtc, u64 crtc_base);
+       void (*post_page_flip)(struct radeon_device *rdev, int crtc);
 };

 /*
@@ -1132,6 +1140,7 @@ struct radeon_device {
        struct r600_ih ih; /* r6/700 interrupt ring */
        struct workqueue_struct *wq;
        struct work_struct hotplug_work;
+       struct work_struct unpin_work;
        int num_crtc; /* number of crtcs */
        struct mutex dc_hw_i2c_mutex; /* display controller hw i2c mutex */
        struct mutex vram_mutex;
@@ -1375,6 +1384,9 @@ static inline void radeon_ring_write(struct radeon_device 
*rdev, uint32_t v)
 #define radeon_pm_finish(rdev) (rdev)->asic->pm_finish((rdev))
 #define radeon_pm_init_profile(rdev) (rdev)->asic->pm_init_profile((rdev))
 #define radeon_pm_get_dynpm_state(rdev) 
(rdev)->asic->pm_get_dynpm_state((rdev))
+#define radeon_pre_page_flip(rdev, crtc) rdev->asic->pre_page_flip((rdev), 
(crtc))
+#define radeon_page_flip(crtc, base) rdev->asic->page_flip((crtc), (base))
+#define radeon_post_page_flip(rdev, crtc) rdev->asic->post_page_flip((rdev), 
(crtc))

 /* Common functions */
 /* AGP */
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c 
b/drivers/gpu/drm/radeon/radeon_asic.c
index 64fb89e..6621d9e 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.c
+++ b/drivers/gpu/drm/radeon/radeon_asic.c
@@ -171,6 +171,9 @@ static struct radeon_asic r100_asic = {
        .pm_finish = &r100_pm_finish,
        .pm_init_profile = &r100_pm_init_profile,
        .pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+       .pre_page_flip = &r100_pre_page_flip,
+       .page_flip = &r100_page_flip,
+       .post_page_flip = &r100_post_page_flip,
 };

 static struct radeon_asic r200_asic = {
@@ -215,6 +218,9 @@ static struct radeon_asic r200_asic = {
        .pm_finish = &r100_pm_finish,
        .pm_init_profile = &r100_pm_init_profile,
        .pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+       .pre_page_flip = &r100_pre_page_flip,
+       .page_flip = &r100_page_flip,
+       .post_page_flip = &r100_post_page_flip,
 };

 static struct radeon_asic r300_asic = {
@@ -260,6 +266,9 @@ static struct radeon_asic r300_asic = {
        .pm_finish = &r100_pm_finish,
        .pm_init_profile = &r100_pm_init_profile,
        .pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+       .pre_page_flip = &r100_pre_page_flip,
+       .page_flip = &r100_page_flip,
+       .post_page_flip = &r100_post_page_flip,
 };

 static struct radeon_asic r300_asic_pcie = {
@@ -304,6 +313,9 @@ static struct radeon_asic r300_asic_pcie = {
        .pm_finish = &r100_pm_finish,
        .pm_init_profile = &r100_pm_init_profile,
        .pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+       .pre_page_flip = &r100_pre_page_flip,
+       .page_flip = &r100_page_flip,
+       .post_page_flip = &r100_post_page_flip,
 };

 static struct radeon_asic r420_asic = {
@@ -349,6 +361,9 @@ static struct radeon_asic r420_asic = {
        .pm_finish = &r100_pm_finish,
        .pm_init_profile = &r420_pm_init_profile,
        .pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+       .pre_page_flip = &r100_pre_page_flip,
+       .page_flip = &r100_page_flip,
+       .post_page_flip = &r100_post_page_flip,
 };

 static struct radeon_asic rs400_asic = {
@@ -394,6 +409,9 @@ static struct radeon_asic rs400_asic = {
        .pm_finish = &r100_pm_finish,
        .pm_init_profile = &r100_pm_init_profile,
        .pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+       .pre_page_flip = &r100_pre_page_flip,
+       .page_flip = &r100_page_flip,
+       .post_page_flip = &r100_post_page_flip,
 };

 static struct radeon_asic rs600_asic = {
@@ -439,6 +457,9 @@ static struct radeon_asic rs600_asic = {
        .pm_finish = &rs600_pm_finish,
        .pm_init_profile = &r420_pm_init_profile,
        .pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+       .pre_page_flip = &r100_pre_page_flip,
+       .page_flip = &rs600_page_flip,
+       .post_page_flip = &r100_post_page_flip,
 };

 static struct radeon_asic rs690_asic = {
@@ -484,6 +505,9 @@ static struct radeon_asic rs690_asic = {
        .pm_finish = &rs600_pm_finish,
        .pm_init_profile = &r420_pm_init_profile,
        .pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+       .pre_page_flip = &r100_pre_page_flip,
+       .page_flip = &rs600_page_flip,
+       .post_page_flip = &r100_post_page_flip,
 };

 static struct radeon_asic rv515_asic = {
@@ -529,6 +553,9 @@ static struct radeon_asic rv515_asic = {
        .pm_finish = &rs600_pm_finish,
        .pm_init_profile = &r420_pm_init_profile,
        .pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+       .pre_page_flip = &r100_pre_page_flip,
+       .page_flip = &rs600_page_flip,
+       .post_page_flip = &r100_post_page_flip,
 };

 static struct radeon_asic r520_asic = {
@@ -574,6 +601,9 @@ static struct radeon_asic r520_asic = {
        .pm_finish = &rs600_pm_finish,
        .pm_init_profile = &r420_pm_init_profile,
        .pm_get_dynpm_state = &r100_pm_get_dynpm_state,
+       .pre_page_flip = &r100_pre_page_flip,
+       .page_flip = &rs600_page_flip,
+       .post_page_flip = &r100_post_page_flip,
 };

 static struct radeon_asic r600_asic = {
@@ -618,6 +648,9 @@ static struct radeon_asic r600_asic = {
        .pm_finish = &rs600_pm_finish,
        .pm_init_profile = &r600_pm_init_profile,
        .pm_get_dynpm_state = &r600_pm_get_dynpm_state,
+       .pre_page_flip = &r100_pre_page_flip,
+       .page_flip = &r600_page_flip,
+       .post_page_flip = &r100_post_page_flip,
 };

 static struct radeon_asic rs780_asic = {
@@ -662,6 +695,9 @@ static struct radeon_asic rs780_asic = {
        .pm_finish = &rs600_pm_finish,
        .pm_init_profile = &rs780_pm_init_profile,
        .pm_get_dynpm_state = &r600_pm_get_dynpm_state,
+       .pre_page_flip = &r100_pre_page_flip,
+       .page_flip = &r600_page_flip,
+       .post_page_flip = &r100_post_page_flip,
 };

 static struct radeon_asic rv770_asic = {
@@ -706,6 +742,9 @@ static struct radeon_asic rv770_asic = {
        .pm_finish = &rs600_pm_finish,
        .pm_init_profile = &r600_pm_init_profile,
        .pm_get_dynpm_state = &r600_pm_get_dynpm_state,
+       .pre_page_flip = &r100_pre_page_flip,
+       .page_flip = &rv770_page_flip,
+       .post_page_flip = &r100_post_page_flip,
 };

 static struct radeon_asic evergreen_asic = {
@@ -749,6 +788,9 @@ static struct radeon_asic evergreen_asic = {
        .pm_finish = &evergreen_pm_finish,
        .pm_init_profile = &r600_pm_init_profile,
        .pm_get_dynpm_state = &r600_pm_get_dynpm_state,
+       .pre_page_flip = &r100_pre_page_flip,
+       .page_flip = &evergreen_page_flip,
+       .post_page_flip = &r100_post_page_flip,
 };

 int radeon_asic_init(struct radeon_device *rdev)
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h 
b/drivers/gpu/drm/radeon/radeon_asic.h
index 7409882..c531850 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.h
+++ b/drivers/gpu/drm/radeon/radeon_asic.h
@@ -130,6 +130,9 @@ extern void r100_pm_prepare(struct radeon_device *rdev);
 extern void r100_pm_finish(struct radeon_device *rdev);
 extern void r100_pm_init_profile(struct radeon_device *rdev);
 extern void r100_pm_get_dynpm_state(struct radeon_device *rdev);
+extern void r100_pre_page_flip(struct radeon_device *rdev, int crtc);
+extern void r100_page_flip(struct drm_crtc *crtc, u64 crtc_base);
+extern void r100_post_page_flip(struct radeon_device *rdev, int crtc);

 /*
  * r200,rv250,rs300,rv280
@@ -205,6 +208,7 @@ void rs600_hpd_set_polarity(struct radeon_device *rdev,
 extern void rs600_pm_misc(struct radeon_device *rdev);
 extern void rs600_pm_prepare(struct radeon_device *rdev);
 extern void rs600_pm_finish(struct radeon_device *rdev);
+extern void rs600_page_flip(struct drm_crtc *crtc, u64 crtc_base);

 /*
  * rs690,rs740
@@ -278,6 +282,7 @@ extern void r600_pm_misc(struct radeon_device *rdev);
 extern void r600_pm_init_profile(struct radeon_device *rdev);
 extern void rs780_pm_init_profile(struct radeon_device *rdev);
 extern void r600_pm_get_dynpm_state(struct radeon_device *rdev);
+extern void r600_page_flip(struct drm_crtc *crtc, u64 crtc_base);

 /*
  * rv770,rv730,rv710,rv740
@@ -287,6 +292,7 @@ void rv770_fini(struct radeon_device *rdev);
 int rv770_suspend(struct radeon_device *rdev);
 int rv770_resume(struct radeon_device *rdev);
 extern void rv770_pm_misc(struct radeon_device *rdev);
+extern void rv770_page_flip(struct drm_crtc *crtc, u64 crtc_base);

 /*
  * evergreen
@@ -314,5 +320,6 @@ extern int evergreen_cs_parse(struct radeon_cs_parser *p);
 extern void evergreen_pm_misc(struct radeon_device *rdev);
 extern void evergreen_pm_prepare(struct radeon_device *rdev);
 extern void evergreen_pm_finish(struct radeon_device *rdev);
+extern void evergreen_page_flip(struct drm_crtc *crtc, u64 crtc_base);

 #endif
diff --git a/drivers/gpu/drm/radeon/radeon_display.c 
b/drivers/gpu/drm/radeon/radeon_display.c
index 0383631..345828a 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -183,12 +183,113 @@ static void radeon_crtc_destroy(struct drm_crtc *crtc)
        kfree(radeon_crtc);
 }

+void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id)
+{
+       struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
+       struct drm_pending_vblank_event *e = radeon_crtc->event;
+       struct timeval now;
+       unsigned long flags;
+
+       spin_lock_irqsave(&rdev->ddev->event_lock, flags);
+       if (radeon_crtc->flip_old_bo &&
+           radeon_fence_signaled(radeon_crtc->pflip_fence)) {
+
+               drm_vblank_put(rdev->ddev, crtc_id);
+
+               /* wakeup userspace */
+               e = radeon_crtc->event;
+               radeon_crtc->event = NULL;
+               if (e) {
+                       do_gettimeofday(&now);
+                       e->event.sequence = drm_vblank_count(rdev->ddev, 
crtc_id);
+                       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);
+               }
+       }
+       spin_unlock_irqrestore(&rdev->ddev->event_lock, flags);
+}
+
+static int radeon_crtc_page_flip(struct drm_crtc *crtc,
+                                struct drm_framebuffer *fb,
+                                struct drm_pending_vblank_event *event)
+{
+       struct drm_device *dev = crtc->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+       struct radeon_framebuffer *old_radeon_fb;
+       struct radeon_framebuffer *new_radeon_fb;
+       struct drm_gem_object *obj;
+       struct radeon_bo *rbo;
+       struct radeon_fence *fence;
+       unsigned long flags;
+       u64 base;
+       int r;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       if (radeon_crtc->flip_old_bo) {
+               spin_unlock_irqrestore(&dev->event_lock, flags);
+               return -EBUSY;
+       }
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       old_radeon_fb = to_radeon_framebuffer(crtc->fb);
+       new_radeon_fb = to_radeon_framebuffer(fb);
+
+       /* pin the new buffer */
+       obj = new_radeon_fb->obj;
+       rbo = obj->driver_private;
+       r = radeon_bo_reserve(rbo, false);
+       if (unlikely(r != 0))
+               return r;
+       r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &base);
+       if (unlikely(r != 0)) {
+               radeon_bo_unreserve(rbo);
+               return -EINVAL;
+       }
+       radeon_bo_unreserve(rbo);
+
+       /* update crtc fb */
+       crtc->fb = fb;
+
+       r = drm_vblank_get(dev, radeon_crtc->crtc_id);
+       if (r)
+               return r;
+
+       /* 64 ought to cover us */
+       r = radeon_ring_lock(rdev, 64);
+       if (r)
+               return r;
+
+       /* set the proper interrupt */
+       radeon_pre_page_flip(rdev, radeon_crtc->crtc_id);
+       /* put the flip on the ring */
+       radeon_page_flip(crtc, base);
+       /* emit a fence */
+       radeon_fence_create(rdev, &fence);
+       radeon_fence_emit(rdev, fence);
+       radeon_ring_unlock_commit(rdev);
+
+       /* schedule unpin of the old buffer */
+       obj = old_radeon_fb->obj;
+       rbo = obj->driver_private;
+       spin_lock_irqsave(&dev->event_lock, flags);
+       radeon_crtc->flip_old_bo = rbo;
+       radeon_crtc->event = event;
+       radeon_crtc->pflip_fence = fence;
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       return 0;
+}
+
 static const struct drm_crtc_funcs radeon_crtc_funcs = {
        .cursor_set = radeon_crtc_cursor_set,
        .cursor_move = radeon_crtc_cursor_move,
        .gamma_set = radeon_crtc_gamma_set,
        .set_config = drm_crtc_helper_set_config,
        .destroy = radeon_crtc_destroy,
+       .page_flip = radeon_crtc_page_flip,
 };

 static void radeon_crtc_init(struct drm_device *dev, int index)
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c 
b/drivers/gpu/drm/radeon/radeon_drv.c
index c20669b..0d2bcd7 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -47,7 +47,7 @@
  * - 2.4.0 - add crtc id query
  * - 2.5.0 - add get accel 2 to work around ddx breakage for evergreen
  * - 2.6.0 - add tiling config query (r6xx+), add initial HiZ support 
(r300->r500)
- *   2.7.0 - fixups for r600 2D tiling support. (no external ABI change), add 
eg dyn gpr regs
+ *   2.7.0 - fixups for r600 2D tiling support. (no external ABI change), add 
eg dyn gpr regs, pageflip support
  */
 #define KMS_DRIVER_MAJOR       2
 #define KMS_DRIVER_MINOR       7
diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c 
b/drivers/gpu/drm/radeon/radeon_irq_kms.c
index d2c9647..fa034c8 100644
--- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
@@ -59,12 +59,43 @@ static void radeon_hotplug_work_func(struct work_struct 
*work)
        drm_helper_hpd_irq_event(dev);
 }

+/*
+ * Handle unpin events outside the interrupt handler proper.
+ */
+static void radeon_unpin_work_func(struct work_struct *work)
+{
+       struct radeon_device *rdev = container_of(work, struct radeon_device,
+                                                 unpin_work);
+       struct radeon_crtc *radeon_crtc;
+       int i, r;
+
+       for (i = 0; i < rdev->num_crtc; i++) {
+               radeon_crtc = rdev->mode_info.crtcs[i];
+               if (radeon_crtc->flip_old_bo &&
+                   radeon_fence_signaled(radeon_crtc->pflip_fence)) {
+                       radeon_post_page_flip(rdev, radeon_crtc->crtc_id);
+                       /* unpin of the old buffer */
+                       r = radeon_bo_reserve(radeon_crtc->flip_old_bo, false);
+                       if (likely(r == 0)) {
+                               r = radeon_bo_unpin(radeon_crtc->flip_old_bo);
+                               if (unlikely(r != 0)) {
+                                       DRM_ERROR("failed to unpin buffer after 
flip\n");
+                               }
+                               radeon_bo_unreserve(radeon_crtc->flip_old_bo);
+                               radeon_crtc->flip_old_bo = NULL;
+                               radeon_fence_unref(&radeon_crtc->pflip_fence);
+                       }
+               }
+       }
+}
+
 void radeon_driver_irq_preinstall_kms(struct drm_device *dev)
 {
        struct radeon_device *rdev = dev->dev_private;
        unsigned i;

        INIT_WORK(&rdev->hotplug_work, radeon_hotplug_work_func);
+       INIT_WORK(&rdev->unpin_work, radeon_unpin_work_func);

        /* Disable *all* interrupts */
        rdev->irq.sw_int = false;
@@ -179,3 +210,34 @@ void radeon_irq_kms_sw_irq_put(struct radeon_device *rdev)
        spin_unlock_irqrestore(&rdev->irq.sw_lock, irqflags);
 }

+void radeon_irq_kms_pflip_irq_get(struct radeon_device *rdev, int crtc)
+{
+       unsigned long irqflags;
+
+       if (crtc < 0 || crtc >= rdev->num_crtc)
+               return;
+
+       spin_lock_irqsave(&rdev->irq.pflip_lock[crtc], irqflags);
+       if (rdev->ddev->irq_enabled && (++rdev->irq.pflip_refcount[crtc] == 1)) 
{
+               rdev->irq.pflip[crtc] = true;
+               radeon_irq_set(rdev);
+       }
+       spin_unlock_irqrestore(&rdev->irq.pflip_lock[crtc], irqflags);
+}
+
+void radeon_irq_kms_pflip_irq_put(struct radeon_device *rdev, int crtc)
+{
+       unsigned long irqflags;
+
+       if (crtc < 0 || crtc >= rdev->num_crtc)
+               return;
+
+       spin_lock_irqsave(&rdev->irq.pflip_lock[crtc], irqflags);
+       BUG_ON(rdev->ddev->irq_enabled && rdev->irq.pflip_refcount[crtc] <= 0);
+       if (rdev->ddev->irq_enabled && (--rdev->irq.pflip_refcount[crtc] == 0)) 
{
+               rdev->irq.pflip[crtc] = false;
+               radeon_irq_set(rdev);
+       }
+       spin_unlock_irqrestore(&rdev->irq.pflip_lock[crtc], irqflags);
+}
+
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h 
b/drivers/gpu/drm/radeon/radeon_mode.h
index 61b9243..edaf692 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -278,6 +278,10 @@ struct radeon_crtc {
        fixed20_12 hsc;
        struct drm_display_mode native_mode;
        int pll_id;
+       /* page flipping */
+       struct radeon_bo *flip_old_bo;
+       struct drm_pending_vblank_event *event;
+       struct radeon_fence *pflip_fence;
 };

 struct radeon_encoder_primary_dac {
@@ -653,4 +657,7 @@ int radeon_fbdev_total_size(struct radeon_device *rdev);
 bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo 
*robj);

 void radeon_fb_output_poll_changed(struct radeon_device *rdev);
+
+void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id);
+
 #endif
diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c
index 834a3a9..d7e29d8 100644
--- a/drivers/gpu/drm/radeon/rs600.c
+++ b/drivers/gpu/drm/radeon/rs600.c
@@ -46,6 +46,22 @@
 void rs600_gpu_init(struct radeon_device *rdev);
 int rs600_mc_wait_for_idle(struct radeon_device *rdev);

+void rs600_page_flip(struct drm_crtc *crtc, u64 crtc_base)
+{
+       struct drm_device *dev = crtc->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+#if 0
+       radeon_ring_write(rdev,
+                         PACKET0(AVIVO_D1GRPH_SECONDARY_SURFACE_ADDRESS + 
radeon_crtc->crtc_offset, 0));
+       radeon_ring_write(rdev, (u32)crtc_base);
+#endif
+       radeon_ring_write(rdev,
+                         PACKET0(AVIVO_D1GRPH_PRIMARY_SURFACE_ADDRESS + 
radeon_crtc->crtc_offset, 0));
+       radeon_ring_write(rdev, (u32)crtc_base);
+
+}
+
 void rs600_pm_misc(struct radeon_device *rdev)
 {
        int requested_index = rdev->pm.requested_power_state_index;
@@ -515,10 +531,12 @@ int rs600_irq_set(struct radeon_device *rdev)
        if (rdev->irq.gui_idle) {
                tmp |= S_000040_GUI_IDLE(1);
        }
-       if (rdev->irq.crtc_vblank_int[0]) {
+       if (rdev->irq.crtc_vblank_int[0] ||
+           rdev->irq.pflip[0]) {
                mode_int |= S_006540_D1MODE_VBLANK_INT_MASK(1);
        }
-       if (rdev->irq.crtc_vblank_int[1]) {
+       if (rdev->irq.crtc_vblank_int[1] ||
+           rdev->irq.pflip[1]) {
                mode_int |= S_006540_D2MODE_VBLANK_INT_MASK(1);
        }
        if (rdev->irq.hpd[0]) {
@@ -589,6 +607,7 @@ int rs600_irq_process(struct radeon_device *rdev)
 {
        u32 status, msi_rearm;
        bool queue_hotplug = false;
+       bool queue_unpin = false;

        /* reset gui idle ack.  the status bit is broken */
        rdev->irq.gui_idle_acked = false;
@@ -609,14 +628,26 @@ int rs600_irq_process(struct radeon_device *rdev)
                }
                /* Vertical blank interrupts */
                if 
(G_007EDC_LB_D1_VBLANK_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) {
-                       drm_handle_vblank(rdev->ddev, 0);
-                       rdev->pm.vblank_sync = true;
-                       wake_up(&rdev->irq.vblank_queue);
+                       if (rdev->irq.crtc_vblank_int[0]) {
+                               drm_handle_vblank(rdev->ddev, 0);
+                               rdev->pm.vblank_sync = true;
+                               wake_up(&rdev->irq.vblank_queue);
+                       }
+                       if (rdev->irq.pflip[0]) {
+                               radeon_crtc_handle_flip(rdev, 0);
+                               queue_unpin = true;
+                       }
                }
                if 
(G_007EDC_LB_D2_VBLANK_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) {
-                       drm_handle_vblank(rdev->ddev, 1);
-                       rdev->pm.vblank_sync = true;
-                       wake_up(&rdev->irq.vblank_queue);
+                       if (rdev->irq.crtc_vblank_int[1]) {
+                               drm_handle_vblank(rdev->ddev, 1);
+                               rdev->pm.vblank_sync = true;
+                               wake_up(&rdev->irq.vblank_queue);
+                       }
+                       if (rdev->irq.pflip[1]) {
+                               radeon_crtc_handle_flip(rdev, 1);
+                               queue_unpin = true;
+                       }
                }
                if 
(G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) {
                        queue_hotplug = true;
@@ -632,6 +663,8 @@ int rs600_irq_process(struct radeon_device *rdev)
        rdev->irq.gui_idle_acked = false;
        if (queue_hotplug)
                queue_work(rdev->wq, &rdev->hotplug_work);
+       if (queue_unpin)
+               queue_work(rdev->wq, &rdev->unpin_work);
        if (rdev->msi_enabled) {
                switch (rdev->family) {
                case CHIP_RS600:
diff --git a/drivers/gpu/drm/radeon/rs600d.h b/drivers/gpu/drm/radeon/rs600d.h
index a27c13a..ebe478b 100644
--- a/drivers/gpu/drm/radeon/rs600d.h
+++ b/drivers/gpu/drm/radeon/rs600d.h
@@ -28,6 +28,19 @@
 #ifndef __RS600D_H__
 #define __RS600D_H__

+/* PM4 */
+#define        PACKET_TYPE0    0
+#define        PACKET_TYPE1    1
+#define        PACKET_TYPE2    2
+#define        PACKET_TYPE3    3
+
+#define PACKET0(reg, n)        ((PACKET_TYPE0 << 30) |                         
\
+                        (((reg) >> 2) & 0xFFFF) |                      \
+                        ((n) & 0x3FFF) << 16)
+#define PACKET3(op, n) ((PACKET_TYPE3 << 30) |                         \
+                        (((op) & 0xFF) << 8) |                         \
+                        ((n) & 0x3FFF) << 16)
+
 /* Registers */
 #define R_000040_GEN_INT_CNTL                        0x000040
 #define   S_000040_SCRATCH_INT_MASK(x)                 (((x) & 0x1) << 18)
diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c
index 245374e..09fa5cd 100644
--- a/drivers/gpu/drm/radeon/rv770.c
+++ b/drivers/gpu/drm/radeon/rv770.c
@@ -42,6 +42,40 @@
 static void rv770_gpu_init(struct radeon_device *rdev);
 void rv770_fini(struct radeon_device *rdev);

+void rv770_page_flip(struct drm_crtc *crtc, u64 crtc_base)
+{
+       struct drm_device *dev = crtc->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+#if 0
+       if (radeon_crtc->crtc_id) {
+               radeon_ring_write(rdev,
+                                 
PACKET0(D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, 0));
+               radeon_ring_write(rdev, upper_32_bits(crtc_base));
+       } else {
+               radeon_ring_write(rdev,
+                                 
PACKET0(D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, 0));
+               radeon_ring_write(rdev, upper_32_bits(crtc_base));
+       }
+       radeon_ring_write(rdev,
+                         PACKET0(D1GRPH_SECONDARY_SURFACE_ADDRESS + 
radeon_crtc->crtc_offset, 0));
+       radeon_ring_write(rdev, (u32)crtc_base);
+#endif
+       if (radeon_crtc->crtc_id) {
+               radeon_ring_write(rdev,
+                                 PACKET0(D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, 
0));
+               radeon_ring_write(rdev, upper_32_bits(crtc_base));
+       } else {
+               radeon_ring_write(rdev,
+                                 PACKET0(D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, 
0));
+               radeon_ring_write(rdev, upper_32_bits(crtc_base));
+       }
+       radeon_ring_write(rdev,
+                         PACKET0(D1GRPH_PRIMARY_SURFACE_ADDRESS + 
radeon_crtc->crtc_offset, 0));
+       radeon_ring_write(rdev, (u32)crtc_base);
+
+}
+
 /* get temperature in millidegrees */
 u32 rv770_get_temp(struct radeon_device *rdev)
 {
diff --git a/drivers/gpu/drm/radeon/rv770d.h b/drivers/gpu/drm/radeon/rv770d.h
index b7a5a20..aa832cb 100644
--- a/drivers/gpu/drm/radeon/rv770d.h
+++ b/drivers/gpu/drm/radeon/rv770d.h
@@ -351,4 +351,43 @@

 #define        SRBM_STATUS                                     0x0E50

+#define D1GRPH_PRIMARY_SURFACE_ADDRESS                    0x6110
+#define D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH               0x6914
+#define D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH               0x6114
+#define D1GRPH_SECONDARY_SURFACE_ADDRESS                  0x6118
+#define D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH             0x691c
+#define D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH             0x611c
+#define D1GRPH_UPDATE                                     0x6144
+#       define D1GRPH_SURFACE_UPDATE_PENDING              (1 << 2)
+#       define D1GRPH_UPDATE_LOCK                         (1 << 16)
+
+/* PM4 */
+#define        PACKET_TYPE0    0
+#define        PACKET_TYPE1    1
+#define        PACKET_TYPE2    2
+#define        PACKET_TYPE3    3
+
+#define PACKET0(reg, n)        ((PACKET_TYPE0 << 30) |                         
\
+                        (((reg) >> 2) & 0xFFFF) |                      \
+                        ((n) & 0x3FFF) << 16)
+#define PACKET3(op, n) ((PACKET_TYPE3 << 30) |                         \
+                        (((op) & 0xFF) << 8) |                         \
+                        ((n) & 0x3FFF) << 16)
+
+#define        PACKET3_WAIT_REG_MEM                            0x3C
+#              define PACKET3_WAIT_REG_MEM_ENGINE(x)    ((x) << 8)
+#              define ENGINE_ME                         0
+#              define ENGINE_PFP                        1
+#              define PACKET3_WAIT_REG_MEM_MEM_SPACE(x) ((x) << 4)
+#              define MEM_SPACE_REG                     0
+#              define MEM_SPACE_MEM                     1
+#              define PACKET3_WAIT_REG_MEM_FUNCTION(x)  ((x) << 0)
+#              define FUNCTION_ALWAYS                   0
+#              define FUNCTION_LT                       1
+#              define FUNCTION_LTE                      2
+#              define FUNCTION_E                        3
+#              define FUNCTION_NE                       4
+#              define FUNCTION_GTE                      5
+#              define FUNCTION_GT                       6
+
 #endif
-- 
1.7.1.1

Reply via email to