Commit 2031f77c authored by Alex Deucher's avatar Alex Deucher Committed by Dave Airlie

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: default avatarAlex Deucher <alexdeucher@gmail.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent def9ba9c
...@@ -1418,6 +1418,7 @@ int evergreen_irq_set(struct radeon_device *rdev) ...@@ -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 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 crtc1 = 0, crtc2 = 0, crtc3 = 0, crtc4 = 0, crtc5 = 0, crtc6 = 0;
u32 hpd1, hpd2, hpd3, hpd4, hpd5, hpd6; u32 hpd1, hpd2, hpd3, hpd4, hpd5, hpd6;
u32 grbm_int_cntl = 0;
if (!rdev->irq.installed) { if (!rdev->irq.installed) {
WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n"); 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) ...@@ -1490,8 +1491,13 @@ int evergreen_irq_set(struct radeon_device *rdev)
DRM_DEBUG("evergreen_irq_set: hpd 6\n"); DRM_DEBUG("evergreen_irq_set: hpd 6\n");
hpd6 |= DC_HPDx_INT_EN; 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(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_CRTC0_REGISTER_OFFSET, crtc1);
WREG32(INT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, crtc2); WREG32(INT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, crtc2);
...@@ -1853,6 +1859,11 @@ int evergreen_irq_process(struct radeon_device *rdev) ...@@ -1853,6 +1859,11 @@ int evergreen_irq_process(struct radeon_device *rdev)
case 181: /* CP EOP event */ case 181: /* CP EOP event */
DRM_DEBUG("IH: CP EOP\n"); DRM_DEBUG("IH: CP EOP\n");
break; break;
case 233: /* GUI IDLE */
DRM_DEBUG("IH: CP EOP\n");
rdev->pm.gui_idle = true;
wake_up(&rdev->irq.idle_queue);
break;
default: default:
DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
break; break;
......
...@@ -262,6 +262,9 @@ int r100_irq_set(struct radeon_device *rdev) ...@@ -262,6 +262,9 @@ int r100_irq_set(struct radeon_device *rdev)
if (rdev->irq.sw_int) { if (rdev->irq.sw_int) {
tmp |= RADEON_SW_INT_ENABLE; tmp |= RADEON_SW_INT_ENABLE;
} }
if (rdev->irq.gui_idle) {
tmp |= RADEON_GUI_IDLE_MASK;
}
if (rdev->irq.crtc_vblank_int[0]) { if (rdev->irq.crtc_vblank_int[0]) {
tmp |= RADEON_CRTC_VBLANK_MASK; tmp |= RADEON_CRTC_VBLANK_MASK;
} }
...@@ -296,6 +299,12 @@ static inline uint32_t r100_irq_ack(struct radeon_device *rdev) ...@@ -296,6 +299,12 @@ static inline uint32_t r100_irq_ack(struct radeon_device *rdev)
RADEON_CRTC_VBLANK_STAT | RADEON_CRTC2_VBLANK_STAT | RADEON_CRTC_VBLANK_STAT | RADEON_CRTC2_VBLANK_STAT |
RADEON_FP_DETECT_STAT | RADEON_FP2_DETECT_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) { if (irqs) {
WREG32(RADEON_GEN_INT_STATUS, irqs); WREG32(RADEON_GEN_INT_STATUS, irqs);
} }
...@@ -307,6 +316,9 @@ int r100_irq_process(struct radeon_device *rdev) ...@@ -307,6 +316,9 @@ int r100_irq_process(struct radeon_device *rdev)
uint32_t status, msi_rearm; uint32_t status, msi_rearm;
bool queue_hotplug = false; bool queue_hotplug = false;
/* reset gui idle ack. the status bit is broken */
rdev->irq.gui_idle_acked = false;
status = r100_irq_ack(rdev); status = r100_irq_ack(rdev);
if (!status) { if (!status) {
return IRQ_NONE; return IRQ_NONE;
...@@ -319,6 +331,12 @@ int r100_irq_process(struct radeon_device *rdev) ...@@ -319,6 +331,12 @@ int r100_irq_process(struct radeon_device *rdev)
if (status & RADEON_SW_INT_TEST) { if (status & RADEON_SW_INT_TEST) {
radeon_fence_process(rdev); 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 */ /* Vertical blank interrupts */
if (status & RADEON_CRTC_VBLANK_STAT) { if (status & RADEON_CRTC_VBLANK_STAT) {
drm_handle_vblank(rdev->ddev, 0); drm_handle_vblank(rdev->ddev, 0);
...@@ -340,6 +358,8 @@ int r100_irq_process(struct radeon_device *rdev) ...@@ -340,6 +358,8 @@ int r100_irq_process(struct radeon_device *rdev)
} }
status = r100_irq_ack(rdev); status = r100_irq_ack(rdev);
} }
/* reset gui idle ack. the status bit is broken */
rdev->irq.gui_idle_acked = false;
if (queue_hotplug) if (queue_hotplug)
queue_work(rdev->wq, &rdev->hotplug_work); queue_work(rdev->wq, &rdev->hotplug_work);
if (rdev->msi_enabled) { if (rdev->msi_enabled) {
......
...@@ -2535,6 +2535,7 @@ int r600_irq_set(struct radeon_device *rdev) ...@@ -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 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE;
u32 mode_int = 0; u32 mode_int = 0;
u32 hpd1, hpd2, hpd3, hpd4 = 0, hpd5 = 0, hpd6 = 0; u32 hpd1, hpd2, hpd3, hpd4 = 0, hpd5 = 0, hpd6 = 0;
u32 grbm_int_cntl = 0;
u32 hdmi1, hdmi2; u32 hdmi1, hdmi2;
if (!rdev->irq.installed) { if (!rdev->irq.installed) {
...@@ -2611,9 +2612,14 @@ int r600_irq_set(struct radeon_device *rdev) ...@@ -2611,9 +2612,14 @@ int r600_irq_set(struct radeon_device *rdev)
DRM_DEBUG("r600_irq_set: hdmi 2\n"); DRM_DEBUG("r600_irq_set: hdmi 2\n");
hdmi2 |= R600_HDMI_INT_EN; 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(CP_INT_CNTL, cp_int_cntl);
WREG32(DxMODE_INT_MASK, mode_int); WREG32(DxMODE_INT_MASK, mode_int);
WREG32(GRBM_INT_CNTL, grbm_int_cntl);
WREG32(R600_HDMI_BLOCK1 + R600_HDMI_CNTL, hdmi1); WREG32(R600_HDMI_BLOCK1 + R600_HDMI_CNTL, hdmi1);
if (ASIC_IS_DCE3(rdev)) { if (ASIC_IS_DCE3(rdev)) {
WREG32(R600_HDMI_BLOCK3 + R600_HDMI_CNTL, hdmi2); WREG32(R600_HDMI_BLOCK3 + R600_HDMI_CNTL, hdmi2);
...@@ -2929,6 +2935,11 @@ int r600_irq_process(struct radeon_device *rdev) ...@@ -2929,6 +2935,11 @@ int r600_irq_process(struct radeon_device *rdev)
case 181: /* CP EOP event */ case 181: /* CP EOP event */
DRM_DEBUG("IH: CP EOP\n"); DRM_DEBUG("IH: CP EOP\n");
break; break;
case 233: /* GUI IDLE */
DRM_DEBUG("IH: CP EOP\n");
rdev->pm.gui_idle = true;
wake_up(&rdev->irq.idle_queue);
break;
default: default:
DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
break; break;
......
...@@ -376,6 +376,9 @@ struct radeon_irq { ...@@ -376,6 +376,9 @@ struct radeon_irq {
wait_queue_head_t vblank_queue; wait_queue_head_t vblank_queue;
/* FIXME: use defines for max hpd/dacs */ /* FIXME: use defines for max hpd/dacs */
bool hpd[6]; bool hpd[6];
bool gui_idle;
bool gui_idle_acked;
wait_queue_head_t idle_queue;
/* FIXME: use defines for max HDMI blocks */ /* FIXME: use defines for max HDMI blocks */
bool hdmi[2]; bool hdmi[2];
spinlock_t sw_lock; spinlock_t sw_lock;
...@@ -694,6 +697,7 @@ struct radeon_pm { ...@@ -694,6 +697,7 @@ struct radeon_pm {
int active_crtcs; int active_crtcs;
int req_vblank; int req_vblank;
bool vblank_sync; bool vblank_sync;
bool gui_idle;
fixed20_12 max_bandwidth; fixed20_12 max_bandwidth;
fixed20_12 igp_sideport_mclk; fixed20_12 igp_sideport_mclk;
fixed20_12 igp_system_mclk; fixed20_12 igp_system_mclk;
......
...@@ -602,6 +602,7 @@ int radeon_device_init(struct radeon_device *rdev, ...@@ -602,6 +602,7 @@ int radeon_device_init(struct radeon_device *rdev,
rwlock_init(&rdev->fence_drv.lock); rwlock_init(&rdev->fence_drv.lock);
INIT_LIST_HEAD(&rdev->gem.objects); INIT_LIST_HEAD(&rdev->gem.objects);
init_waitqueue_head(&rdev->irq.vblank_queue); init_waitqueue_head(&rdev->irq.vblank_queue);
init_waitqueue_head(&rdev->irq.idle_queue);
/* setup workqueue */ /* setup workqueue */
rdev->wq = create_workqueue("radeon"); rdev->wq = create_workqueue("radeon");
......
...@@ -68,6 +68,7 @@ void radeon_driver_irq_preinstall_kms(struct drm_device *dev) ...@@ -68,6 +68,7 @@ void radeon_driver_irq_preinstall_kms(struct drm_device *dev)
/* Disable *all* interrupts */ /* Disable *all* interrupts */
rdev->irq.sw_int = false; rdev->irq.sw_int = false;
rdev->irq.gui_idle = false;
for (i = 0; i < rdev->num_crtc; i++) for (i = 0; i < rdev->num_crtc; i++)
rdev->irq.crtc_vblank_int[i] = false; rdev->irq.crtc_vblank_int[i] = false;
for (i = 0; i < 6; i++) for (i = 0; i < 6; i++)
...@@ -97,6 +98,7 @@ void radeon_driver_irq_uninstall_kms(struct drm_device *dev) ...@@ -97,6 +98,7 @@ void radeon_driver_irq_uninstall_kms(struct drm_device *dev)
} }
/* Disable *all* interrupts */ /* Disable *all* interrupts */
rdev->irq.sw_int = false; rdev->irq.sw_int = false;
rdev->irq.gui_idle = false;
for (i = 0; i < rdev->num_crtc; i++) for (i = 0; i < rdev->num_crtc; i++)
rdev->irq.crtc_vblank_int[i] = false; rdev->irq.crtc_vblank_int[i] = false;
for (i = 0; i < 6; i++) for (i = 0; i < 6; i++)
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#define RADEON_IDLE_LOOP_MS 100 #define RADEON_IDLE_LOOP_MS 100
#define RADEON_RECLOCK_DELAY_MS 200 #define RADEON_RECLOCK_DELAY_MS 200
#define RADEON_WAIT_VBLANK_TIMEOUT 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 bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish);
static void radeon_pm_set_clocks_locked(struct radeon_device *rdev); static void radeon_pm_set_clocks_locked(struct radeon_device *rdev);
......
...@@ -995,6 +995,7 @@ ...@@ -995,6 +995,7 @@
# define RADEON_FP_DETECT_MASK (1 << 4) # define RADEON_FP_DETECT_MASK (1 << 4)
# define RADEON_CRTC2_VBLANK_MASK (1 << 9) # define RADEON_CRTC2_VBLANK_MASK (1 << 9)
# define RADEON_FP2_DETECT_MASK (1 << 10) # define RADEON_FP2_DETECT_MASK (1 << 10)
# define RADEON_GUI_IDLE_MASK (1 << 19)
# define RADEON_SW_INT_ENABLE (1 << 25) # define RADEON_SW_INT_ENABLE (1 << 25)
#define RADEON_GEN_INT_STATUS 0x0044 #define RADEON_GEN_INT_STATUS 0x0044
# define AVIVO_DISPLAY_INT_STATUS (1 << 0) # define AVIVO_DISPLAY_INT_STATUS (1 << 0)
...@@ -1006,6 +1007,8 @@ ...@@ -1006,6 +1007,8 @@
# define RADEON_CRTC2_VBLANK_STAT_ACK (1 << 9) # define RADEON_CRTC2_VBLANK_STAT_ACK (1 << 9)
# define RADEON_FP2_DETECT_STAT (1 << 10) # define RADEON_FP2_DETECT_STAT (1 << 10)
# define RADEON_FP2_DETECT_STAT_ACK (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_FIRE (1 << 26)
# define RADEON_SW_INT_TEST (1 << 25) # define RADEON_SW_INT_TEST (1 << 25)
# define RADEON_SW_INT_TEST_ACK (1 << 25) # define RADEON_SW_INT_TEST_ACK (1 << 25)
......
...@@ -382,6 +382,9 @@ int rs600_irq_set(struct radeon_device *rdev) ...@@ -382,6 +382,9 @@ int rs600_irq_set(struct radeon_device *rdev)
if (rdev->irq.sw_int) { if (rdev->irq.sw_int) {
tmp |= S_000040_SW_INT_EN(1); 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]) { if (rdev->irq.crtc_vblank_int[0]) {
mode_int |= S_006540_D1MODE_VBLANK_INT_MASK(1); mode_int |= S_006540_D1MODE_VBLANK_INT_MASK(1);
} }
...@@ -404,9 +407,15 @@ int rs600_irq_set(struct radeon_device *rdev) ...@@ -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) 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 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; 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)) { if (G_000044_DISPLAY_INT_STAT(irqs)) {
*r500_disp_int = RREG32(R_007EDC_DISP_INTERRUPT_STATUS); *r500_disp_int = RREG32(R_007EDC_DISP_INTERRUPT_STATUS);
if (G_007EDC_LB_D1_VBLANK_INTERRUPT(*r500_disp_int)) { if (G_007EDC_LB_D1_VBLANK_INTERRUPT(*r500_disp_int)) {
...@@ -454,6 +463,9 @@ int rs600_irq_process(struct radeon_device *rdev) ...@@ -454,6 +463,9 @@ int rs600_irq_process(struct radeon_device *rdev)
uint32_t r500_disp_int; uint32_t r500_disp_int;
bool queue_hotplug = false; 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); status = rs600_irq_ack(rdev, &r500_disp_int);
if (!status && !r500_disp_int) { if (!status && !r500_disp_int) {
return IRQ_NONE; return IRQ_NONE;
...@@ -462,6 +474,12 @@ int rs600_irq_process(struct radeon_device *rdev) ...@@ -462,6 +474,12 @@ int rs600_irq_process(struct radeon_device *rdev)
/* SW interrupt */ /* SW interrupt */
if (G_000044_SW_INT(status)) if (G_000044_SW_INT(status))
radeon_fence_process(rdev); 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 */ /* Vertical blank interrupts */
if (G_007EDC_LB_D1_VBLANK_INTERRUPT(r500_disp_int)) { if (G_007EDC_LB_D1_VBLANK_INTERRUPT(r500_disp_int)) {
drm_handle_vblank(rdev->ddev, 0); drm_handle_vblank(rdev->ddev, 0);
...@@ -483,6 +501,8 @@ int rs600_irq_process(struct radeon_device *rdev) ...@@ -483,6 +501,8 @@ int rs600_irq_process(struct radeon_device *rdev)
} }
status = rs600_irq_ack(rdev, &r500_disp_int); 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) if (queue_hotplug)
queue_work(rdev->wq, &rdev->hotplug_work); queue_work(rdev->wq, &rdev->hotplug_work);
if (rdev->msi_enabled) { if (rdev->msi_enabled) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment