Il Mon, Dec 28, 2009 at 05:27:13PM -0500, Alex Deucher ha scritto: 
> 2009/12/28 Luca Tettamanti <kronos...@gmail.com>:
> > On Mon, Dec 28, 2009 at 01:32:24PM -0500, Alex Deucher wrote:
> >> 2009/12/28 Luca Tettamanti <kronos...@gmail.com>:
> >> > 2009/12/28 Alex Deucher <alexdeuc...@gmail.com>:
> >> >> On Mon, Dec 28, 2009 at 5:53 AM, Luca Tettamanti <kronos...@gmail.com> 
> >> >> wrote:
> >> >>> On Sun, Dec 27, 2009 at 1:55 AM, Rafał Miłecki <zaj...@gmail.com> 
> >> >>> wrote:
> >> >>>> W dniu 26 grudnia 2009 20:08 użytkownik Alex Deucher
> >> >>>> <alexdeuc...@gmail.com> napisał:
> >> >>>>> It may be that the engine doesn't like to be reclocked while it's
> >> >>>>> running.  Perhaps we should use the GUI idle interrupt rather than
> >> >>>>> vblanks to reclock the engine.
> >> >>>>
> >> >>>> Could you say something more about GUI idle interrupt, please?
> >> >>>
> >> >>> It's mentioned in the documentation of the IH (see r600.c); I guess
> >> >>> it's enabled by GUI_IDLE_INT_ENABLE.
> >> >>>
> >> >>>> What is this, do we already have code for that?
> >> >>>
> >> >>> Unless there are more subtleties all is needed it to enabled the
> >> >>> interrupt and catch it in r600_irq_process.
> >> >>
> >> >> That's pretty much it.  Pre-r6xx asics have a GUI interrupt as well.
> >> >> I can look up the details for that if they are not already documented.
> >> >
> >> > I can't find a way to ack the interrupt; I see
> >> > RADEON_GUI_IDLE_INT_TEST_ACK but I think it's for pre-r6xx cards,
> >> > right?
> >>
> >> You don't have to ACK it as the CP generates the interrupt in
> >> software; similar to the sw interrupts used for fences.
> >
> > Ok, good: I've got the stub running on my M76. r100 and rs600 parts are
> > untested. Comments?
> >
> 
> Looks pretty good.  I've included the proper defines from the register
> database below and you'll need to ack the gui idle interrupts on
> pre-r600 chips.  Now you just have to do something when you get the
> idle interrupt.

I've adapted Rafał's patch to do the reclock when the idle interrupt is
fired (which btw should take care of the special case for nr CRTCs > 1).
Unfortunately I still see the black frame when reclocking is performed.
So I tried recloking directly from the IH (yeah, I'm ashamed of
myself...); this got rid of the black frame, but causes corruption of a
horizontal block of the screen (during the reclock, before and after the
screen looks fine). In this second case I've added a spinlock to guard
the access to the CP ring, so nothing touches it while reclocking is
performed; however by the time we process the idle interrupt -
especially considering that multiple events might be queued in the IH
ring - someone else (i.e. one of the other cores) might already have
submitted more work; what do you think?

I'm attaching 3 patches; the first one contains the stub idle IH, the
second one is Rafał's patch adapted for idle interrupt (it's not very
polished), the third one moves reclocking to IH (and as is it's just an
ugly hack).

Luca
-- 
"In linea di principio sarei indifferente al natale, se solo il natale
 ricambiasse la cortesia e mi lasciasse in pace." -- Marco d'Itri
--- linux-2.6.git.orig/drivers/gpu/drm/radeon/r600.c	2009-12-28 16:38:38.388825742 +0100
+++ linux-2.6.git/drivers/gpu/drm/radeon/r600.c	2009-12-28 22:03:12.936157804 +0100
@@ -2458,6 +2458,7 @@
 int r600_irq_set(struct radeon_device *rdev)
 {
 	u32 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE;
+	u32 grbm_int_cntl;
 	u32 mode_int = 0;
 	u32 hpd1, hpd2, hpd3, hpd4 = 0, hpd5 = 0, hpd6 = 0;
 
@@ -2465,6 +2466,8 @@
 	if (!rdev->ih.enabled)
 		return 0;
 
+	grbm_int_cntl = RREG32(GRBM_INT_CNTL) & ~GUI_IDLE_INT_ENABLE;
+
 	if (ASIC_IS_DCE3(rdev)) {
 		hpd1 = RREG32(DC_HPD1_INT_CONTROL) & ~DC_HPDx_INT_EN;
 		hpd2 = RREG32(DC_HPD2_INT_CONTROL) & ~DC_HPDx_INT_EN;
@@ -2484,6 +2487,10 @@
 		DRM_DEBUG("r600_irq_set: sw int\n");
 		cp_int_cntl |= RB_INT_ENABLE;
 	}
+	if (rdev->irq.idle_int) {
+		DRM_DEBUG("r600_irq_set: GUI idle int\n");
+		grbm_int_cntl |= GUI_IDLE_INT_ENABLE;
+	}
 	if (rdev->irq.crtc_vblank_int[0]) {
 		DRM_DEBUG("r600_irq_set: vblank 0\n");
 		mode_int |= D1MODE_VBLANK_INT_MASK;
@@ -2518,6 +2525,7 @@
 	}
 
 	WREG32(CP_INT_CNTL, cp_int_cntl);
+	WREG32(GRBM_INT_CNTL, grbm_int_cntl);
 	WREG32(DxMODE_INT_MASK, mode_int);
 	if (ASIC_IS_DCE3(rdev)) {
 		WREG32(DC_HPD1_INT_CONTROL, hpd1);
@@ -2806,6 +2814,9 @@
 		case 181: /* CP EOP event */
 			DRM_DEBUG("IH: CP EOP\n");
 			break;
+		case 233: /* GUI idle event */
+			DRM_DEBUG("IH: GUI idle\n");
+			break;
 		default:
 			DRM_ERROR("Unhandled interrupt: %d %d\n", src_id, src_data);
 			break;
--- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon.h	2009-12-28 16:38:13.945836481 +0100
+++ linux-2.6.git/drivers/gpu/drm/radeon/radeon.h	2009-12-28 22:02:48.964001788 +0100
@@ -343,6 +343,7 @@
 struct radeon_irq {
 	bool		installed;
 	bool		sw_int;
+	bool		idle_int;
 	/* FIXME: use a define max crtc rather than hardcode it */
 	bool		crtc_vblank_int[2];
 	/* FIXME: use defines for max hpd/dacs */
--- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon_irq_kms.c	2009-12-28 16:37:10.669823739 +0100
+++ linux-2.6.git/drivers/gpu/drm/radeon/radeon_irq_kms.c	2009-12-28 22:03:01.508077338 +0100
@@ -67,6 +67,7 @@
 
 	/* Disable *all* interrupts */
 	rdev->irq.sw_int = false;
+	rdev->irq.idle_int = false;
 	for (i = 0; i < 2; i++) {
 		rdev->irq.crtc_vblank_int[i] = false;
 	}
@@ -81,6 +82,7 @@
 
 	dev->max_vblank_count = 0x001fffff;
 	rdev->irq.sw_int = true;
+	rdev->irq.idle_int = true;
 	radeon_irq_set(rdev);
 	return 0;
 }
@@ -95,6 +97,7 @@
 	}
 	/* Disable *all* interrupts */
 	rdev->irq.sw_int = false;
+	rdev->irq.idle_int = false;
 	for (i = 0; i < 2; i++) {
 		rdev->irq.crtc_vblank_int[i] = false;
 	}
--- linux-2.6.git.orig/drivers/gpu/drm/radeon/r100.c	2009-12-28 22:59:45.076740411 +0100
+++ linux-2.6.git/drivers/gpu/drm/radeon/r100.c	2009-12-29 14:14:42.600043339 +0100
@@ -246,6 +246,9 @@
 	if (rdev->irq.sw_int) {
 		tmp |= RADEON_SW_INT_ENABLE;
 	}
+	if (rdev->irq.idle_int) {
+		tmp |= RADEON_GUI_IDLE_INT_MASK;
+	}
 	if (rdev->irq.crtc_vblank_int[0]) {
 		tmp |= RADEON_CRTC_VBLANK_MASK;
 	}
@@ -278,7 +281,8 @@
 	uint32_t irqs = RREG32(RADEON_GEN_INT_STATUS);
 	uint32_t irq_mask = RADEON_SW_INT_TEST |
 		RADEON_CRTC_VBLANK_STAT | RADEON_CRTC2_VBLANK_STAT |
-		RADEON_FP_DETECT_STAT | RADEON_FP2_DETECT_STAT;
+		RADEON_FP_DETECT_STAT | RADEON_FP2_DETECT_STAT |
+		RADEON_GUI_IDLE_STAT;
 
 	if (irqs) {
 		WREG32(RADEON_GEN_INT_STATUS, irqs);
@@ -318,6 +322,9 @@
 			queue_hotplug = true;
 			DRM_DEBUG("HPD2\n");
 		}
+		if (status & RADEON_GUI_IDLE_STAT) {
+			DRM_DEBUG("GUI idle\n");
+		}
 		status = r100_irq_ack(rdev);
 	}
 	if (queue_hotplug)
--- linux-2.6.git.orig/drivers/gpu/drm/radeon/rs600.c	2009-12-28 22:59:45.052738246 +0100
+++ linux-2.6.git/drivers/gpu/drm/radeon/rs600.c	2009-12-29 14:22:09.240002197 +0100
@@ -318,6 +318,9 @@
 	if (rdev->irq.sw_int) {
 		tmp |= S_000040_SW_INT_EN(1);
 	}
+	if (rdev->irq.idle_int) {
+		tmp |= S_000040_GUI_IDLE(1);
+	}
 	if (rdev->irq.crtc_vblank_int[0]) {
 		mode_int |= S_006540_D1MODE_VBLANK_INT_MASK(1);
 	}
@@ -340,7 +343,7 @@
 static inline uint32_t rs600_irq_ack(struct radeon_device *rdev, u32 *r500_disp_int)
 {
 	uint32_t irqs = RREG32(R_000044_GEN_INT_STATUS);
-	uint32_t irq_mask = ~C_000044_SW_INT;
+	uint32_t irq_mask = ~C_000044_SW_INT | ~C_000044_GUI_IDLE_STAT;
 	u32 tmp;
 
 	if (G_000044_DISPLAY_INT_STAT(irqs)) {
@@ -411,6 +414,9 @@
 			queue_hotplug = true;
 			DRM_DEBUG("HPD2\n");
 		}
+		if (G_000044_GUI_IDLE_STAT(status)) {
+			DRM_DEBUG("GUI idle\n");
+		}
 		status = rs600_irq_ack(rdev, &r500_disp_int);
 	}
 	if (queue_hotplug)
--- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon_reg.h	2009-12-28 22:59:45.060748373 +0100
+++ linux-2.6.git/drivers/gpu/drm/radeon/radeon_reg.h	2009-12-29 14:13:35.801002197 +0100
@@ -994,6 +994,7 @@
 #	define RADEON_FP_DETECT_MASK		(1 << 4)
 #	define RADEON_CRTC2_VBLANK_MASK		(1 << 9)
 #	define RADEON_FP2_DETECT_MASK		(1 << 10)
+#	define RADEON_GUI_IDLE_INT_MASK		(1 << 19)
 #	define RADEON_SW_INT_ENABLE		(1 << 25)
 #define RADEON_GEN_INT_STATUS               0x0044
 #	define AVIVO_DISPLAY_INT_STATUS		(1 << 0)
@@ -1005,6 +1006,8 @@
 #	define RADEON_CRTC2_VBLANK_STAT_ACK	(1 << 9)
 #	define RADEON_FP2_DETECT_STAT		(1 << 10)
 #	define RADEON_FP2_DETECT_STAT_ACK	(1 << 10)
+#	define RADEON_GUI_IDLE_STAT		(1 << 19)
+#	define RADEON_GUI_IDLE_STAT_ACK		(1 << 19)
 #	define RADEON_SW_INT_FIRE		(1 << 26)
 #	define RADEON_SW_INT_TEST		(1 << 25)
 #	define RADEON_SW_INT_TEST_ACK		(1 << 25)
Index: linux-2.6.git/drivers/gpu/drm/radeon/radeon.h
===================================================================
--- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon.h	2009-12-29 14:11:49.113001762 +0100
+++ linux-2.6.git/drivers/gpu/drm/radeon/radeon.h	2009-12-29 19:08:02.904889536 +0100
@@ -89,6 +89,7 @@
 extern int radeon_connector_table;
 extern int radeon_tv;
 extern int radeon_new_pll;
+extern int radeon_dynpm;
 extern int radeon_audio;
 
 /*
@@ -148,6 +149,7 @@
  * Power management
  */
 int radeon_pm_init(struct radeon_device *rdev);
+void radeon_pm_compute_clocks(struct radeon_device *rdev);
 
 /*
  * Fences.
@@ -567,7 +569,33 @@
  * Equation between gpu/memory clock and available bandwidth is hw dependent
  * (type of memory, bus size, efficiency, ...)
  */
+enum radeon_pm_state {
+	PM_STATE_DISABLED,
+	PM_STATE_MINIMUM,
+	PM_STATE_PAUSED,
+	PM_STATE_ACTIVE
+};
+enum radeon_pm_action {
+	PM_ACTION_NONE,
+	PM_ACTION_MINIMUM,
+	PM_ACTION_DOWNCLOCK,
+	PM_ACTION_UPCLOCK
+};
 struct radeon_pm {
+	struct mutex		reclock_mutex;
+	struct spinlock		lock;
+	struct work_struct	reclock_work;
+	struct delayed_work	idle_work;
+	enum radeon_pm_state	state;
+	enum radeon_pm_action	planned_action;
+	unsigned long		action_timeout;
+	bool 			downclocked;
+	int			active_crtcs;
+	int			req_vblank;
+	uint32_t		min_gpu_engine_clock;
+	uint32_t		min_gpu_memory_clock;
+	uint32_t		min_mode_engine_clock;
+	uint32_t		min_mode_memory_clock;
 	fixed20_12		max_bandwidth;
 	fixed20_12		igp_sideport_mclk;
 	fixed20_12		igp_system_mclk;
Index: linux-2.6.git/drivers/gpu/drm/radeon/radeon_device.c
===================================================================
--- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon_device.c	2009-12-28 11:40:27.826946593 +0100
+++ linux-2.6.git/drivers/gpu/drm/radeon/radeon_device.c	2009-12-29 19:05:43.772754474 +0100
@@ -640,6 +640,8 @@
 	if (rdev->family >= CHIP_R600)
 		spin_lock_init(&rdev->ih.lock);
 	mutex_init(&rdev->gem.mutex);
+	mutex_init(&rdev->pm.reclock_mutex);
+	spin_lock_init(&rdev->pm.lock);
 	rwlock_init(&rdev->fence_drv.lock);
 	INIT_LIST_HEAD(&rdev->gem.objects);
 
Index: linux-2.6.git/drivers/gpu/drm/radeon/radeon_drv.c
===================================================================
--- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon_drv.c	2009-12-28 11:40:27.826946593 +0100
+++ linux-2.6.git/drivers/gpu/drm/radeon/radeon_drv.c	2009-12-29 15:19:02.393011113 +0100
@@ -87,6 +87,7 @@
 int radeon_connector_table = 0;
 int radeon_tv = 1;
 int radeon_new_pll = 1;
+int radeon_dynpm = -1;
 int radeon_audio = 1;
 
 MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers");
@@ -125,6 +126,9 @@
 MODULE_PARM_DESC(new_pll, "Select new PLL code for AVIVO chips");
 module_param_named(new_pll, radeon_new_pll, int, 0444);
 
+MODULE_PARM_DESC(dynpm, "Disable/Enable dynamic power management (1 = enable)");
+module_param_named(dynpm, radeon_dynpm, int, 0444);
+
 MODULE_PARM_DESC(audio, "Audio enable (0 = disable)");
 module_param_named(audio, radeon_audio, int, 0444);
 
Index: linux-2.6.git/drivers/gpu/drm/radeon/radeon_encoders.c
===================================================================
--- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon_encoders.c	2009-12-28 11:40:27.826946593 +0100
+++ linux-2.6.git/drivers/gpu/drm/radeon/radeon_encoders.c	2009-12-29 15:19:49.512042228 +0100
@@ -216,6 +216,9 @@
 	struct drm_device *dev = encoder->dev;
 	struct radeon_device *rdev = dev->dev_private;
 
+	/* adjust pm to upcoming mode change */
+	radeon_pm_compute_clocks(rdev);
+
 	/* set the active encoder to connector routing */
 	radeon_encoder_set_active_device(encoder);
 	drm_mode_set_crtcinfo(adjusted_mode, 0);
@@ -1027,6 +1030,9 @@
 		atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
 	}
 	radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
+
+	/* adjust pm to dpms change */
+	radeon_pm_compute_clocks(rdev);
 }
 
 union crtc_sourc_param {
Index: linux-2.6.git/drivers/gpu/drm/radeon/radeon_legacy_encoders.c
===================================================================
--- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon_legacy_encoders.c	2009-12-28 11:40:27.826946593 +0100
+++ linux-2.6.git/drivers/gpu/drm/radeon/radeon_legacy_encoders.c	2009-12-29 15:19:02.393011113 +0100
@@ -96,6 +96,9 @@
 		radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
 	else
 		radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
+
+	/* adjust pm to dpms change */
+	radeon_pm_compute_clocks(rdev);
 }
 
 static void radeon_legacy_lvds_prepare(struct drm_encoder *encoder)
@@ -195,6 +198,11 @@
 				     struct drm_display_mode *adjusted_mode)
 {
 	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	struct radeon_device *rdev = dev->dev_private;
+
+	/* adjust pm to upcoming mode change */
+	radeon_pm_compute_clocks(rdev);
 
 	/* set the active encoder to connector routing */
 	radeon_encoder_set_active_device(encoder);
@@ -266,6 +274,9 @@
 		radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
 	else
 		radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
+
+	/* adjust pm to dpms change */
+	radeon_pm_compute_clocks(rdev);
 }
 
 static void radeon_legacy_primary_dac_prepare(struct drm_encoder *encoder)
@@ -451,6 +462,9 @@
 		radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
 	else
 		radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
+
+	/* adjust pm to dpms change */
+	radeon_pm_compute_clocks(rdev);
 }
 
 static void radeon_legacy_tmds_int_prepare(struct drm_encoder *encoder)
@@ -616,6 +630,9 @@
 		radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
 	else
 		radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
+
+	/* adjust pm to dpms change */
+	radeon_pm_compute_clocks(rdev);
 }
 
 static void radeon_legacy_tmds_ext_prepare(struct drm_encoder *encoder)
@@ -823,6 +840,9 @@
 		radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
 	else
 		radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false);
+
+	/* adjust pm to dpms change */
+	radeon_pm_compute_clocks(rdev);
 }
 
 static void radeon_legacy_tv_dac_prepare(struct drm_encoder *encoder)
Index: linux-2.6.git/drivers/gpu/drm/radeon/radeon_pm.c
===================================================================
--- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon_pm.c	2009-12-28 11:40:27.830951642 +0100
+++ linux-2.6.git/drivers/gpu/drm/radeon/radeon_pm.c	2009-12-29 19:07:39.496885146 +0100
@@ -22,17 +22,260 @@
 #include "drmP.h"
 #include "radeon.h"
 
-int radeon_debugfs_pm_init(struct radeon_device *rdev);
+#define RADEON_IDLE_LOOP_MS 100
+#define RADEON_RECLOCK_DELAY_MS 500
+
+static void radeon_pm_check_limits(struct radeon_device *rdev);
+static void radeon_pm_set_clocks_locked(struct radeon_device *rdev);
+static void radeon_pm_set_clocks(struct radeon_device *rdev);
+static void radeon_pm_reclock_work_handler(struct work_struct *work);
+static void radeon_pm_idle_work_handler(struct work_struct *work);
+static int radeon_debugfs_pm_init(struct radeon_device *rdev);
+
+static const char *pm_state_names[4] = {
+	"PM_STATE_DISABLED",
+	"PM_STATE_MINIMUM",
+	"PM_STATE_PAUSED",
+	"PM_STATE_ACTIVE"
+};
 
 int radeon_pm_init(struct radeon_device *rdev)
 {
+	rdev->pm.state = PM_STATE_DISABLED;
+	rdev->pm.planned_action = PM_ACTION_NONE;
+	rdev->pm.downclocked = false;
+
+	radeon_pm_check_limits(rdev);
+
 	if (radeon_debugfs_pm_init(rdev)) {
 		DRM_ERROR("Failed to register debugfs file for PM!\n");
 	}
 
+	INIT_WORK(&rdev->pm.reclock_work, radeon_pm_reclock_work_handler);
+	INIT_DELAYED_WORK(&rdev->pm.idle_work, radeon_pm_idle_work_handler);
+
+	if (radeon_dynpm != -1 && radeon_dynpm) {
+		rdev->pm.state = PM_STATE_PAUSED;
+		DRM_INFO("radeon: dynamic power management enabled\n");
+	}
+
+	DRM_INFO("radeon: power management initialized\n");
+
 	return 0;
 }
 
+static void radeon_pm_check_limits(struct radeon_device *rdev)
+{
+	rdev->pm.min_gpu_engine_clock = rdev->clock.default_sclk - 10000;
+	rdev->pm.min_gpu_memory_clock = rdev->clock.default_mclk - 5000;
+}
+
+void radeon_pm_compute_clocks(struct radeon_device *rdev)
+{
+	struct drm_device *ddev = rdev->ddev;
+	struct drm_connector *connector;
+	struct radeon_crtc *radeon_crtc;
+	int count = 0;
+
+	if (rdev->pm.state == PM_STATE_DISABLED)
+		return;
+
+	spin_lock_irq(&rdev->pm.lock);
+
+	rdev->pm.active_crtcs = 0;
+	list_for_each_entry(connector,
+		&ddev->mode_config.connector_list, head) {
+		if (connector->encoder &&
+			connector->dpms != DRM_MODE_DPMS_OFF) {
+			radeon_crtc = to_radeon_crtc(connector->encoder->crtc);
+			rdev->pm.active_crtcs |= (1 << radeon_crtc->crtc_id);
+			++count;
+		}
+	}
+
+	DRM_INFO("Active CRTCs: %d\n", count);
+	DRM_INFO("Current state: %s\n", pm_state_names[rdev->pm.state]);
+
+	if (count > 1) {
+		if (rdev->pm.state == PM_STATE_ACTIVE) {
+			wait_queue_head_t wait;
+			init_waitqueue_head(&wait);
+
+			cancel_delayed_work(&rdev->pm.idle_work);
+
+			rdev->pm.state = PM_STATE_PAUSED;
+			rdev->pm.planned_action = PM_ACTION_UPCLOCK;
+
+			spin_unlock_irq(&rdev->pm.lock);
+
+			DRM_INFO("radeon: dynamic power management deactivated\n");
+		} else {
+			spin_unlock_irq(&rdev->pm.lock);
+			//mutex_unlock(&rdev->pm.mutex);
+		}
+	} else if (count == 1) {
+		rdev->pm.min_mode_engine_clock = rdev->pm.min_gpu_engine_clock;
+		rdev->pm.min_mode_memory_clock = rdev->pm.min_gpu_memory_clock;
+		/* TODO: Increase clocks if needed for current mode */
+
+		if (rdev->pm.state == PM_STATE_MINIMUM) {
+			rdev->pm.state = PM_STATE_ACTIVE;
+			rdev->pm.planned_action = PM_ACTION_UPCLOCK;
+
+			queue_delayed_work(rdev->wq, &rdev->pm.idle_work,
+				msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
+		} else if (rdev->pm.state == PM_STATE_PAUSED) {
+			rdev->pm.state = PM_STATE_ACTIVE;
+			queue_delayed_work(rdev->wq, &rdev->pm.idle_work,
+				msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
+			DRM_INFO("radeon: dynamic power management activated\n");
+		}
+
+		spin_unlock_irq(&rdev->pm.lock);
+	} else { /* count == 0 */
+		if (rdev->pm.state != PM_STATE_MINIMUM) {
+			cancel_delayed_work(&rdev->pm.idle_work);
+
+			rdev->pm.state = PM_STATE_MINIMUM;
+			rdev->pm.planned_action = PM_ACTION_MINIMUM;
+		}
+
+		spin_unlock_irq(&rdev->pm.lock);
+	}
+}
+
+static void radeon_pm_set_clocks(struct radeon_device *rdev)
+{
+	unsigned long flags;
+	enum radeon_pm_action action;
+
+	spin_lock_irqsave(&rdev->pm.lock, flags);
+	/* TODO: retrieve the full target power state */
+	action = rdev->pm.planned_action;
+	rdev->pm.planned_action = PM_ACTION_NONE;
+	spin_unlock_irqrestore(&rdev->pm.lock, flags);
+
+	mutex_lock(&rdev->cp.mutex);
+	/*radeon_fence_wait_last(rdev);*/
+	switch (action) {
+	case PM_ACTION_UPCLOCK:
+		DRM_DEBUG_DRIVER("PM_ACTION_UPCLOCK\n");
+		radeon_set_engine_clock(rdev, rdev->clock.default_sclk);
+		rdev->pm.downclocked = false;
+		break;
+	case PM_ACTION_DOWNCLOCK:
+		DRM_DEBUG_DRIVER("PM_ACTION_DOWNCLOCK\n");
+		radeon_set_engine_clock(rdev, rdev->pm.min_mode_engine_clock);
+		rdev->pm.downclocked = true;
+		break;
+	case PM_ACTION_MINIMUM:
+		DRM_DEBUG_DRIVER("PM_ACTION_MINIMUM\n");
+		radeon_set_engine_clock(rdev, rdev->pm.min_gpu_engine_clock);
+		break;
+	case PM_ACTION_NONE:
+		DRM_ERROR("%s: PM_ACTION_NONE\n", __func__);
+		break;
+	}
+
+	mutex_unlock(&rdev->cp.mutex);
+}
+
+#if 0
+static void radeon_pm_set_clocks(struct radeon_device *rdev)
+{
+	radeon_pm_set_clocks_locked(rdev);
+	mutex_lock(&rdev->pm.mutex);
+	/* new VBLANK irq may come before handling previous one */
+	if (rdev->pm.vblank_callback) {
+		mutex_lock(&rdev->cp.mutex);
+		if (rdev->pm.req_vblank & (1 << 0)) {
+			rdev->pm.req_vblank &= ~(1 << 0);
+			drm_vblank_put(rdev->ddev, 0);
+		}
+		if (rdev->pm.req_vblank & (1 << 1)) {
+			rdev->pm.req_vblank &= ~(1 << 1);
+			drm_vblank_put(rdev->ddev, 1);
+		}
+		rdev->pm.vblank_callback = false;
+		radeon_pm_set_clocks_locked(rdev);
+		mutex_unlock(&rdev->cp.mutex);
+	}
+	mutex_unlock(&rdev->pm.mutex);
+}
+#endif
+
+static void radeon_pm_reclock_work_handler(struct work_struct *work)
+{
+	struct radeon_device *rdev;
+	rdev = container_of(work, struct radeon_device,
+				pm.reclock_work);
+	
+	mutex_lock(&rdev->pm.reclock_mutex);
+	radeon_pm_set_clocks(rdev);
+	mutex_unlock(&rdev->pm.reclock_mutex);
+}
+
+static void radeon_pm_idle_work_handler(struct work_struct *work)
+{
+	struct radeon_device *rdev;
+	unsigned long irq_flags;
+	rdev = container_of(work, struct radeon_device, pm.idle_work.work);
+
+	spin_lock_irqsave(&rdev->pm.lock, irq_flags);
+	if (rdev->pm.state == PM_STATE_ACTIVE) {
+		int not_processed = 0;
+
+		read_lock(&rdev->fence_drv.lock);
+		if (!list_empty(&rdev->fence_drv.emited)) {
+			struct list_head *ptr;
+			list_for_each(ptr, &rdev->fence_drv.emited) {
+				/* count up to 3, that's enought info */
+				if (++not_processed >= 3)
+					break;
+			}
+		}
+		read_unlock(&rdev->fence_drv.lock);
+
+		if (not_processed >= 3) { /* should upclock */
+			if (rdev->pm.planned_action == PM_ACTION_DOWNCLOCK) {
+				rdev->pm.planned_action = PM_ACTION_NONE;
+			} else if (rdev->pm.planned_action == PM_ACTION_NONE &&
+					rdev->pm.downclocked) {
+				rdev->pm.planned_action = PM_ACTION_UPCLOCK;
+				rdev->pm.action_timeout = jiffies +
+					msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS);
+			}
+		} else if (not_processed == 0) { /* should downclock */
+			if (rdev->pm.planned_action == PM_ACTION_UPCLOCK) {
+				rdev->pm.planned_action = PM_ACTION_NONE;
+			} else if (rdev->pm.planned_action == PM_ACTION_NONE &&
+					!rdev->pm.downclocked) {
+				rdev->pm.planned_action = PM_ACTION_DOWNCLOCK;
+				rdev->pm.action_timeout = jiffies +
+					msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS);
+			}
+		}
+#if 0
+		if (rdev->pm.planned_action != PM_ACTION_NONE &&
+				jiffies > rdev->pm.action_timeout) {
+			if (rdev->pm.active_crtcs & (1 << 0)) {
+				rdev->pm.req_vblank |= (1 << 0);
+				drm_vblank_get(rdev->ddev, 0);
+			}
+			if (rdev->pm.active_crtcs & (1 << 1)) {
+				rdev->pm.req_vblank |= (1 << 1);
+				drm_vblank_get(rdev->ddev, 1);
+			}
+			rdev->pm.vblank_callback = true;
+		}
+#endif
+	}
+	spin_unlock_irqrestore(&rdev->pm.lock, irq_flags);
+
+	queue_delayed_work(rdev->wq, &rdev->pm.idle_work,
+				msecs_to_jiffies(RADEON_IDLE_LOOP_MS));
+}
+
 /*
  * Debugfs info
  */
@@ -44,6 +287,7 @@
 	struct drm_device *dev = node->minor->dev;
 	struct radeon_device *rdev = dev->dev_private;
 
+	seq_printf(m, "state: %s\n", pm_state_names[rdev->pm.state]);
 	seq_printf(m, "default engine clock: %u0 kHz\n", rdev->clock.default_sclk);
 	seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev));
 	seq_printf(m, "default memory clock: %u0 kHz\n", rdev->clock.default_mclk);
@@ -58,7 +302,7 @@
 };
 #endif
 
-int radeon_debugfs_pm_init(struct radeon_device *rdev)
+static int radeon_debugfs_pm_init(struct radeon_device *rdev)
 {
 #if defined(CONFIG_DEBUG_FS)
 	return radeon_debugfs_add_files(rdev, radeon_pm_info_list, ARRAY_SIZE(radeon_pm_info_list));
Index: linux-2.6.git/drivers/gpu/drm/radeon/r600.c
===================================================================
--- linux-2.6.git.orig/drivers/gpu/drm/radeon/r600.c	2009-12-29 15:54:56.036020552 +0100
+++ linux-2.6.git/drivers/gpu/drm/radeon/r600.c	2009-12-29 19:05:43.732756081 +0100
@@ -2689,6 +2689,7 @@
 	u32 last_entry = rdev->ih.ring_size - 16;
 	u32 ring_index, disp_int, disp_int_cont, disp_int_cont2;
 	unsigned long flags;
+	enum radeon_pm_action pm_action;
 	bool queue_hotplug = false;
 
 	DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr);
@@ -2816,6 +2817,15 @@
 			break;
 		case 233: /* GUI idle event */
 			DRM_DEBUG("IH: GUI idle\n");
+
+			spin_lock(&rdev->pm.lock);
+			pm_action = rdev->pm.planned_action;
+			spin_unlock(&rdev->pm.lock);
+
+			/* Kick off the reclock if needed */
+			if (pm_action != PM_ACTION_NONE)
+				queue_work(rdev->wq, &rdev->pm.reclock_work);
+			
 			break;
 		default:
 			DRM_ERROR("Unhandled interrupt: %d %d\n", src_id, src_data);
Index: linux-2.6.git/drivers/gpu/drm/radeon/atom.c
===================================================================
--- linux-2.6.git.orig/drivers/gpu/drm/radeon/atom.c	2009-12-29 19:05:43.828736246 +0100
+++ linux-2.6.git/drivers/gpu/drm/radeon/atom.c	2009-12-29 19:08:27.560753496 +0100
@@ -608,7 +608,8 @@
 	uint8_t count = U8((*ptr)++);
 	SDEBUG("   count: %d\n", count);
 	if (arg == ATOM_UNIT_MICROSEC)
-		schedule_timeout_uninterruptible(usecs_to_jiffies(count));
+		//schedule_timeout_uninterruptible(usecs_to_jiffies(count));
+		mdelay(count);
 	else
 		schedule_timeout_uninterruptible(msecs_to_jiffies(count));
 }
@@ -1065,7 +1066,7 @@
 	ectx.start = base;
 	ectx.ps = params;
 	if (ws)
-		ectx.ws = kzalloc(4 * ws, GFP_KERNEL);
+		ectx.ws = kzalloc(4 * ws, in_atomic() ? GFP_ATOMIC : GFP_KERNEL);
 	else
 		ectx.ws = NULL;
 
@@ -1095,9 +1096,9 @@
 
 void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
 {
-	mutex_lock(&ctx->mutex);
+//	mutex_lock(&ctx->mutex);
 	atom_execute_table_locked(ctx, index, params);
-	mutex_unlock(&ctx->mutex);
+//	mutex_unlock(&ctx->mutex);
 }
 
 static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 };
Index: linux-2.6.git/drivers/gpu/drm/radeon/r600.c
===================================================================
--- linux-2.6.git.orig/drivers/gpu/drm/radeon/r600.c	2009-12-29 19:05:43.732756081 +0100
+++ linux-2.6.git/drivers/gpu/drm/radeon/r600.c	2009-12-29 19:09:04.501787866 +0100
@@ -74,6 +74,8 @@
 void r600_gpu_init(struct radeon_device *rdev);
 void r600_fini(struct radeon_device *rdev);
 
+void radeon_pm_set_clocks(struct radeon_device *rdev, enum radeon_pm_action action);
+
 /* hpd for digital panel detect/disconnect */
 bool r600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd)
 {
@@ -2824,7 +2826,7 @@
 
 			/* Kick off the reclock if needed */
 			if (pm_action != PM_ACTION_NONE)
-				queue_work(rdev->wq, &rdev->pm.reclock_work);
+				radeon_pm_set_clocks(rdev, pm_action);
 			
 			break;
 		default:
Index: linux-2.6.git/drivers/gpu/drm/radeon/radeon_device.c
===================================================================
--- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon_device.c	2009-12-29 19:05:43.772754474 +0100
+++ linux-2.6.git/drivers/gpu/drm/radeon/radeon_device.c	2009-12-29 19:08:27.560753496 +0100
@@ -637,6 +637,7 @@
 	mutex_init(&rdev->cs_mutex);
 	mutex_init(&rdev->ib_pool.mutex);
 	mutex_init(&rdev->cp.mutex);
+	spin_lock_init(&rdev->cp.ring_lock);
 	if (rdev->family >= CHIP_R600)
 		spin_lock_init(&rdev->ih.lock);
 	mutex_init(&rdev->gem.mutex);
Index: linux-2.6.git/drivers/gpu/drm/radeon/radeon.h
===================================================================
--- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon.h	2009-12-29 19:08:02.904889536 +0100
+++ linux-2.6.git/drivers/gpu/drm/radeon/radeon.h	2009-12-29 19:08:27.560753496 +0100
@@ -397,6 +397,7 @@
 	uint32_t		align_mask;
 	uint32_t		ptr_mask;
 	struct mutex		mutex;
+	struct spinlock		ring_lock;
 	bool			ready;
 };
 
Index: linux-2.6.git/drivers/gpu/drm/radeon/radeon_pm.c
===================================================================
--- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon_pm.c	2009-12-29 19:07:39.496885146 +0100
+++ linux-2.6.git/drivers/gpu/drm/radeon/radeon_pm.c	2009-12-29 19:12:32.592787422 +0100
@@ -27,7 +27,7 @@
 
 static void radeon_pm_check_limits(struct radeon_device *rdev);
 static void radeon_pm_set_clocks_locked(struct radeon_device *rdev);
-static void radeon_pm_set_clocks(struct radeon_device *rdev);
+void radeon_pm_set_clocks(struct radeon_device *rdev, enum radeon_pm_action action);
 static void radeon_pm_reclock_work_handler(struct work_struct *work);
 static void radeon_pm_idle_work_handler(struct work_struct *work);
 static int radeon_debugfs_pm_init(struct radeon_device *rdev);
@@ -51,7 +51,7 @@
 		DRM_ERROR("Failed to register debugfs file for PM!\n");
 	}
 
-	INIT_WORK(&rdev->pm.reclock_work, radeon_pm_reclock_work_handler);
+//	INIT_WORK(&rdev->pm.reclock_work, radeon_pm_reclock_work_handler);
 	INIT_DELAYED_WORK(&rdev->pm.idle_work, radeon_pm_idle_work_handler);
 
 	if (radeon_dynpm != -1 && radeon_dynpm) {
@@ -144,8 +144,9 @@
 	}
 }
 
-static void radeon_pm_set_clocks(struct radeon_device *rdev)
+void radeon_pm_set_clocks(struct radeon_device *rdev, enum radeon_pm_action action)
 {
+#if 0
 	unsigned long flags;
 	enum radeon_pm_action action;
 
@@ -154,8 +155,12 @@
 	action = rdev->pm.planned_action;
 	rdev->pm.planned_action = PM_ACTION_NONE;
 	spin_unlock_irqrestore(&rdev->pm.lock, flags);
+#endif
+	rdev->pm.planned_action = PM_ACTION_NONE;
+
+	/* Lock the CP ring */
+	spin_lock(&rdev->cp.ring_lock);
 
-	mutex_lock(&rdev->cp.mutex);
 	/*radeon_fence_wait_last(rdev);*/
 	switch (action) {
 	case PM_ACTION_UPCLOCK:
@@ -176,8 +181,7 @@
 		DRM_ERROR("%s: PM_ACTION_NONE\n", __func__);
 		break;
 	}
-
-	mutex_unlock(&rdev->cp.mutex);
+	spin_unlock(&rdev->cp.ring_lock);
 }
 
 #if 0
@@ -204,6 +208,7 @@
 }
 #endif
 
+#if 0
 static void radeon_pm_reclock_work_handler(struct work_struct *work)
 {
 	struct radeon_device *rdev;
@@ -214,6 +219,7 @@
 	radeon_pm_set_clocks(rdev);
 	mutex_unlock(&rdev->pm.reclock_mutex);
 }
+#endif
 
 static void radeon_pm_idle_work_handler(struct work_struct *work)
 {
Index: linux-2.6.git/drivers/gpu/drm/radeon/radeon_ring.c
===================================================================
--- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon_ring.c	2009-12-29 19:05:43.796755382 +0100
+++ linux-2.6.git/drivers/gpu/drm/radeon/radeon_ring.c	2009-12-29 19:08:27.564755871 +0100
@@ -279,11 +279,13 @@
 	/* We pad to match fetch size */
 	count_dw_pad = (rdev->cp.align_mask + 1) -
 		       (rdev->cp.wptr & rdev->cp.align_mask);
+	spin_lock_irq(&rdev->cp.ring_lock);
 	for (i = 0; i < count_dw_pad; i++) {
 		radeon_ring_write(rdev, 2 << 30);
 	}
 	DRM_MEMORYBARRIER();
 	radeon_cp_commit(rdev);
+	spin_unlock_irq(&rdev->cp.ring_lock);
 	mutex_unlock(&rdev->cp.mutex);
 }
 
------------------------------------------------------------------------------
This SF.Net email is sponsored by the Verizon Developer Community
Take advantage of Verizon's best-in-class app development support
A streamlined, 14 day to market process makes app distribution fast and easy
Join now and get one step closer to millions of Verizon customers
http://p.sf.net/sfu/verizon-dev2dev 
--
_______________________________________________
Dri-devel mailing list
Dri-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/dri-devel

Reply via email to