drm/radeon: Add radeon_test_syncing function v2

Tests syncing between all rings by using
semaphores and fences.

v2: use radeon_testing as a bit flag rather than on/off switch
    this allow to test for one thing at a time (bo_move or semaphore
    test). It kind of break the usage if user wheren't using 1
    for bo move test but as it's a test feature i believe it's ok.

Signed-off-by: Christian König <deathsimple@vodafone.de>
Reviewed-by: Jerome Glisse <jglisse@redhat.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 76c58e9e..fbe902b 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -913,6 +913,10 @@
  * Testing
  */
 void radeon_test_moves(struct radeon_device *rdev);
+void radeon_test_ring_sync(struct radeon_device *rdev,
+			   struct radeon_cp *cpA,
+			   struct radeon_cp *cpB);
+void radeon_test_syncing(struct radeon_device *rdev);
 
 
 /*
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 023c156..fa36b53 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -823,9 +823,12 @@
 		if (r)
 			return r;
 	}
-	if (radeon_testing) {
+	if ((radeon_testing & 1)) {
 		radeon_test_moves(rdev);
 	}
+	if ((radeon_testing & 2)) {
+		radeon_test_syncing(rdev);
+	}
 	if (radeon_benchmarking) {
 		radeon_benchmark(rdev, radeon_benchmarking);
 	}
diff --git a/drivers/gpu/drm/radeon/radeon_test.c b/drivers/gpu/drm/radeon/radeon_test.c
index 160e7df..5f4d31e 100644
--- a/drivers/gpu/drm/radeon/radeon_test.c
+++ b/drivers/gpu/drm/radeon/radeon_test.c
@@ -234,3 +234,91 @@
 		printk(KERN_WARNING "Error while testing BO move.\n");
 	}
 }
+
+void radeon_test_ring_sync(struct radeon_device *rdev,
+			   struct radeon_cp *cpA,
+			   struct radeon_cp *cpB)
+{
+	struct radeon_fence *fence = NULL;
+	struct radeon_semaphore *semaphore = NULL;
+	int ringA = radeon_ring_index(rdev, cpA);
+	int ringB = radeon_ring_index(rdev, cpB);
+	int r;
+
+	r = radeon_fence_create(rdev, &fence, ringA);
+	if (r) {
+		DRM_ERROR("Failed to create sync fence\n");
+		goto out_cleanup;
+	}
+
+	r = radeon_semaphore_create(rdev, &semaphore);
+	if (r) {
+		DRM_ERROR("Failed to create semaphore\n");
+		goto out_cleanup;
+	}
+
+	r = radeon_ring_lock(rdev, cpA, 64);
+	if (r) {
+		DRM_ERROR("Failed to lock ring %d\n", ringA);
+		goto out_cleanup;
+	}
+	radeon_semaphore_emit_wait(rdev, ringA, semaphore);
+	radeon_fence_emit(rdev, fence);
+	radeon_ring_unlock_commit(rdev, cpA);
+
+	mdelay(1000);
+
+	if (radeon_fence_signaled(fence)) {
+		DRM_ERROR("Fence signaled without waiting for semaphore.\n");
+		goto out_cleanup;
+	}
+
+	r = radeon_ring_lock(rdev, cpB, 64);
+	if (r) {
+		DRM_ERROR("Failed to lock ring %d\n", ringB);
+		goto out_cleanup;
+	}
+	radeon_semaphore_emit_signal(rdev, ringB, semaphore);
+	radeon_ring_unlock_commit(rdev, cpB);
+
+	r = radeon_fence_wait(fence, false);
+	if (r) {
+		DRM_ERROR("Failed to wait for sync fence\n");
+		goto out_cleanup;
+	}
+
+	DRM_INFO("Syncing between rings %d and %d seems to work.\n", ringA, ringB);
+
+out_cleanup:
+	if (semaphore)
+		radeon_semaphore_free(rdev, semaphore);
+
+	if (fence)
+		radeon_fence_unref(&fence);
+
+	if (r)
+		printk(KERN_WARNING "Error while testing ring sync (%d).\n", r);
+}
+
+void radeon_test_syncing(struct radeon_device *rdev)
+{
+	int i, j;
+
+	for (i = 1; i < RADEON_NUM_RINGS; ++i) {
+		struct radeon_cp *cpA = &rdev->cp[i];
+		if (!cpA->ready)
+			continue;
+
+		for (j = 0; j < i; ++j) {
+			struct radeon_cp *cpB = &rdev->cp[j];
+			if (!cpB->ready)
+				continue;
+
+			DRM_INFO("Testing syncing between rings %d and %d\n", i, j);
+			radeon_test_ring_sync(rdev, cpA, cpB);
+
+			DRM_INFO("Testing syncing between rings %d and %d\n", j, i);
+			radeon_test_ring_sync(rdev, cpB, cpA);
+		}
+	}
+}