From 670f328dcf69bd89a6654bd029d25140e70759a9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
Date: Sat, 21 Nov 2009 13:33:02 +0100
Subject: [PATCH] drm/radeon/kms/pm: add non-aggressive dynamic engine downclocking
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
---
 drivers/gpu/drm/radeon/radeon.h    |   10 ++++++
 drivers/gpu/drm/radeon/radeon_pm.c |   64 ++++++++++++++++++++++++++++++++++++
 2 files changed, 74 insertions(+), 0 deletions(-)

diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 9f0bd98..2183eb4 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -550,7 +550,17 @@ struct radeon_wb {
  * Equation between gpu/memory clock and available bandwidth is hw dependent
  * (type of memory, bus size, efficiency, ...)
  */
+enum radeon_pm_action {
+	RADEON_PM_ACTION_NONE,
+	RADEON_PM_ACTION_DOWNCLOCK,
+	RADEON_PM_ACTION_UPCLOCK
+};
 struct radeon_pm {
+	struct workqueue_struct	*wq;
+	struct delayed_work		reclock_work;
+	enum radeon_pm_action	planned_action;
+	unsigned long	action_timeout;
+	bool 			downclocked;
 	fixed20_12		max_bandwidth;
 	fixed20_12		igp_sideport_mclk;
 	fixed20_12		igp_system_mclk;
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
index 46146c6..a7dc92b 100644
--- a/drivers/gpu/drm/radeon/radeon_pm.c
+++ b/drivers/gpu/drm/radeon/radeon_pm.c
@@ -22,7 +22,11 @@
 #include "drmP.h"
 #include "radeon.h"
 
+#define RADEON_RECLOCK_LOOP_MS 100
+#define RADEON_RECLOCK_DELAY_MS 200
+
 int radeon_debugfs_pm_init(struct radeon_device *rdev);
+void radeon_pm_reclock_work_handler(struct work_struct *work);
 
 int radeon_pm_init(struct radeon_device *rdev)
 {
@@ -30,9 +34,69 @@ int radeon_pm_init(struct radeon_device *rdev)
 		DRM_ERROR("Failed to register debugfs file for CP !\n");
 	}
 
+	if (radeon_dynclks != -1 && radeon_dynclks) {
+		rdev->pm.planned_action = RADEON_PM_ACTION_NONE;
+		rdev->pm.downclocked = false;
+
+		rdev->pm.wq = create_workqueue("radeon");
+		INIT_DELAYED_WORK(&rdev->pm.reclock_work, radeon_pm_reclock_work_handler);
+		queue_delayed_work(rdev->pm.wq, &rdev->pm.reclock_work, msecs_to_jiffies(RADEON_RECLOCK_LOOP_MS));
+
+		DRM_INFO("radeon: dynamic clocking initialized\n");
+	}
+
 	return 0;
 }
 
+void radeon_pm_reclock_work_handler(struct work_struct *work)
+{
+	struct radeon_device *rdev;
+	rdev = container_of(work, struct radeon_device,
+				pm.reclock_work.work);
+
+	mutex_lock(&rdev->cp.mutex);
+	if (!list_empty(&rdev->fence_drv.emited)) {
+		struct radeon_fence *fence;
+		int notProcessed;
+
+		fence = list_entry(rdev->fence_drv.emited.prev, struct radeon_fence, list);
+		notProcessed = fence->seq - RREG32(rdev->fence_drv.scratch_reg);
+
+		if (notProcessed > 3) { /* should upclock */
+			if (rdev->pm.planned_action == RADEON_PM_ACTION_DOWNCLOCK) {
+				rdev->pm.planned_action = RADEON_PM_ACTION_NONE;
+			}
+			else if (rdev->pm.planned_action == RADEON_PM_ACTION_NONE && rdev->pm.downclocked) {
+				rdev->pm.planned_action = RADEON_PM_ACTION_UPCLOCK;
+				rdev->pm.action_timeout = jiffies + msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS);
+			}
+		}
+		else if (notProcessed == 0) { /* should downclock */
+			if (rdev->pm.planned_action == RADEON_PM_ACTION_UPCLOCK) {
+				rdev->pm.planned_action = RADEON_PM_ACTION_NONE;
+			}
+			else if (rdev->pm.planned_action == RADEON_PM_ACTION_NONE && !rdev->pm.downclocked) {
+				rdev->pm.planned_action = RADEON_PM_ACTION_DOWNCLOCK;
+				rdev->pm.action_timeout = jiffies + msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS);
+			}
+		}
+	}
+
+	if (rdev->pm.planned_action == RADEON_PM_ACTION_UPCLOCK && jiffies > rdev->pm.action_timeout) {
+		rdev->pm.planned_action = RADEON_PM_ACTION_NONE;
+		rdev->pm.downclocked = false;
+		radeon_set_engine_clock(rdev, rdev->clock.default_sclk);
+	}
+	else if (rdev->pm.planned_action == RADEON_PM_ACTION_DOWNCLOCK && jiffies > rdev->pm.action_timeout) {
+		rdev->pm.planned_action = RADEON_PM_ACTION_NONE;
+		rdev->pm.downclocked = true;
+		radeon_set_engine_clock(rdev, rdev->clock.default_sclk / 2);
+	}
+	mutex_unlock(&rdev->cp.mutex);
+
+	queue_delayed_work(rdev->pm.wq, &rdev->pm.reclock_work, msecs_to_jiffies(RADEON_RECLOCK_LOOP_MS));
+}
+
 /*
  * Debugfs info
  */
-- 
1.6.4.2

