drm/radeon/kms: add support for gui idle interrupts (v4)
authorAlex Deucher <alexdeucher@gmail.com>
Thu, 22 Apr 2010 16:52:11 +0000 (12:52 -0400)
committerDave Airlie <airlied@redhat.com>
Tue, 18 May 2010 08:20:49 +0000 (18:20 +1000)
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>
drivers/gpu/drm/radeon/evergreen.c
drivers/gpu/drm/radeon/r100.c
drivers/gpu/drm/radeon/r600.c
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_device.c
drivers/gpu/drm/radeon/radeon_irq_kms.c
drivers/gpu/drm/radeon/radeon_pm.c
drivers/gpu/drm/radeon/radeon_reg.h
drivers/gpu/drm/radeon/rs600.c

index b3d168f..0137a4c 100644 (file)
@@ -1418,6 +1418,7 @@ int evergreen_irq_set(struct radeon_device *rdev)
        u32 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE;
        u32 crtc1 = 0, crtc2 = 0, crtc3 = 0, crtc4 = 0, crtc5 = 0, crtc6 = 0;
        u32 hpd1, hpd2, hpd3, hpd4, hpd5, hpd6;
+       u32 grbm_int_cntl = 0;
 
        if (!rdev->irq.installed) {
                WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n");
@@ -1490,8 +1491,13 @@ int evergreen_irq_set(struct radeon_device *rdev)
                DRM_DEBUG("evergreen_irq_set: hpd 6\n");
                hpd6 |= DC_HPDx_INT_EN;
        }
+       if (rdev->irq.gui_idle) {
+               DRM_DEBUG("gui idle\n");
+               grbm_int_cntl |= GUI_IDLE_INT_ENABLE;
+       }
 
        WREG32(CP_INT_CNTL, cp_int_cntl);
+       WREG32(GRBM_INT_CNTL, grbm_int_cntl);
 
        WREG32(INT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, crtc1);
        WREG32(INT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, crtc2);
@@ -1853,6 +1859,11 @@ restart_ih:
                case 181: /* CP EOP event */
                        DRM_DEBUG("IH: CP EOP\n");
                        break;
+               case 233: /* GUI IDLE */
+                       DRM_DEBUG("IH: CP EOP\n");
+                       rdev->pm.gui_idle = true;
+                       wake_up(&rdev->irq.idle_queue);
+                       break;
                default:
                        DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
                        break;
index e62116e..d352986 100644 (file)
@@ -262,6 +262,9 @@ int r100_irq_set(struct radeon_device *rdev)
        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 @@ static inline uint32_t r100_irq_ack(struct radeon_device *rdev)
                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 @@ int r100_irq_process(struct radeon_device *rdev)
        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 @@ int r100_irq_process(struct radeon_device *rdev)
                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 @@ int r100_irq_process(struct radeon_device *rdev)
                }
                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) {
index 1c85dcb..094c29d 100644 (file)
@@ -2535,6 +2535,7 @@ int r600_irq_set(struct radeon_device *rdev)
        u32 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE;
        u32 mode_int = 0;
        u32 hpd1, hpd2, hpd3, hpd4 = 0, hpd5 = 0, hpd6 = 0;
+       u32 grbm_int_cntl = 0;
        u32 hdmi1, hdmi2;
 
        if (!rdev->irq.installed) {
@@ -2611,9 +2612,14 @@ int r600_irq_set(struct radeon_device *rdev)
                DRM_DEBUG("r600_irq_set: hdmi 2\n");
                hdmi2 |= R600_HDMI_INT_EN;
        }
+       if (rdev->irq.gui_idle) {
+               DRM_DEBUG("gui idle\n");
+               grbm_int_cntl |= GUI_IDLE_INT_ENABLE;
+       }
 
        WREG32(CP_INT_CNTL, cp_int_cntl);
        WREG32(DxMODE_INT_MASK, mode_int);
+       WREG32(GRBM_INT_CNTL, grbm_int_cntl);
        WREG32(R600_HDMI_BLOCK1 + R600_HDMI_CNTL, hdmi1);
        if (ASIC_IS_DCE3(rdev)) {
                WREG32(R600_HDMI_BLOCK3 + R600_HDMI_CNTL, hdmi2);
@@ -2929,6 +2935,11 @@ restart_ih:
                case 181: /* CP EOP event */
                        DRM_DEBUG("IH: CP EOP\n");
                        break;
+               case 233: /* GUI IDLE */
+                       DRM_DEBUG("IH: CP EOP\n");
+                       rdev->pm.gui_idle = true;
+                       wake_up(&rdev->irq.idle_queue);
+                       break;
                default:
                        DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
                        break;
index bb4a2a6..4339371 100644 (file)
@@ -376,6 +376,9 @@ struct radeon_irq {
        wait_queue_head_t       vblank_queue;
        /* FIXME: use defines for max hpd/dacs */
        bool            hpd[6];
+       bool            gui_idle;
+       bool            gui_idle_acked;
+       wait_queue_head_t       idle_queue;
        /* FIXME: use defines for max HDMI blocks */
        bool            hdmi[2];
        spinlock_t sw_lock;
@@ -694,6 +697,7 @@ struct radeon_pm {
        int                     active_crtcs;
        int                     req_vblank;
        bool                    vblank_sync;
+       bool                    gui_idle;
        fixed20_12              max_bandwidth;
        fixed20_12              igp_sideport_mclk;
        fixed20_12              igp_system_mclk;
index 26217ff..53a2c27 100644 (file)
@@ -602,6 +602,7 @@ int radeon_device_init(struct radeon_device *rdev,
        rwlock_init(&rdev->fence_drv.lock);
        INIT_LIST_HEAD(&rdev->gem.objects);
        init_waitqueue_head(&rdev->irq.vblank_queue);
+       init_waitqueue_head(&rdev->irq.idle_queue);
 
        /* setup workqueue */
        rdev->wq = create_workqueue("radeon");
index 8fa40ed..059bfa4 100644 (file)
@@ -68,6 +68,7 @@ void radeon_driver_irq_preinstall_kms(struct drm_device *dev)
 
        /* Disable *all* interrupts */
        rdev->irq.sw_int = false;
+       rdev->irq.gui_idle = false;
        for (i = 0; i < rdev->num_crtc; i++)
                rdev->irq.crtc_vblank_int[i] = false;
        for (i = 0; i < 6; i++)
@@ -97,6 +98,7 @@ void radeon_driver_irq_uninstall_kms(struct drm_device *dev)
        }
        /* Disable *all* interrupts */
        rdev->irq.sw_int = false;
+       rdev->irq.gui_idle = false;
        for (i = 0; i < rdev->num_crtc; i++)
                rdev->irq.crtc_vblank_int[i] = false;
        for (i = 0; i < 6; i++)
index a4b5749..6dfeb9c 100644 (file)
@@ -27,6 +27,7 @@
 #define RADEON_IDLE_LOOP_MS 100
 #define RADEON_RECLOCK_DELAY_MS 200
 #define RADEON_WAIT_VBLANK_TIMEOUT 200
+#define RADEON_WAIT_IDLE_TIMEOUT 200
 
 static bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish);
 static void radeon_pm_set_clocks_locked(struct radeon_device *rdev);
index eabbc9c..74d1cbf 100644 (file)
 #      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_MASK             (1 << 19)
 #      define RADEON_SW_INT_ENABLE             (1 << 25)
 #define RADEON_GEN_INT_STATUS               0x0044
 #      define AVIVO_DISPLAY_INT_STATUS         (1 << 0)
 #      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 5e3f218..b312b72 100644 (file)
@@ -382,6 +382,9 @@ int rs600_irq_set(struct radeon_device *rdev)
        if (rdev->irq.sw_int) {
                tmp |= S_000040_SW_INT_EN(1);
        }
+       if (rdev->irq.gui_idle) {
+               tmp |= S_000040_GUI_IDLE(1);
+       }
        if (rdev->irq.crtc_vblank_int[0]) {
                mode_int |= S_006540_D1MODE_VBLANK_INT_MASK(1);
        }
@@ -404,9 +407,15 @@ int rs600_irq_set(struct radeon_device *rdev)
 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 = S_000044_SW_INT(1);
        u32 tmp;
 
+       /* 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 |= S_000044_GUI_IDLE_STAT(1);
+       }
+
        if (G_000044_DISPLAY_INT_STAT(irqs)) {
                *r500_disp_int = RREG32(R_007EDC_DISP_INTERRUPT_STATUS);
                if (G_007EDC_LB_D1_VBLANK_INTERRUPT(*r500_disp_int)) {
@@ -454,6 +463,9 @@ int rs600_irq_process(struct radeon_device *rdev)
        uint32_t r500_disp_int;
        bool queue_hotplug = false;
 
+       /* reset gui idle ack.  the status bit is broken */
+       rdev->irq.gui_idle_acked = false;
+
        status = rs600_irq_ack(rdev, &r500_disp_int);
        if (!status && !r500_disp_int) {
                return IRQ_NONE;
@@ -462,6 +474,12 @@ int rs600_irq_process(struct radeon_device *rdev)
                /* SW interrupt */
                if (G_000044_SW_INT(status))
                        radeon_fence_process(rdev);
+               /* GUI idle */
+               if (G_000040_GUI_IDLE(status)) {
+                       rdev->irq.gui_idle_acked = true;
+                       rdev->pm.gui_idle = true;
+                       wake_up(&rdev->irq.idle_queue);
+               }
                /* Vertical blank interrupts */
                if (G_007EDC_LB_D1_VBLANK_INTERRUPT(r500_disp_int)) {
                        drm_handle_vblank(rdev->ddev, 0);
@@ -483,6 +501,8 @@ int rs600_irq_process(struct radeon_device *rdev)
                }
                status = rs600_irq_ack(rdev, &r500_disp_int);
        }
+       /* 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) {