drm/radeon/kms: add support for gui idle interrupts (v4)

Useful for certain power management operations.  You
need to wait for the GUI engine (2D, 3D, CP, etc.) to be
idle before changing clocks or adjusting engine parameters.

(v2) Fix gui idle enable on pre-r6xx asics

(v3) The gui idle interrrupt status bit is permanently asserted
on pre-r6xx chips, but the interrrupt is still generated.
workaround it in the driver.

(v4) Add support for evergreen

Signed-off-by: Alex Deucher <alexdeucher@gmail.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index e62116e..d352986 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -262,6 +262,9 @@
 	if (rdev->irq.sw_int) {
 		tmp |= RADEON_SW_INT_ENABLE;
 	}
+	if (rdev->irq.gui_idle) {
+		tmp |= RADEON_GUI_IDLE_MASK;
+	}
 	if (rdev->irq.crtc_vblank_int[0]) {
 		tmp |= RADEON_CRTC_VBLANK_MASK;
 	}
@@ -296,6 +299,12 @@
 		RADEON_CRTC_VBLANK_STAT | RADEON_CRTC2_VBLANK_STAT |
 		RADEON_FP_DETECT_STAT | RADEON_FP2_DETECT_STAT;
 
+	/* the interrupt works, but the status bit is permanently asserted */
+	if (rdev->irq.gui_idle && radeon_gui_idle(rdev)) {
+		if (!rdev->irq.gui_idle_acked)
+			irq_mask |= RADEON_GUI_IDLE_STAT;
+	}
+
 	if (irqs) {
 		WREG32(RADEON_GEN_INT_STATUS, irqs);
 	}
@@ -307,6 +316,9 @@
 	uint32_t status, msi_rearm;
 	bool queue_hotplug = false;
 
+	/* reset gui idle ack.  the status bit is broken */
+	rdev->irq.gui_idle_acked = false;
+
 	status = r100_irq_ack(rdev);
 	if (!status) {
 		return IRQ_NONE;
@@ -319,6 +331,12 @@
 		if (status & RADEON_SW_INT_TEST) {
 			radeon_fence_process(rdev);
 		}
+		/* gui idle interrupt */
+		if (status & RADEON_GUI_IDLE_STAT) {
+			rdev->irq.gui_idle_acked = true;
+			rdev->pm.gui_idle = true;
+			wake_up(&rdev->irq.idle_queue);
+		}
 		/* Vertical blank interrupts */
 		if (status & RADEON_CRTC_VBLANK_STAT) {
 			drm_handle_vblank(rdev->ddev, 0);
@@ -340,6 +358,8 @@
 		}
 		status = r100_irq_ack(rdev);
 	}
+	/* reset gui idle ack.  the status bit is broken */
+	rdev->irq.gui_idle_acked = false;
 	if (queue_hotplug)
 		queue_work(rdev->wq, &rdev->hotplug_work);
 	if (rdev->msi_enabled) {