From: Michel Dänzer <michel.daenzer@amd.com>
Date: Fri Sep 2 11:08:28 2016 +0900
Subject: [PATCH 20/20]Make TearFree effective with PRIME slave scanout
Patch-mainline: Upstream
Git-repo: git://anongit.freedesktop.org/xorg/driver/xf86-video-ati
Git-commit: 38797a33117222dadbc89e5f21ed8cd5deef9bea
References: bsc#990066
Signed-off-by: Max Staudt <mstaudt@suse.de>

TearFree can now prevent tearing with any possible display
configuration.

Note that there may still be inter-GPU tearing if the primary GPU uses
a different driver.

v2:
* Also test dirty->slave_dst in radeon_prime_scanout_do_update

Reviewed-by: Alex Deucher <alexander.deucher@amd.com> [v1]
---
 src/drmmode_display.c |  33 ++++++++++++---
 src/drmmode_display.h |   1 +
 src/radeon_kms.c      | 111 ++++++++++++++++++++++++++++++++++++++++++++++----
 3 files changed, 132 insertions(+), 13 deletions(-)

diff --git a/src/drmmode_display.c b/src/drmmode_display.c
index 44a4032..44615d3 100644
--- a/src/drmmode_display.c
+++ b/src/drmmode_display.c
@@ -524,10 +524,19 @@ drmmode_crtc_scanout_destroy(drmmode_ptr drmmode,
 static void
 drmmode_crtc_scanout_free(drmmode_crtc_private_ptr drmmode_crtc)
 {
-	drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode,
-				     &drmmode_crtc->scanout[0]);
-	drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode,
-				     &drmmode_crtc->scanout[1]);
+	if (drmmode_crtc->flip_pending) {
+		drmmode_crtc->scanout_destroy[0] = drmmode_crtc->scanout[0];
+		drmmode_crtc->scanout[0].pixmap = NULL;
+		drmmode_crtc->scanout[0].bo = NULL;
+		drmmode_crtc->scanout_destroy[1] = drmmode_crtc->scanout[1];
+		drmmode_crtc->scanout[1].pixmap = NULL;
+		drmmode_crtc->scanout[1].bo = NULL;
+	} else {
+		drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode,
+					     &drmmode_crtc->scanout[0]);
+		drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode,
+					     &drmmode_crtc->scanout[1]);
+	}

 	if (drmmode_crtc->scanout_damage) {
 		DamageDestroy(drmmode_crtc->scanout_damage);
@@ -1116,11 +1125,12 @@ static Bool
 drmmode_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix)
 {
 	drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+	RADEONInfoPtr info = RADEONPTR(crtc->scrn);

 	if (!ppix) {
 		if (crtc->randr_crtc->scanout_pixmap)
 			PixmapStopDirtyTracking(crtc->randr_crtc->scanout_pixmap,
-						drmmode_crtc->scanout[0].pixmap);
+						drmmode_crtc->scanout[drmmode_crtc->scanout_id].pixmap);
 		drmmode_crtc_scanout_free(drmmode_crtc);
 		return TRUE;
 	}
@@ -1130,6 +1140,14 @@ drmmode_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix)
 					 ppix->drawable.height))
 		return FALSE;

+	if (info->tear_free &&
+	    !drmmode_crtc_scanout_create(crtc, &drmmode_crtc->scanout[1],
+					 ppix->drawable.width,
+					 ppix->drawable.height)) {
+		drmmode_crtc_scanout_free(drmmode_crtc);
+		return FALSE;
+	}
+
 #ifdef HAS_DIRTYTRACKING_ROTATION
 	PixmapStartDirtyTracking(ppix, drmmode_crtc->scanout[0].pixmap,
 				 0, 0, 0, 0, RR_Rotate_0);
@@ -2196,6 +2214,11 @@ drmmode_clear_pending_flip(xf86CrtcPtr crtc)

 		drmmode_crtc_dpms(crtc, drmmode_crtc->pending_dpms_mode);
 	}
+
+	drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode,
+				     &drmmode_crtc->scanout_destroy[0]);
+	drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode,
+				     &drmmode_crtc->scanout_destroy[1]);
 }

 static void
diff --git a/src/drmmode_display.h b/src/drmmode_display.h
index 5df9773..7602eb8 100644
--- a/src/drmmode_display.h
+++ b/src/drmmode_display.h
@@ -84,6 +84,7 @@ typedef struct {
     struct radeon_bo *cursor_bo;
     struct drmmode_scanout rotate;
     struct drmmode_scanout scanout[2];
+    struct drmmode_scanout scanout_destroy[2];
     DamagePtr scanout_damage;
     RegionRec scanout_last_region;
     unsigned scanout_id;
diff --git a/src/radeon_kms.c b/src/radeon_kms.c
index bcaa024..9bcf657 100644
--- a/src/radeon_kms.c
+++ b/src/radeon_kms.c
@@ -597,31 +597,56 @@ slave_has_sync_shared_pixmap(ScrnInfoPtr scrn, PixmapDirtyUpdatePtr dirty)
     return slave_scrn->driverName == scrn->driverName;
 }

-void
-radeon_prime_scanout_update_handler(xf86CrtcPtr crtc, uint32_t frame, uint64_t usec,
-				    void *event_data)
+static Bool
+radeon_prime_scanout_do_update(xf86CrtcPtr crtc, unsigned scanout_id)
 {
     ScrnInfoPtr scrn = crtc->scrn;
     ScreenPtr screen = scrn->pScreen;
+    RADEONInfoPtr info = RADEONPTR(scrn);
     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
     PixmapPtr scanoutpix = crtc->randr_crtc->scanout_pixmap;
     PixmapDirtyUpdatePtr dirty;
+    Bool ret = FALSE;

     xorg_list_for_each_entry(dirty, &screen->pixmap_dirty_list, ent) {
-	if (dirty->src == scanoutpix &&
-	    dirty->slave_dst == drmmode_crtc->scanout[0].pixmap) {
+	if (dirty->src == scanoutpix && dirty->slave_dst ==
+	    drmmode_crtc->scanout[scanout_id ^ info->tear_free].pixmap) {
 	    RegionPtr region;

 	    if (master_has_sync_shared_pixmap(scrn, dirty))
 		radeon_sync_shared_pixmap(dirty);

 	    region = dirty_region(dirty);
+	    if (RegionNil(region))
+		goto destroy;
+
+	    if (info->tear_free) {
+		RegionTranslate(region, crtc->x, crtc->y);
+		radeon_sync_scanout_pixmaps(crtc, region, scanout_id);
+		radeon_cs_flush_indirect(scrn);
+		RegionCopy(&drmmode_crtc->scanout_last_region, region);
+		RegionTranslate(region, -crtc->x, -crtc->y);
+		dirty->slave_dst = drmmode_crtc->scanout[scanout_id].pixmap;
+	    }
+
 	    redisplay_dirty(dirty, region);
+	    ret = TRUE;
+	destroy:
 	    RegionDestroy(region);
 	    break;
 	}
     }

+    return ret;
+}
+
+void
+radeon_prime_scanout_update_handler(xf86CrtcPtr crtc, uint32_t frame, uint64_t usec,
+				    void *event_data)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+
+    radeon_prime_scanout_do_update(crtc, 0);
     drmmode_crtc->scanout_update_pending = FALSE;
 }

@@ -679,8 +704,74 @@ radeon_prime_scanout_update(PixmapDirtyUpdatePtr dirty)
 }

 static void
+radeon_prime_scanout_flip_abort(xf86CrtcPtr crtc, void *event_data)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = event_data;
+
+    drmmode_crtc->scanout_update_pending = FALSE;
+    drmmode_clear_pending_flip(crtc);
+}
+
+static void
+radeon_prime_scanout_flip(PixmapDirtyUpdatePtr ent)
+{
+    ScreenPtr screen = ent->slave_dst->drawable.pScreen;
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
+    xf86CrtcPtr crtc = NULL;
+    drmmode_crtc_private_ptr drmmode_crtc = NULL;
+    uintptr_t drm_queue_seq;
+    unsigned scanout_id;
+    int c;
+
+    /* Find the CRTC which is scanning out from this slave pixmap */
+    for (c = 0; c < xf86_config->num_crtc; c++) {
+	crtc = xf86_config->crtc[c];
+	drmmode_crtc = crtc->driver_private;
+	scanout_id = drmmode_crtc->scanout_id;
+	if (drmmode_crtc->scanout[scanout_id].pixmap == ent->slave_dst)
+	    break;
+    }
+
+    if (c == xf86_config->num_crtc ||
+	!crtc->enabled ||
+	drmmode_crtc->scanout_update_pending ||
+	!drmmode_crtc->scanout[drmmode_crtc->scanout_id].pixmap ||
+	drmmode_crtc->pending_dpms_mode != DPMSModeOn)
+	return;
+
+    scanout_id = drmmode_crtc->scanout_id ^ 1;
+    if (!radeon_prime_scanout_do_update(crtc, scanout_id))
+	return;
+
+    drm_queue_seq = radeon_drm_queue_alloc(crtc,
+					   RADEON_DRM_QUEUE_CLIENT_DEFAULT,
+					   RADEON_DRM_QUEUE_ID_DEFAULT,
+					   drmmode_crtc, NULL,
+					   radeon_prime_scanout_flip_abort);
+    if (drm_queue_seq == RADEON_DRM_QUEUE_ERROR) {
+	xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+		   "Allocating DRM event queue entry failed for PRIME flip.\n");
+	return;
+    }
+
+    if (drmModePageFlip(drmmode_crtc->drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
+			drmmode_crtc->scanout[scanout_id].fb_id,
+			DRM_MODE_PAGE_FLIP_EVENT, (void*)drm_queue_seq)) {
+	xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue failed in %s: %s\n",
+		   __func__, strerror(errno));
+	return;
+    }
+
+    drmmode_crtc->scanout_id = scanout_id;
+    drmmode_crtc->scanout_update_pending = TRUE;
+    drmmode_crtc->flip_pending = TRUE;
+}
+
+static void
 radeon_dirty_update(ScrnInfoPtr scrn)
 {
+	RADEONInfoPtr info = RADEONPTR(scrn);
 	ScreenPtr screen = scrn->pScreen;
 	PixmapDirtyUpdatePtr ent;
 	RegionPtr region;
@@ -700,10 +791,14 @@ radeon_dirty_update(ScrnInfoPtr scrn)

 			region = dirty_region(region_ent);

-			if (RegionNotEmpty(region))
-				radeon_prime_scanout_update(ent);
-			else
+			if (RegionNotEmpty(region)) {
+				if (info->tear_free)
+					radeon_prime_scanout_flip(ent);
+				else
+					radeon_prime_scanout_update(ent);
+			} else {
 				DamageEmpty(region_ent->damage);
+			}

 			RegionDestroy(region);
 		} else {
