diff mbox

[2/2] drm/radeon/kms: add pageflip ioctl support

Message ID 1288081832-2372-3-git-send-email-alexdeucher@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Alex Deucher Oct. 26, 2010, 8:30 a.m. UTC
None
diff mbox

Patch

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