This set of patches adds pageflipping support for radeon. There are two drm patches and one ddx patch required. The drm patches are against Dave's drm-next branch and the ddx patch is against git master. So far it's been lightly tested with openarena on a variety of radeons. Feedback appreciated.
Alex
Signed-off-by: Alex Deucher alexdeucher@gmail.com --- drivers/gpu/drm/radeon/evergreen.c | 228 ++++++++++++++++++++----------- drivers/gpu/drm/radeon/r600.c | 120 ++++++++++------- drivers/gpu/drm/radeon/r600d.h | 9 ++ drivers/gpu/drm/radeon/radeon.h | 35 +++++ drivers/gpu/drm/radeon/radeon_irq_kms.c | 8 +- drivers/gpu/drm/radeon/rs600.c | 37 +++--- 6 files changed, 291 insertions(+), 146 deletions(-)
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index f12a5b3..242055a 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -2031,6 +2031,7 @@ int evergreen_irq_set(struct radeon_device *rdev) 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; + u32 grph1 = 0, grph2 = 0, grph3 = 0, grph4 = 0, grph5 = 0, grph6 = 0;
if (!rdev->irq.installed) { WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n"); @@ -2108,6 +2109,30 @@ int evergreen_irq_set(struct radeon_device *rdev) DRM_DEBUG("gui idle\n"); grbm_int_cntl |= GUI_IDLE_INT_ENABLE; } + if (rdev->irq.pflip[0]) { + DRM_DEBUG("pflip 0\n"); + grph1 |= GRPH_PFLIP_INT_MASK; + } + if (rdev->irq.pflip[1]) { + DRM_DEBUG("pflip 1\n"); + grph2 |= GRPH_PFLIP_INT_MASK; + } + if (rdev->irq.pflip[2]) { + DRM_DEBUG("pflip 2\n"); + grph3 |= GRPH_PFLIP_INT_MASK; + } + if (rdev->irq.pflip[3]) { + DRM_DEBUG("pflip 3\n"); + grph4 |= GRPH_PFLIP_INT_MASK; + } + if (rdev->irq.pflip[4]) { + DRM_DEBUG("pflip 4\n"); + grph5 |= GRPH_PFLIP_INT_MASK; + } + if (rdev->irq.pflip[5]) { + DRM_DEBUG("pflip 5\n"); + grph6 |= GRPH_PFLIP_INT_MASK; + }
WREG32(CP_INT_CNTL, cp_int_cntl); WREG32(GRBM_INT_CNTL, grbm_int_cntl); @@ -2119,6 +2144,13 @@ int evergreen_irq_set(struct radeon_device *rdev) WREG32(INT_MASK + EVERGREEN_CRTC4_REGISTER_OFFSET, crtc5); WREG32(INT_MASK + EVERGREEN_CRTC5_REGISTER_OFFSET, crtc6);
+ WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, grph1); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, grph2); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, grph3); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, grph4); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, grph5); + WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, grph6); + WREG32(DC_HPD1_INT_CONTROL, hpd1); WREG32(DC_HPD2_INT_CONTROL, hpd2); WREG32(DC_HPD3_INT_CONTROL, hpd3); @@ -2129,79 +2161,92 @@ int evergreen_irq_set(struct radeon_device *rdev) return 0; }
-static inline void evergreen_irq_ack(struct radeon_device *rdev, - u32 *disp_int, - u32 *disp_int_cont, - u32 *disp_int_cont2, - u32 *disp_int_cont3, - u32 *disp_int_cont4, - u32 *disp_int_cont5) +static inline void evergreen_irq_ack(struct radeon_device *rdev) { u32 tmp;
- *disp_int = RREG32(DISP_INTERRUPT_STATUS); - *disp_int_cont = RREG32(DISP_INTERRUPT_STATUS_CONTINUE); - *disp_int_cont2 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE2); - *disp_int_cont3 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE3); - *disp_int_cont4 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE4); - *disp_int_cont5 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE5); - - if (*disp_int & LB_D1_VBLANK_INTERRUPT) + rdev->irq.stat_regs.evergreen.disp_int = RREG32(DISP_INTERRUPT_STATUS); + rdev->irq.stat_regs.evergreen.disp_int_cont = RREG32(DISP_INTERRUPT_STATUS_CONTINUE); + rdev->irq.stat_regs.evergreen.disp_int_cont2 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE2); + rdev->irq.stat_regs.evergreen.disp_int_cont3 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE3); + rdev->irq.stat_regs.evergreen.disp_int_cont4 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE4); + rdev->irq.stat_regs.evergreen.disp_int_cont5 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE5); + rdev->irq.stat_regs.evergreen.d1grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET); + rdev->irq.stat_regs.evergreen.d2grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET); + rdev->irq.stat_regs.evergreen.d3grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET); + rdev->irq.stat_regs.evergreen.d4grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET); + rdev->irq.stat_regs.evergreen.d5grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET); + rdev->irq.stat_regs.evergreen.d6grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET); + + if (rdev->irq.stat_regs.evergreen.d1grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.evergreen.d2grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.evergreen.d3grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.evergreen.d4grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.evergreen.d5grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.evergreen.d6grph_int & GRPH_PFLIP_INT_OCCURRED) + WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR); + + if (rdev->irq.stat_regs.evergreen.disp_int & LB_D1_VBLANK_INTERRUPT) WREG32(VBLANK_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET, VBLANK_ACK); - if (*disp_int & LB_D1_VLINE_INTERRUPT) + if (rdev->irq.stat_regs.evergreen.disp_int & LB_D1_VLINE_INTERRUPT) WREG32(VLINE_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET, VLINE_ACK);
- if (*disp_int_cont & LB_D2_VBLANK_INTERRUPT) + if (rdev->irq.stat_regs.evergreen.disp_int_cont & LB_D2_VBLANK_INTERRUPT) WREG32(VBLANK_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET, VBLANK_ACK); - if (*disp_int_cont & LB_D2_VLINE_INTERRUPT) + if (rdev->irq.stat_regs.evergreen.disp_int_cont & LB_D2_VLINE_INTERRUPT) WREG32(VLINE_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET, VLINE_ACK);
- if (*disp_int_cont2 & LB_D3_VBLANK_INTERRUPT) + if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & LB_D3_VBLANK_INTERRUPT) WREG32(VBLANK_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET, VBLANK_ACK); - if (*disp_int_cont2 & LB_D3_VLINE_INTERRUPT) + if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & LB_D3_VLINE_INTERRUPT) WREG32(VLINE_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET, VLINE_ACK);
- if (*disp_int_cont3 & LB_D4_VBLANK_INTERRUPT) + if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & LB_D4_VBLANK_INTERRUPT) WREG32(VBLANK_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET, VBLANK_ACK); - if (*disp_int_cont3 & LB_D4_VLINE_INTERRUPT) + if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & LB_D4_VLINE_INTERRUPT) WREG32(VLINE_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET, VLINE_ACK);
- if (*disp_int_cont4 & LB_D5_VBLANK_INTERRUPT) + if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & LB_D5_VBLANK_INTERRUPT) WREG32(VBLANK_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET, VBLANK_ACK); - if (*disp_int_cont4 & LB_D5_VLINE_INTERRUPT) + if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & LB_D5_VLINE_INTERRUPT) WREG32(VLINE_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET, VLINE_ACK);
- if (*disp_int_cont5 & LB_D6_VBLANK_INTERRUPT) + if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & LB_D6_VBLANK_INTERRUPT) WREG32(VBLANK_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET, VBLANK_ACK); - if (*disp_int_cont5 & LB_D6_VLINE_INTERRUPT) + if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & LB_D6_VLINE_INTERRUPT) WREG32(VLINE_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET, VLINE_ACK);
- if (*disp_int & DC_HPD1_INTERRUPT) { + if (rdev->irq.stat_regs.evergreen.disp_int & DC_HPD1_INTERRUPT) { tmp = RREG32(DC_HPD1_INT_CONTROL); tmp |= DC_HPDx_INT_ACK; WREG32(DC_HPD1_INT_CONTROL, tmp); } - if (*disp_int_cont & DC_HPD2_INTERRUPT) { + if (rdev->irq.stat_regs.evergreen.disp_int_cont & DC_HPD2_INTERRUPT) { tmp = RREG32(DC_HPD2_INT_CONTROL); tmp |= DC_HPDx_INT_ACK; WREG32(DC_HPD2_INT_CONTROL, tmp); } - if (*disp_int_cont2 & DC_HPD3_INTERRUPT) { + if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & DC_HPD3_INTERRUPT) { tmp = RREG32(DC_HPD3_INT_CONTROL); tmp |= DC_HPDx_INT_ACK; WREG32(DC_HPD3_INT_CONTROL, tmp); } - if (*disp_int_cont3 & DC_HPD4_INTERRUPT) { + if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & DC_HPD4_INTERRUPT) { tmp = RREG32(DC_HPD4_INT_CONTROL); tmp |= DC_HPDx_INT_ACK; WREG32(DC_HPD4_INT_CONTROL, tmp); } - if (*disp_int_cont4 & DC_HPD5_INTERRUPT) { + if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & DC_HPD5_INTERRUPT) { tmp = RREG32(DC_HPD5_INT_CONTROL); tmp |= DC_HPDx_INT_ACK; WREG32(DC_HPD5_INT_CONTROL, tmp); } - if (*disp_int_cont5 & DC_HPD6_INTERRUPT) { + if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & DC_HPD6_INTERRUPT) { tmp = RREG32(DC_HPD5_INT_CONTROL); tmp |= DC_HPDx_INT_ACK; WREG32(DC_HPD6_INT_CONTROL, tmp); @@ -2210,14 +2255,10 @@ static inline void evergreen_irq_ack(struct radeon_device *rdev,
void evergreen_irq_disable(struct radeon_device *rdev) { - u32 disp_int, disp_int_cont, disp_int_cont2; - u32 disp_int_cont3, disp_int_cont4, disp_int_cont5; - r600_disable_interrupts(rdev); /* Wait and acknowledge irq */ mdelay(1); - evergreen_irq_ack(rdev, &disp_int, &disp_int_cont, &disp_int_cont2, - &disp_int_cont3, &disp_int_cont4, &disp_int_cont5); + evergreen_irq_ack(rdev); evergreen_disable_interrupt_state(rdev); }
@@ -2257,8 +2298,6 @@ int evergreen_irq_process(struct radeon_device *rdev) u32 rptr = rdev->ih.rptr; u32 src_id, src_data; u32 ring_index; - u32 disp_int, disp_int_cont, disp_int_cont2; - u32 disp_int_cont3, disp_int_cont4, disp_int_cont5; unsigned long flags; bool queue_hotplug = false;
@@ -2279,8 +2318,7 @@ int evergreen_irq_process(struct radeon_device *rdev)
restart_ih: /* display interrupts */ - evergreen_irq_ack(rdev, &disp_int, &disp_int_cont, &disp_int_cont2, - &disp_int_cont3, &disp_int_cont4, &disp_int_cont5); + evergreen_irq_ack(rdev);
rdev->ih.wptr = wptr; while (rptr != wptr) { @@ -2293,16 +2331,16 @@ restart_ih: case 1: /* D1 vblank/vline */ switch (src_data) { case 0: /* D1 vblank */ - if (disp_int & LB_D1_VBLANK_INTERRUPT) { + if (rdev->irq.stat_regs.evergreen.disp_int & LB_D1_VBLANK_INTERRUPT) { drm_handle_vblank(rdev->ddev, 0); wake_up(&rdev->irq.vblank_queue); - disp_int &= ~LB_D1_VBLANK_INTERRUPT; + rdev->irq.stat_regs.evergreen.disp_int &= ~LB_D1_VBLANK_INTERRUPT; DRM_DEBUG("IH: D1 vblank\n"); } break; case 1: /* D1 vline */ - if (disp_int & LB_D1_VLINE_INTERRUPT) { - disp_int &= ~LB_D1_VLINE_INTERRUPT; + if (rdev->irq.stat_regs.evergreen.disp_int & LB_D1_VLINE_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int &= ~LB_D1_VLINE_INTERRUPT; DRM_DEBUG("IH: D1 vline\n"); } break; @@ -2314,16 +2352,16 @@ restart_ih: case 2: /* D2 vblank/vline */ switch (src_data) { case 0: /* D2 vblank */ - if (disp_int_cont & LB_D2_VBLANK_INTERRUPT) { + if (rdev->irq.stat_regs.evergreen.disp_int_cont & LB_D2_VBLANK_INTERRUPT) { drm_handle_vblank(rdev->ddev, 1); wake_up(&rdev->irq.vblank_queue); - disp_int_cont &= ~LB_D2_VBLANK_INTERRUPT; + rdev->irq.stat_regs.evergreen.disp_int_cont &= ~LB_D2_VBLANK_INTERRUPT; DRM_DEBUG("IH: D2 vblank\n"); } break; case 1: /* D2 vline */ - if (disp_int_cont & LB_D2_VLINE_INTERRUPT) { - disp_int_cont &= ~LB_D2_VLINE_INTERRUPT; + if (rdev->irq.stat_regs.evergreen.disp_int_cont & LB_D2_VLINE_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont &= ~LB_D2_VLINE_INTERRUPT; DRM_DEBUG("IH: D2 vline\n"); } break; @@ -2335,16 +2373,16 @@ restart_ih: case 3: /* D3 vblank/vline */ switch (src_data) { case 0: /* D3 vblank */ - if (disp_int_cont2 & LB_D3_VBLANK_INTERRUPT) { + if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & LB_D3_VBLANK_INTERRUPT) { drm_handle_vblank(rdev->ddev, 2); wake_up(&rdev->irq.vblank_queue); - disp_int_cont2 &= ~LB_D3_VBLANK_INTERRUPT; + rdev->irq.stat_regs.evergreen.disp_int_cont2 &= ~LB_D3_VBLANK_INTERRUPT; DRM_DEBUG("IH: D3 vblank\n"); } break; case 1: /* D3 vline */ - if (disp_int_cont2 & LB_D3_VLINE_INTERRUPT) { - disp_int_cont2 &= ~LB_D3_VLINE_INTERRUPT; + if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & LB_D3_VLINE_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont2 &= ~LB_D3_VLINE_INTERRUPT; DRM_DEBUG("IH: D3 vline\n"); } break; @@ -2356,16 +2394,16 @@ restart_ih: case 4: /* D4 vblank/vline */ switch (src_data) { case 0: /* D4 vblank */ - if (disp_int_cont3 & LB_D4_VBLANK_INTERRUPT) { + if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & LB_D4_VBLANK_INTERRUPT) { drm_handle_vblank(rdev->ddev, 3); wake_up(&rdev->irq.vblank_queue); - disp_int_cont3 &= ~LB_D4_VBLANK_INTERRUPT; + rdev->irq.stat_regs.evergreen.disp_int_cont3 &= ~LB_D4_VBLANK_INTERRUPT; DRM_DEBUG("IH: D4 vblank\n"); } break; case 1: /* D4 vline */ - if (disp_int_cont3 & LB_D4_VLINE_INTERRUPT) { - disp_int_cont3 &= ~LB_D4_VLINE_INTERRUPT; + if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & LB_D4_VLINE_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont3 &= ~LB_D4_VLINE_INTERRUPT; DRM_DEBUG("IH: D4 vline\n"); } break; @@ -2377,16 +2415,16 @@ restart_ih: case 5: /* D5 vblank/vline */ switch (src_data) { case 0: /* D5 vblank */ - if (disp_int_cont4 & LB_D5_VBLANK_INTERRUPT) { + if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & LB_D5_VBLANK_INTERRUPT) { drm_handle_vblank(rdev->ddev, 4); wake_up(&rdev->irq.vblank_queue); - disp_int_cont4 &= ~LB_D5_VBLANK_INTERRUPT; + rdev->irq.stat_regs.evergreen.disp_int_cont4 &= ~LB_D5_VBLANK_INTERRUPT; DRM_DEBUG("IH: D5 vblank\n"); } break; case 1: /* D5 vline */ - if (disp_int_cont4 & LB_D5_VLINE_INTERRUPT) { - disp_int_cont4 &= ~LB_D5_VLINE_INTERRUPT; + if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & LB_D5_VLINE_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont4 &= ~LB_D5_VLINE_INTERRUPT; DRM_DEBUG("IH: D5 vline\n"); } break; @@ -2398,16 +2436,16 @@ restart_ih: case 6: /* D6 vblank/vline */ switch (src_data) { case 0: /* D6 vblank */ - if (disp_int_cont5 & LB_D6_VBLANK_INTERRUPT) { + if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & LB_D6_VBLANK_INTERRUPT) { drm_handle_vblank(rdev->ddev, 5); wake_up(&rdev->irq.vblank_queue); - disp_int_cont5 &= ~LB_D6_VBLANK_INTERRUPT; + rdev->irq.stat_regs.evergreen.disp_int_cont5 &= ~LB_D6_VBLANK_INTERRUPT; DRM_DEBUG("IH: D6 vblank\n"); } break; case 1: /* D6 vline */ - if (disp_int_cont5 & LB_D6_VLINE_INTERRUPT) { - disp_int_cont5 &= ~LB_D6_VLINE_INTERRUPT; + if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & LB_D6_VLINE_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont5 &= ~LB_D6_VLINE_INTERRUPT; DRM_DEBUG("IH: D6 vline\n"); } break; @@ -2416,46 +2454,82 @@ restart_ih: break; } break; + case 8: /* D1 pflip */ + if (rdev->irq.stat_regs.evergreen.d1grph_int & GRPH_PFLIP_INT_OCCURRED) { + rdev->irq.stat_regs.evergreen.d1grph_int &= ~GRPH_PFLIP_INT_OCCURRED; + DRM_DEBUG("IH: D1 pflip\n"); + } + break; + case 10: /* D2 pflip */ + if (rdev->irq.stat_regs.evergreen.d2grph_int & GRPH_PFLIP_INT_OCCURRED) { + rdev->irq.stat_regs.evergreen.d2grph_int &= ~GRPH_PFLIP_INT_OCCURRED; + DRM_DEBUG("IH: D2 pflip\n"); + } + break; + case 12: /* D3 pflip */ + if (rdev->irq.stat_regs.evergreen.d3grph_int & GRPH_PFLIP_INT_OCCURRED) { + rdev->irq.stat_regs.evergreen.d3grph_int &= ~GRPH_PFLIP_INT_OCCURRED; + DRM_DEBUG("IH: D3 pflip\n"); + } + break; + case 14: /* D4 pflip */ + if (rdev->irq.stat_regs.evergreen.d4grph_int & GRPH_PFLIP_INT_OCCURRED) { + rdev->irq.stat_regs.evergreen.d4grph_int &= ~GRPH_PFLIP_INT_OCCURRED; + DRM_DEBUG("IH: D4 pflip\n"); + } + break; + case 16: /* D5 pflip */ + if (rdev->irq.stat_regs.evergreen.d5grph_int & GRPH_PFLIP_INT_OCCURRED) { + rdev->irq.stat_regs.evergreen.d5grph_int &= ~GRPH_PFLIP_INT_OCCURRED; + DRM_DEBUG("IH: D5 pflip\n"); + } + break; + case 18: /* D6 pflip */ + if (rdev->irq.stat_regs.evergreen.d6grph_int & GRPH_PFLIP_INT_OCCURRED) { + rdev->irq.stat_regs.evergreen.d6grph_int &= ~GRPH_PFLIP_INT_OCCURRED; + DRM_DEBUG("IH: D6 pflip\n"); + } + break; case 42: /* HPD hotplug */ switch (src_data) { case 0: - if (disp_int & DC_HPD1_INTERRUPT) { - disp_int &= ~DC_HPD1_INTERRUPT; + if (rdev->irq.stat_regs.evergreen.disp_int & DC_HPD1_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int &= ~DC_HPD1_INTERRUPT; queue_hotplug = true; DRM_DEBUG("IH: HPD1\n"); } break; case 1: - if (disp_int_cont & DC_HPD2_INTERRUPT) { - disp_int_cont &= ~DC_HPD2_INTERRUPT; + if (rdev->irq.stat_regs.evergreen.disp_int_cont & DC_HPD2_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont &= ~DC_HPD2_INTERRUPT; queue_hotplug = true; DRM_DEBUG("IH: HPD2\n"); } break; case 2: - if (disp_int_cont2 & DC_HPD3_INTERRUPT) { - disp_int_cont2 &= ~DC_HPD3_INTERRUPT; + if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & DC_HPD3_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont2 &= ~DC_HPD3_INTERRUPT; queue_hotplug = true; DRM_DEBUG("IH: HPD3\n"); } break; case 3: - if (disp_int_cont3 & DC_HPD4_INTERRUPT) { - disp_int_cont3 &= ~DC_HPD4_INTERRUPT; + if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & DC_HPD4_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont3 &= ~DC_HPD4_INTERRUPT; queue_hotplug = true; DRM_DEBUG("IH: HPD4\n"); } break; case 4: - if (disp_int_cont4 & DC_HPD5_INTERRUPT) { - disp_int_cont4 &= ~DC_HPD5_INTERRUPT; + if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & DC_HPD5_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont4 &= ~DC_HPD5_INTERRUPT; queue_hotplug = true; DRM_DEBUG("IH: HPD5\n"); } break; case 5: - if (disp_int_cont5 & DC_HPD6_INTERRUPT) { - disp_int_cont5 &= ~DC_HPD6_INTERRUPT; + if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & DC_HPD6_INTERRUPT) { + rdev->irq.stat_regs.evergreen.disp_int_cont5 &= ~DC_HPD6_INTERRUPT; queue_hotplug = true; DRM_DEBUG("IH: HPD6\n"); } diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 33952a1..d1d91e9 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -2869,6 +2869,8 @@ static void r600_disable_interrupt_state(struct radeon_device *rdev) WREG32(CP_INT_CNTL, CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE); WREG32(GRBM_INT_CNTL, 0); WREG32(DxMODE_INT_MASK, 0); + WREG32(D1GRPH_INTERRUPT_CONTROL, 0); + WREG32(D2GRPH_INTERRUPT_CONTROL, 0); if (ASIC_IS_DCE3(rdev)) { WREG32(DCE3_DACA_AUTODETECT_INT_CONTROL, 0); WREG32(DCE3_DACB_AUTODETECT_INT_CONTROL, 0); @@ -2993,6 +2995,7 @@ int r600_irq_set(struct radeon_device *rdev) u32 hpd1, hpd2, hpd3, hpd4 = 0, hpd5 = 0, hpd6 = 0; u32 grbm_int_cntl = 0; u32 hdmi1, hdmi2; + u32 d1grph = 0, d2grph = 0;
if (!rdev->irq.installed) { WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n"); @@ -3073,9 +3076,19 @@ int r600_irq_set(struct radeon_device *rdev) DRM_DEBUG("gui idle\n"); grbm_int_cntl |= GUI_IDLE_INT_ENABLE; } + if (rdev->irq.pflip[0]) { + DRM_DEBUG("pflip 0\n"); + d1grph |= DxGRPH_PFLIP_INT_MASK; + } + if (rdev->irq.pflip[1]) { + DRM_DEBUG("pflip 1\n"); + d2grph |= DxGRPH_PFLIP_INT_MASK; + }
WREG32(CP_INT_CNTL, cp_int_cntl); WREG32(DxMODE_INT_MASK, mode_int); + WREG32(D1GRPH_INTERRUPT_CONTROL, d1grph); + WREG32(D2GRPH_INTERRUPT_CONTROL, d2grph); WREG32(GRBM_INT_CNTL, grbm_int_cntl); WREG32(R600_HDMI_BLOCK1 + R600_HDMI_CNTL, hdmi1); if (ASIC_IS_DCE3(rdev)) { @@ -3098,32 +3111,35 @@ int r600_irq_set(struct radeon_device *rdev) return 0; }
-static inline void r600_irq_ack(struct radeon_device *rdev, - u32 *disp_int, - u32 *disp_int_cont, - u32 *disp_int_cont2) +static inline void r600_irq_ack(struct radeon_device *rdev) { u32 tmp;
if (ASIC_IS_DCE3(rdev)) { - *disp_int = RREG32(DCE3_DISP_INTERRUPT_STATUS); - *disp_int_cont = RREG32(DCE3_DISP_INTERRUPT_STATUS_CONTINUE); - *disp_int_cont2 = RREG32(DCE3_DISP_INTERRUPT_STATUS_CONTINUE2); + rdev->irq.stat_regs.r600.disp_int = RREG32(DCE3_DISP_INTERRUPT_STATUS); + rdev->irq.stat_regs.r600.disp_int_cont = RREG32(DCE3_DISP_INTERRUPT_STATUS_CONTINUE); + rdev->irq.stat_regs.r600.disp_int_cont2 = RREG32(DCE3_DISP_INTERRUPT_STATUS_CONTINUE2); } else { - *disp_int = RREG32(DISP_INTERRUPT_STATUS); - *disp_int_cont = RREG32(DISP_INTERRUPT_STATUS_CONTINUE); - *disp_int_cont2 = 0; - } - - if (*disp_int & LB_D1_VBLANK_INTERRUPT) + rdev->irq.stat_regs.r600.disp_int = RREG32(DISP_INTERRUPT_STATUS); + rdev->irq.stat_regs.r600.disp_int_cont = RREG32(DISP_INTERRUPT_STATUS_CONTINUE); + rdev->irq.stat_regs.r600.disp_int_cont2 = 0; + } + rdev->irq.stat_regs.r600.d1grph_int = RREG32(D1GRPH_INTERRUPT_STATUS); + rdev->irq.stat_regs.r600.d2grph_int = RREG32(D2GRPH_INTERRUPT_STATUS); + + if (rdev->irq.stat_regs.r600.d1grph_int & DxGRPH_PFLIP_INT_OCCURRED) + WREG32(D1GRPH_INTERRUPT_STATUS, DxGRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.r600.d2grph_int & DxGRPH_PFLIP_INT_OCCURRED) + WREG32(D2GRPH_INTERRUPT_STATUS, DxGRPH_PFLIP_INT_CLEAR); + if (rdev->irq.stat_regs.r600.disp_int & LB_D1_VBLANK_INTERRUPT) WREG32(D1MODE_VBLANK_STATUS, DxMODE_VBLANK_ACK); - if (*disp_int & LB_D1_VLINE_INTERRUPT) + if (rdev->irq.stat_regs.r600.disp_int & LB_D1_VLINE_INTERRUPT) WREG32(D1MODE_VLINE_STATUS, DxMODE_VLINE_ACK); - if (*disp_int & LB_D2_VBLANK_INTERRUPT) + if (rdev->irq.stat_regs.r600.disp_int & LB_D2_VBLANK_INTERRUPT) WREG32(D2MODE_VBLANK_STATUS, DxMODE_VBLANK_ACK); - if (*disp_int & LB_D2_VLINE_INTERRUPT) + if (rdev->irq.stat_regs.r600.disp_int & LB_D2_VLINE_INTERRUPT) WREG32(D2MODE_VLINE_STATUS, DxMODE_VLINE_ACK); - if (*disp_int & DC_HPD1_INTERRUPT) { + if (rdev->irq.stat_regs.r600.disp_int & DC_HPD1_INTERRUPT) { if (ASIC_IS_DCE3(rdev)) { tmp = RREG32(DC_HPD1_INT_CONTROL); tmp |= DC_HPDx_INT_ACK; @@ -3134,7 +3150,7 @@ static inline void r600_irq_ack(struct radeon_device *rdev, WREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL, tmp); } } - if (*disp_int & DC_HPD2_INTERRUPT) { + if (rdev->irq.stat_regs.r600.disp_int & DC_HPD2_INTERRUPT) { if (ASIC_IS_DCE3(rdev)) { tmp = RREG32(DC_HPD2_INT_CONTROL); tmp |= DC_HPDx_INT_ACK; @@ -3145,7 +3161,7 @@ static inline void r600_irq_ack(struct radeon_device *rdev, WREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL, tmp); } } - if (*disp_int_cont & DC_HPD3_INTERRUPT) { + if (rdev->irq.stat_regs.r600.disp_int_cont & DC_HPD3_INTERRUPT) { if (ASIC_IS_DCE3(rdev)) { tmp = RREG32(DC_HPD3_INT_CONTROL); tmp |= DC_HPDx_INT_ACK; @@ -3156,18 +3172,18 @@ static inline void r600_irq_ack(struct radeon_device *rdev, WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, tmp); } } - if (*disp_int_cont & DC_HPD4_INTERRUPT) { + if (rdev->irq.stat_regs.r600.disp_int_cont & DC_HPD4_INTERRUPT) { tmp = RREG32(DC_HPD4_INT_CONTROL); tmp |= DC_HPDx_INT_ACK; WREG32(DC_HPD4_INT_CONTROL, tmp); } if (ASIC_IS_DCE32(rdev)) { - if (*disp_int_cont2 & DC_HPD5_INTERRUPT) { + if (rdev->irq.stat_regs.r600.disp_int_cont2 & DC_HPD5_INTERRUPT) { tmp = RREG32(DC_HPD5_INT_CONTROL); tmp |= DC_HPDx_INT_ACK; WREG32(DC_HPD5_INT_CONTROL, tmp); } - if (*disp_int_cont2 & DC_HPD6_INTERRUPT) { + if (rdev->irq.stat_regs.r600.disp_int_cont2 & DC_HPD6_INTERRUPT) { tmp = RREG32(DC_HPD5_INT_CONTROL); tmp |= DC_HPDx_INT_ACK; WREG32(DC_HPD6_INT_CONTROL, tmp); @@ -3189,12 +3205,10 @@ static inline void r600_irq_ack(struct radeon_device *rdev,
void r600_irq_disable(struct radeon_device *rdev) { - u32 disp_int, disp_int_cont, disp_int_cont2; - r600_disable_interrupts(rdev); /* Wait and acknowledge irq */ mdelay(1); - r600_irq_ack(rdev, &disp_int, &disp_int_cont, &disp_int_cont2); + r600_irq_ack(rdev); r600_disable_interrupt_state(rdev); }
@@ -3257,7 +3271,7 @@ int r600_irq_process(struct radeon_device *rdev) u32 wptr = r600_get_ih_wptr(rdev); u32 rptr = rdev->ih.rptr; u32 src_id, src_data; - u32 ring_index, disp_int, disp_int_cont, disp_int_cont2; + u32 ring_index; unsigned long flags; bool queue_hotplug = false;
@@ -3278,7 +3292,7 @@ int r600_irq_process(struct radeon_device *rdev)
restart_ih: /* display interrupts */ - r600_irq_ack(rdev, &disp_int, &disp_int_cont, &disp_int_cont2); + r600_irq_ack(rdev);
rdev->ih.wptr = wptr; while (rptr != wptr) { @@ -3291,17 +3305,17 @@ restart_ih: case 1: /* D1 vblank/vline */ switch (src_data) { case 0: /* D1 vblank */ - if (disp_int & LB_D1_VBLANK_INTERRUPT) { + if (rdev->irq.stat_regs.r600.disp_int & LB_D1_VBLANK_INTERRUPT) { drm_handle_vblank(rdev->ddev, 0); rdev->pm.vblank_sync = true; wake_up(&rdev->irq.vblank_queue); - disp_int &= ~LB_D1_VBLANK_INTERRUPT; + rdev->irq.stat_regs.r600.disp_int &= ~LB_D1_VBLANK_INTERRUPT; DRM_DEBUG("IH: D1 vblank\n"); } break; case 1: /* D1 vline */ - if (disp_int & LB_D1_VLINE_INTERRUPT) { - disp_int &= ~LB_D1_VLINE_INTERRUPT; + if (rdev->irq.stat_regs.r600.disp_int & LB_D1_VLINE_INTERRUPT) { + rdev->irq.stat_regs.r600.disp_int &= ~LB_D1_VLINE_INTERRUPT; DRM_DEBUG("IH: D1 vline\n"); } break; @@ -3313,17 +3327,17 @@ restart_ih: case 5: /* D2 vblank/vline */ switch (src_data) { case 0: /* D2 vblank */ - if (disp_int & LB_D2_VBLANK_INTERRUPT) { + if (rdev->irq.stat_regs.r600.disp_int & LB_D2_VBLANK_INTERRUPT) { drm_handle_vblank(rdev->ddev, 1); rdev->pm.vblank_sync = true; wake_up(&rdev->irq.vblank_queue); - disp_int &= ~LB_D2_VBLANK_INTERRUPT; + rdev->irq.stat_regs.r600.disp_int &= ~LB_D2_VBLANK_INTERRUPT; DRM_DEBUG("IH: D2 vblank\n"); } break; case 1: /* D1 vline */ - if (disp_int & LB_D2_VLINE_INTERRUPT) { - disp_int &= ~LB_D2_VLINE_INTERRUPT; + if (rdev->irq.stat_regs.r600.disp_int & LB_D2_VLINE_INTERRUPT) { + rdev->irq.stat_regs.r600.disp_int &= ~LB_D2_VLINE_INTERRUPT; DRM_DEBUG("IH: D2 vline\n"); } break; @@ -3332,46 +3346,58 @@ restart_ih: break; } break; + case 9: /* D1 pflip */ + if (rdev->irq.stat_regs.r600.d1grph_int & DxGRPH_PFLIP_INT_OCCURRED) { + rdev->irq.stat_regs.r600.d1grph_int &= ~DxGRPH_PFLIP_INT_OCCURRED; + DRM_DEBUG("IH: D1 pflip\n"); + } + break; + case 11: /* D2 pflip */ + if (rdev->irq.stat_regs.r600.d2grph_int & DxGRPH_PFLIP_INT_OCCURRED) { + rdev->irq.stat_regs.r600.d2grph_int &= ~DxGRPH_PFLIP_INT_OCCURRED; + DRM_DEBUG("IH: D2 pflip\n"); + } + break; case 19: /* HPD/DAC hotplug */ switch (src_data) { case 0: - if (disp_int & DC_HPD1_INTERRUPT) { - disp_int &= ~DC_HPD1_INTERRUPT; + if (rdev->irq.stat_regs.r600.disp_int & DC_HPD1_INTERRUPT) { + rdev->irq.stat_regs.r600.disp_int &= ~DC_HPD1_INTERRUPT; queue_hotplug = true; DRM_DEBUG("IH: HPD1\n"); } break; case 1: - if (disp_int & DC_HPD2_INTERRUPT) { - disp_int &= ~DC_HPD2_INTERRUPT; + if (rdev->irq.stat_regs.r600.disp_int & DC_HPD2_INTERRUPT) { + rdev->irq.stat_regs.r600.disp_int &= ~DC_HPD2_INTERRUPT; queue_hotplug = true; DRM_DEBUG("IH: HPD2\n"); } break; case 4: - if (disp_int_cont & DC_HPD3_INTERRUPT) { - disp_int_cont &= ~DC_HPD3_INTERRUPT; + if (rdev->irq.stat_regs.r600.disp_int_cont & DC_HPD3_INTERRUPT) { + rdev->irq.stat_regs.r600.disp_int_cont &= ~DC_HPD3_INTERRUPT; queue_hotplug = true; DRM_DEBUG("IH: HPD3\n"); } break; case 5: - if (disp_int_cont & DC_HPD4_INTERRUPT) { - disp_int_cont &= ~DC_HPD4_INTERRUPT; + if (rdev->irq.stat_regs.r600.disp_int_cont & DC_HPD4_INTERRUPT) { + rdev->irq.stat_regs.r600.disp_int_cont &= ~DC_HPD4_INTERRUPT; queue_hotplug = true; DRM_DEBUG("IH: HPD4\n"); } break; case 10: - if (disp_int_cont2 & DC_HPD5_INTERRUPT) { - disp_int_cont2 &= ~DC_HPD5_INTERRUPT; + if (rdev->irq.stat_regs.r600.disp_int_cont2 & DC_HPD5_INTERRUPT) { + rdev->irq.stat_regs.r600.disp_int_cont2 &= ~DC_HPD5_INTERRUPT; queue_hotplug = true; DRM_DEBUG("IH: HPD5\n"); } break; case 12: - if (disp_int_cont2 & DC_HPD6_INTERRUPT) { - disp_int_cont2 &= ~DC_HPD6_INTERRUPT; + if (rdev->irq.stat_regs.r600.disp_int_cont2 & DC_HPD6_INTERRUPT) { + rdev->irq.stat_regs.r600.disp_int_cont2 &= ~DC_HPD6_INTERRUPT; queue_hotplug = true; DRM_DEBUG("IH: HPD6\n"); } diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h index 966a793..45995b4 100644 --- a/drivers/gpu/drm/radeon/r600d.h +++ b/drivers/gpu/drm/radeon/r600d.h @@ -722,6 +722,15 @@ /* DCE 3.2 */ # define DC_HPDx_EN (1 << 28)
+#define D1GRPH_INTERRUPT_STATUS 0x6158 +#define D2GRPH_INTERRUPT_STATUS 0x6958 +# define DxGRPH_PFLIP_INT_OCCURRED (1 << 0) +# define DxGRPH_PFLIP_INT_CLEAR (1 << 8) +#define D1GRPH_INTERRUPT_CONTROL 0x615c +#define D2GRPH_INTERRUPT_CONTROL 0x695c +# define DxGRPH_PFLIP_INT_MASK (1 << 0) +# define DxGRPH_PFLIP_INT_TYPE (1 << 8) + /* * PM4 */ diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 73f600d..94474e6 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -377,11 +377,45 @@ void radeon_scratch_free(struct radeon_device *rdev, uint32_t reg); /* * IRQS. */ +struct r500_irq_stat_regs { + u32 disp_int; +}; + +struct r600_irq_stat_regs { + u32 disp_int; + u32 disp_int_cont; + u32 disp_int_cont2; + u32 d1grph_int; + u32 d2grph_int; +}; + +struct evergreen_irq_stat_regs { + u32 disp_int; + u32 disp_int_cont; + u32 disp_int_cont2; + u32 disp_int_cont3; + u32 disp_int_cont4; + u32 disp_int_cont5; + u32 d1grph_int; + u32 d2grph_int; + u32 d3grph_int; + u32 d4grph_int; + u32 d5grph_int; + u32 d6grph_int; +}; + +union radeon_irq_stat_regs { + struct r500_irq_stat_regs r500; + struct r600_irq_stat_regs r600; + struct evergreen_irq_stat_regs evergreen; +}; + struct radeon_irq { bool installed; bool sw_int; /* FIXME: use a define max crtc rather than hardcode it */ bool crtc_vblank_int[6]; + bool pflip[6]; wait_queue_head_t vblank_queue; /* FIXME: use defines for max hpd/dacs */ bool hpd[6]; @@ -392,6 +426,7 @@ struct radeon_irq { bool hdmi[2]; spinlock_t sw_lock; int sw_refcount; + union radeon_irq_stat_regs stat_regs; };
int radeon_irq_kms_init(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index a108c7e..d2c9647 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -71,8 +71,10 @@ void radeon_driver_irq_preinstall_kms(struct drm_device *dev) 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++) + for (i = 0; i < 6; i++) { rdev->irq.hpd[i] = false; + rdev->irq.pflip[i] = false; + } radeon_irq_set(rdev); /* Clear bits */ radeon_irq_process(rdev); @@ -101,8 +103,10 @@ void radeon_driver_irq_uninstall_kms(struct drm_device *dev) 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++) + for (i = 0; i < 6; i++) { rdev->irq.hpd[i] = false; + rdev->irq.pflip[i] = false; + } radeon_irq_set(rdev); }
diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index b091a1f..834a3a9 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c @@ -534,7 +534,7 @@ int rs600_irq_set(struct radeon_device *rdev) return 0; }
-static inline uint32_t rs600_irq_ack(struct radeon_device *rdev, u32 *r500_disp_int) +static inline u32 rs600_irq_ack(struct radeon_device *rdev) { uint32_t irqs = RREG32(R_000044_GEN_INT_STATUS); uint32_t irq_mask = S_000044_SW_INT(1); @@ -547,27 +547,27 @@ static inline uint32_t rs600_irq_ack(struct radeon_device *rdev, u32 *r500_disp_ }
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)) { + rdev->irq.stat_regs.r500.disp_int = RREG32(R_007EDC_DISP_INTERRUPT_STATUS); + if (G_007EDC_LB_D1_VBLANK_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) { WREG32(R_006534_D1MODE_VBLANK_STATUS, S_006534_D1MODE_VBLANK_ACK(1)); } - if (G_007EDC_LB_D2_VBLANK_INTERRUPT(*r500_disp_int)) { + if (G_007EDC_LB_D2_VBLANK_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) { WREG32(R_006D34_D2MODE_VBLANK_STATUS, S_006D34_D2MODE_VBLANK_ACK(1)); } - if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(*r500_disp_int)) { + if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) { tmp = RREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL); tmp |= S_007D08_DC_HOT_PLUG_DETECT1_INT_ACK(1); WREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL, tmp); } - if (G_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(*r500_disp_int)) { + if (G_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) { tmp = RREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL); tmp |= S_007D18_DC_HOT_PLUG_DETECT2_INT_ACK(1); WREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL, tmp); } } else { - *r500_disp_int = 0; + rdev->irq.stat_regs.r500.disp_int = 0; }
if (irqs) { @@ -578,29 +578,26 @@ static inline uint32_t rs600_irq_ack(struct radeon_device *rdev, u32 *r500_disp_
void rs600_irq_disable(struct radeon_device *rdev) { - u32 tmp; - WREG32(R_000040_GEN_INT_CNTL, 0); WREG32(R_006540_DxMODE_INT_MASK, 0); /* Wait and acknowledge irq */ mdelay(1); - rs600_irq_ack(rdev, &tmp); + rs600_irq_ack(rdev); }
int rs600_irq_process(struct radeon_device *rdev) { - uint32_t status, msi_rearm; - uint32_t r500_disp_int; + u32 status, msi_rearm; 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) { + status = rs600_irq_ack(rdev); + if (!status && !rdev->irq.stat_regs.r500.disp_int) { return IRQ_NONE; } - while (status || r500_disp_int) { + while (status || rdev->irq.stat_regs.r500.disp_int) { /* SW interrupt */ if (G_000044_SW_INT(status)) radeon_fence_process(rdev); @@ -611,25 +608,25 @@ int rs600_irq_process(struct radeon_device *rdev) wake_up(&rdev->irq.idle_queue); } /* Vertical blank interrupts */ - if (G_007EDC_LB_D1_VBLANK_INTERRUPT(r500_disp_int)) { + if (G_007EDC_LB_D1_VBLANK_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) { drm_handle_vblank(rdev->ddev, 0); rdev->pm.vblank_sync = true; wake_up(&rdev->irq.vblank_queue); } - if (G_007EDC_LB_D2_VBLANK_INTERRUPT(r500_disp_int)) { + if (G_007EDC_LB_D2_VBLANK_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) { drm_handle_vblank(rdev->ddev, 1); rdev->pm.vblank_sync = true; wake_up(&rdev->irq.vblank_queue); } - if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(r500_disp_int)) { + if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) { queue_hotplug = true; DRM_DEBUG("HPD1\n"); } - if (G_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(r500_disp_int)) { + if (G_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) { queue_hotplug = true; DRM_DEBUG("HPD2\n"); } - status = rs600_irq_ack(rdev, &r500_disp_int); + status = rs600_irq_ack(rdev); } /* reset gui idle ack. the status bit is broken */ rdev->irq.gui_idle_acked = false;
This adds support for dri2 pageflipping.
Signed-off-by: Alex Deucher alexdeucher@gmail.com Signed-off-by: Jerome Glisse jglisse@redhat.com --- drivers/gpu/drm/radeon/evergreen.c | 36 +++++++++++ drivers/gpu/drm/radeon/evergreen_reg.h | 3 + drivers/gpu/drm/radeon/evergreend.h | 14 ++++ drivers/gpu/drm/radeon/r100.c | 104 ++++++++++++++++++++++++++++--- drivers/gpu/drm/radeon/r500_reg.h | 1 + drivers/gpu/drm/radeon/r600.c | 23 +++++++ drivers/gpu/drm/radeon/r600d.h | 20 ++++++ drivers/gpu/drm/radeon/radeon.h | 12 ++++ drivers/gpu/drm/radeon/radeon_asic.c | 42 ++++++++++++ drivers/gpu/drm/radeon/radeon_asic.h | 7 ++ drivers/gpu/drm/radeon/radeon_display.c | 101 ++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/radeon_drv.c | 2 +- drivers/gpu/drm/radeon/radeon_irq_kms.c | 62 ++++++++++++++++++ drivers/gpu/drm/radeon/radeon_mode.h | 7 ++ drivers/gpu/drm/radeon/rs600.c | 49 ++++++++++++--- drivers/gpu/drm/radeon/rs600d.h | 13 ++++ drivers/gpu/drm/radeon/rv770.c | 34 ++++++++++ drivers/gpu/drm/radeon/rv770d.h | 39 ++++++++++++ 18 files changed, 552 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 242055a..19d9a0c 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -40,6 +40,27 @@ static void evergreen_gpu_init(struct radeon_device *rdev); void evergreen_fini(struct radeon_device *rdev);
+void evergreen_page_flip(struct drm_crtc *crtc, u64 crtc_base) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); +#if 0 + radeon_ring_write(rdev, + PACKET0(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset, 0)); + radeon_ring_write(rdev, upper_32_bits(crtc_base)); + radeon_ring_write(rdev, + PACKET0(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, 0)); + radeon_ring_write(rdev, (u32)crtc_base); +#endif + radeon_ring_write(rdev, + PACKET0(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset, 0)); + radeon_ring_write(rdev, upper_32_bits(crtc_base)); + radeon_ring_write(rdev, + PACKET0(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, 0)); + radeon_ring_write(rdev, (u32)crtc_base); +} + /* get temperature in millidegrees */ u32 evergreen_get_temp(struct radeon_device *rdev) { @@ -2300,6 +2321,7 @@ int evergreen_irq_process(struct radeon_device *rdev) u32 ring_index; unsigned long flags; bool queue_hotplug = false; + bool queue_unpin = false;
DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr); if (!rdev->ih.enabled) @@ -2456,36 +2478,48 @@ restart_ih: break; case 8: /* D1 pflip */ if (rdev->irq.stat_regs.evergreen.d1grph_int & GRPH_PFLIP_INT_OCCURRED) { + radeon_crtc_handle_flip(rdev, 0); + queue_unpin = true; rdev->irq.stat_regs.evergreen.d1grph_int &= ~GRPH_PFLIP_INT_OCCURRED; DRM_DEBUG("IH: D1 pflip\n"); } break; case 10: /* D2 pflip */ if (rdev->irq.stat_regs.evergreen.d2grph_int & GRPH_PFLIP_INT_OCCURRED) { + radeon_crtc_handle_flip(rdev, 1); + queue_unpin = true; rdev->irq.stat_regs.evergreen.d2grph_int &= ~GRPH_PFLIP_INT_OCCURRED; DRM_DEBUG("IH: D2 pflip\n"); } break; case 12: /* D3 pflip */ if (rdev->irq.stat_regs.evergreen.d3grph_int & GRPH_PFLIP_INT_OCCURRED) { + radeon_crtc_handle_flip(rdev, 2); + queue_unpin = true; rdev->irq.stat_regs.evergreen.d3grph_int &= ~GRPH_PFLIP_INT_OCCURRED; DRM_DEBUG("IH: D3 pflip\n"); } break; case 14: /* D4 pflip */ if (rdev->irq.stat_regs.evergreen.d4grph_int & GRPH_PFLIP_INT_OCCURRED) { + radeon_crtc_handle_flip(rdev, 3); + queue_unpin = true; rdev->irq.stat_regs.evergreen.d4grph_int &= ~GRPH_PFLIP_INT_OCCURRED; DRM_DEBUG("IH: D4 pflip\n"); } break; case 16: /* D5 pflip */ if (rdev->irq.stat_regs.evergreen.d5grph_int & GRPH_PFLIP_INT_OCCURRED) { + radeon_crtc_handle_flip(rdev, 4); + queue_unpin = true; rdev->irq.stat_regs.evergreen.d5grph_int &= ~GRPH_PFLIP_INT_OCCURRED; DRM_DEBUG("IH: D5 pflip\n"); } break; case 18: /* D6 pflip */ if (rdev->irq.stat_regs.evergreen.d6grph_int & GRPH_PFLIP_INT_OCCURRED) { + radeon_crtc_handle_flip(rdev, 5); + queue_unpin = true; rdev->irq.stat_regs.evergreen.d6grph_int &= ~GRPH_PFLIP_INT_OCCURRED; DRM_DEBUG("IH: D6 pflip\n"); } @@ -2569,6 +2603,8 @@ restart_ih: goto restart_ih; if (queue_hotplug) queue_work(rdev->wq, &rdev->hotplug_work); + if (queue_unpin) + queue_work(rdev->wq, &rdev->unpin_work); rdev->ih.rptr = rptr; WREG32(IH_RB_RPTR, rdev->ih.rptr); spin_unlock_irqrestore(&rdev->ih.lock, flags); diff --git a/drivers/gpu/drm/radeon/evergreen_reg.h b/drivers/gpu/drm/radeon/evergreen_reg.h index 2330f3a..e8c625b 100644 --- a/drivers/gpu/drm/radeon/evergreen_reg.h +++ b/drivers/gpu/drm/radeon/evergreen_reg.h @@ -105,6 +105,9 @@ #define EVERGREEN_GRPH_Y_START 0x6830 #define EVERGREEN_GRPH_X_END 0x6834 #define EVERGREEN_GRPH_Y_END 0x6838 +#define EVERGREEN_GRPH_UPDATE 0x6844 +# define EVERGREEN_GRPH_SURFACE_UPDATE_PENDING (1 << 2) +# define EVERGREEN_GRPH_UPDATE_LOCK (1 << 16)
/* CUR blocks at 0x6998, 0x7598, 0x10198, 0x10d98, 0x11998, 0x12598 */ #define EVERGREEN_CUR_CONTROL 0x6998 diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h index 113c70c..5ffbc32 100644 --- a/drivers/gpu/drm/radeon/evergreend.h +++ b/drivers/gpu/drm/radeon/evergreend.h @@ -628,6 +628,20 @@ #define PACKET3_MEM_SEMAPHORE 0x39 #define PACKET3_MPEG_INDEX 0x3A #define PACKET3_WAIT_REG_MEM 0x3C +# define PACKET3_WAIT_REG_MEM_ENGINE(x) ((x) << 8) +# define ENGINE_ME 0 +# define ENGINE_PFP 1 +# define PACKET3_WAIT_REG_MEM_MEM_SPACE(x) ((x) << 4) +# define MEM_SPACE_REG 0 +# define MEM_SPACE_MEM 1 +# define PACKET3_WAIT_REG_MEM_FUNCTION(x) ((x) << 0) +# define FUNCTION_ALWAYS 0 +# define FUNCTION_LT 1 +# define FUNCTION_LTE 2 +# define FUNCTION_E 3 +# define FUNCTION_NE 4 +# define FUNCTION_GTE 5 +# define FUNCTION_GT 6 #define PACKET3_MEM_WRITE 0x3D #define PACKET3_INDIRECT_BUFFER 0x32 #define PACKET3_SURFACE_SYNC 0x43 diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index 6d1540c..cde67d1 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -68,6 +68,77 @@ MODULE_FIRMWARE(FIRMWARE_R520); * r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280 */
+void r100_pre_page_flip(struct radeon_device *rdev, int crtc) +{ + /* enable the pflip int */ + radeon_irq_kms_pflip_irq_get(rdev, crtc); +} + +void r100_post_page_flip(struct radeon_device *rdev, int crtc) +{ + /* disable the pflip int */ + radeon_irq_kms_pflip_irq_put(rdev, crtc); +} + +void r100_page_flip(struct drm_crtc *crtc, u64 crtc_base) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(crtc->fb); + struct drm_framebuffer *target_fb = crtc->fb; + struct drm_gem_object *obj = radeon_fb->obj; + struct radeon_bo *rbo = obj->driver_private; + u32 tiling_flags, pitch_pixels; + int r; + + /* crtc offset is from display base addr not FB location */ + crtc_base -= radeon_crtc->legacy_display_base_addr; + + r = radeon_bo_reserve(rbo, false); + if (likely(r == 0)) { + radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL); + radeon_bo_unreserve(rbo); + } + + pitch_pixels = target_fb->pitch / (target_fb->bits_per_pixel / 8); + + if (tiling_flags & RADEON_TILING_MACRO) { + if (ASIC_IS_R300(rdev)) { + crtc_base &= ~0x7ff; + } else { + int byteshift = target_fb->bits_per_pixel >> 4; + int tile_addr = (((crtc->y >> 3) * pitch_pixels + crtc->x) >> (8 - byteshift)) << 11; + crtc_base += tile_addr + ((crtc->x << byteshift) % 256) + ((crtc->y % 8) << 8); + } + } else { + int offset = crtc->y * pitch_pixels + crtc->x; + switch (target_fb->bits_per_pixel) { + case 8: + default: + offset *= 1; + break; + case 15: + case 16: + offset *= 2; + break; + case 24: + offset *= 3; + break; + case 32: + offset *= 4; + break; + } + crtc_base += offset; + } + crtc_base &= ~7; + + radeon_ring_write(rdev, + PACKET0(RADEON_CRTC_OFFSET + radeon_crtc->crtc_offset, 0)); + radeon_ring_write(rdev, (u32)crtc_base); + +} + void r100_pm_get_dynpm_state(struct radeon_device *rdev) { int i; @@ -526,10 +597,12 @@ int r100_irq_set(struct radeon_device *rdev) if (rdev->irq.gui_idle) { tmp |= RADEON_GUI_IDLE_MASK; } - if (rdev->irq.crtc_vblank_int[0]) { + if (rdev->irq.crtc_vblank_int[0] || + rdev->irq.pflip[0]) { tmp |= RADEON_CRTC_VBLANK_MASK; } - if (rdev->irq.crtc_vblank_int[1]) { + if (rdev->irq.crtc_vblank_int[1] || + rdev->irq.pflip[1]) { tmp |= RADEON_CRTC2_VBLANK_MASK; } if (rdev->irq.hpd[0]) { @@ -576,6 +649,7 @@ int r100_irq_process(struct radeon_device *rdev) { uint32_t status, msi_rearm; bool queue_hotplug = false; + bool queue_unpin = false;
/* reset gui idle ack. the status bit is broken */ rdev->irq.gui_idle_acked = false; @@ -600,14 +674,26 @@ int r100_irq_process(struct radeon_device *rdev) } /* Vertical blank interrupts */ if (status & RADEON_CRTC_VBLANK_STAT) { - drm_handle_vblank(rdev->ddev, 0); - rdev->pm.vblank_sync = true; - wake_up(&rdev->irq.vblank_queue); + if (rdev->irq.crtc_vblank_int[0]) { + drm_handle_vblank(rdev->ddev, 0); + rdev->pm.vblank_sync = true; + wake_up(&rdev->irq.vblank_queue); + } + if (rdev->irq.pflip[0]) { + radeon_crtc_handle_flip(rdev, 0); + queue_unpin = true; + } } if (status & RADEON_CRTC2_VBLANK_STAT) { - drm_handle_vblank(rdev->ddev, 1); - rdev->pm.vblank_sync = true; - wake_up(&rdev->irq.vblank_queue); + if (rdev->irq.crtc_vblank_int[1]) { + drm_handle_vblank(rdev->ddev, 1); + rdev->pm.vblank_sync = true; + wake_up(&rdev->irq.vblank_queue); + } + if (rdev->irq.pflip[1]) { + radeon_crtc_handle_flip(rdev, 1); + queue_unpin = true; + } } if (status & RADEON_FP_DETECT_STAT) { queue_hotplug = true; @@ -623,6 +709,8 @@ int r100_irq_process(struct radeon_device *rdev) rdev->irq.gui_idle_acked = false; if (queue_hotplug) queue_work(rdev->wq, &rdev->hotplug_work); + if (queue_unpin) + queue_work(rdev->wq, &rdev->unpin_work); if (rdev->msi_enabled) { switch (rdev->family) { case CHIP_RS400: diff --git a/drivers/gpu/drm/radeon/r500_reg.h b/drivers/gpu/drm/radeon/r500_reg.h index 6ac1f60..bc1eea1 100644 --- a/drivers/gpu/drm/radeon/r500_reg.h +++ b/drivers/gpu/drm/radeon/r500_reg.h @@ -409,6 +409,7 @@ #define AVIVO_D1GRPH_X_END 0x6134 #define AVIVO_D1GRPH_Y_END 0x6138 #define AVIVO_D1GRPH_UPDATE 0x6144 +# define AVIVO_D1GRPH_SURFACE_UPDATE_PENDING (1 << 2) # define AVIVO_D1GRPH_UPDATE_LOCK (1 << 16) #define AVIVO_D1GRPH_FLIP_CONTROL 0x6148
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index d1d91e9..8a2fe86 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -92,6 +92,22 @@ void r600_gpu_init(struct radeon_device *rdev); void r600_fini(struct radeon_device *rdev); void r600_irq_disable(struct radeon_device *rdev);
+void r600_page_flip(struct drm_crtc *crtc, u64 crtc_base) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); +#if 0 + radeon_ring_write(rdev, + PACKET0(D1GRPH_SECONDARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, 0)); + radeon_ring_write(rdev, (u32)crtc_base); +#endif + radeon_ring_write(rdev, + PACKET0(D1GRPH_PRIMARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, 0)); + radeon_ring_write(rdev, (u32)crtc_base); + +} + /* get temperature in millidegrees */ u32 rv6xx_get_temp(struct radeon_device *rdev) { @@ -3274,6 +3290,7 @@ int r600_irq_process(struct radeon_device *rdev) u32 ring_index; unsigned long flags; bool queue_hotplug = false; + bool queue_unpin = false;
DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr); if (!rdev->ih.enabled) @@ -3348,12 +3365,16 @@ restart_ih: break; case 9: /* D1 pflip */ if (rdev->irq.stat_regs.r600.d1grph_int & DxGRPH_PFLIP_INT_OCCURRED) { + radeon_crtc_handle_flip(rdev, 0); + queue_unpin = true; rdev->irq.stat_regs.r600.d1grph_int &= ~DxGRPH_PFLIP_INT_OCCURRED; DRM_DEBUG("IH: D1 pflip\n"); } break; case 11: /* D2 pflip */ if (rdev->irq.stat_regs.r600.d2grph_int & DxGRPH_PFLIP_INT_OCCURRED) { + radeon_crtc_handle_flip(rdev, 1); + queue_unpin = true; rdev->irq.stat_regs.r600.d2grph_int &= ~DxGRPH_PFLIP_INT_OCCURRED; DRM_DEBUG("IH: D2 pflip\n"); } @@ -3441,6 +3462,8 @@ restart_ih: goto restart_ih; if (queue_hotplug) queue_work(rdev->wq, &rdev->hotplug_work); + if (queue_unpin) + queue_work(rdev->wq, &rdev->unpin_work); rdev->ih.rptr = rptr; WREG32(IH_RB_RPTR, rdev->ih.rptr); spin_unlock_irqrestore(&rdev->ih.lock, flags); diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h index 45995b4..0e5b428 100644 --- a/drivers/gpu/drm/radeon/r600d.h +++ b/drivers/gpu/drm/radeon/r600d.h @@ -731,6 +731,12 @@ # define DxGRPH_PFLIP_INT_MASK (1 << 0) # define DxGRPH_PFLIP_INT_TYPE (1 << 8)
+#define D1GRPH_PRIMARY_SURFACE_ADDRESS 0x6110 +#define D1GRPH_SECONDARY_SURFACE_ADDRESS 0x6118 +#define D1GRPH_UPDATE 0x6144 +# define D1GRPH_SURFACE_UPDATE_PENDING (1 << 2) +# define D1GRPH_UPDATE_LOCK (1 << 16) + /* * PM4 */ @@ -771,6 +777,20 @@ #define PACKET3_MEM_SEMAPHORE 0x39 #define PACKET3_MPEG_INDEX 0x3A #define PACKET3_WAIT_REG_MEM 0x3C +# define PACKET3_WAIT_REG_MEM_ENGINE(x) ((x) << 8) +# define ENGINE_ME 0 +# define ENGINE_PFP 1 +# define PACKET3_WAIT_REG_MEM_MEM_SPACE(x) ((x) << 4) +# define MEM_SPACE_REG 0 +# define MEM_SPACE_MEM 1 +# define PACKET3_WAIT_REG_MEM_FUNCTION(x) ((x) << 0) +# define FUNCTION_ALWAYS 0 +# define FUNCTION_LT 1 +# define FUNCTION_LTE 2 +# define FUNCTION_E 3 +# define FUNCTION_NE 4 +# define FUNCTION_GTE 5 +# define FUNCTION_GT 6 #define PACKET3_MEM_WRITE 0x3D #define PACKET3_INDIRECT_BUFFER 0x32 #define PACKET3_SURFACE_SYNC 0x43 diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 94474e6..be65735 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -427,12 +427,16 @@ struct radeon_irq { spinlock_t sw_lock; int sw_refcount; union radeon_irq_stat_regs stat_regs; + spinlock_t pflip_lock[6]; + int pflip_refcount[6]; };
int radeon_irq_kms_init(struct radeon_device *rdev); void radeon_irq_kms_fini(struct radeon_device *rdev); void radeon_irq_kms_sw_irq_get(struct radeon_device *rdev); void radeon_irq_kms_sw_irq_put(struct radeon_device *rdev); +void radeon_irq_kms_pflip_irq_get(struct radeon_device *rdev, int crtc); +void radeon_irq_kms_pflip_irq_put(struct radeon_device *rdev, int crtc);
/* * CP & ring. @@ -916,6 +920,10 @@ struct radeon_asic { void (*pm_finish)(struct radeon_device *rdev); void (*pm_init_profile)(struct radeon_device *rdev); void (*pm_get_dynpm_state)(struct radeon_device *rdev); + /* pageflipping */ + void (*pre_page_flip)(struct radeon_device *rdev, int crtc); + void (*page_flip)(struct drm_crtc *crtc, u64 crtc_base); + void (*post_page_flip)(struct radeon_device *rdev, int crtc); };
/* @@ -1132,6 +1140,7 @@ struct radeon_device { struct r600_ih ih; /* r6/700 interrupt ring */ struct workqueue_struct *wq; struct work_struct hotplug_work; + struct work_struct unpin_work; int num_crtc; /* number of crtcs */ struct mutex dc_hw_i2c_mutex; /* display controller hw i2c mutex */ struct mutex vram_mutex; @@ -1375,6 +1384,9 @@ static inline void radeon_ring_write(struct radeon_device *rdev, uint32_t v) #define radeon_pm_finish(rdev) (rdev)->asic->pm_finish((rdev)) #define radeon_pm_init_profile(rdev) (rdev)->asic->pm_init_profile((rdev)) #define radeon_pm_get_dynpm_state(rdev) (rdev)->asic->pm_get_dynpm_state((rdev)) +#define radeon_pre_page_flip(rdev, crtc) rdev->asic->pre_page_flip((rdev), (crtc)) +#define radeon_page_flip(crtc, base) rdev->asic->page_flip((crtc), (base)) +#define radeon_post_page_flip(rdev, crtc) rdev->asic->post_page_flip((rdev), (crtc))
/* Common functions */ /* AGP */ diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 64fb89e..6621d9e 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -171,6 +171,9 @@ static struct radeon_asic r100_asic = { .pm_finish = &r100_pm_finish, .pm_init_profile = &r100_pm_init_profile, .pm_get_dynpm_state = &r100_pm_get_dynpm_state, + .pre_page_flip = &r100_pre_page_flip, + .page_flip = &r100_page_flip, + .post_page_flip = &r100_post_page_flip, };
static struct radeon_asic r200_asic = { @@ -215,6 +218,9 @@ static struct radeon_asic r200_asic = { .pm_finish = &r100_pm_finish, .pm_init_profile = &r100_pm_init_profile, .pm_get_dynpm_state = &r100_pm_get_dynpm_state, + .pre_page_flip = &r100_pre_page_flip, + .page_flip = &r100_page_flip, + .post_page_flip = &r100_post_page_flip, };
static struct radeon_asic r300_asic = { @@ -260,6 +266,9 @@ static struct radeon_asic r300_asic = { .pm_finish = &r100_pm_finish, .pm_init_profile = &r100_pm_init_profile, .pm_get_dynpm_state = &r100_pm_get_dynpm_state, + .pre_page_flip = &r100_pre_page_flip, + .page_flip = &r100_page_flip, + .post_page_flip = &r100_post_page_flip, };
static struct radeon_asic r300_asic_pcie = { @@ -304,6 +313,9 @@ static struct radeon_asic r300_asic_pcie = { .pm_finish = &r100_pm_finish, .pm_init_profile = &r100_pm_init_profile, .pm_get_dynpm_state = &r100_pm_get_dynpm_state, + .pre_page_flip = &r100_pre_page_flip, + .page_flip = &r100_page_flip, + .post_page_flip = &r100_post_page_flip, };
static struct radeon_asic r420_asic = { @@ -349,6 +361,9 @@ static struct radeon_asic r420_asic = { .pm_finish = &r100_pm_finish, .pm_init_profile = &r420_pm_init_profile, .pm_get_dynpm_state = &r100_pm_get_dynpm_state, + .pre_page_flip = &r100_pre_page_flip, + .page_flip = &r100_page_flip, + .post_page_flip = &r100_post_page_flip, };
static struct radeon_asic rs400_asic = { @@ -394,6 +409,9 @@ static struct radeon_asic rs400_asic = { .pm_finish = &r100_pm_finish, .pm_init_profile = &r100_pm_init_profile, .pm_get_dynpm_state = &r100_pm_get_dynpm_state, + .pre_page_flip = &r100_pre_page_flip, + .page_flip = &r100_page_flip, + .post_page_flip = &r100_post_page_flip, };
static struct radeon_asic rs600_asic = { @@ -439,6 +457,9 @@ static struct radeon_asic rs600_asic = { .pm_finish = &rs600_pm_finish, .pm_init_profile = &r420_pm_init_profile, .pm_get_dynpm_state = &r100_pm_get_dynpm_state, + .pre_page_flip = &r100_pre_page_flip, + .page_flip = &rs600_page_flip, + .post_page_flip = &r100_post_page_flip, };
static struct radeon_asic rs690_asic = { @@ -484,6 +505,9 @@ static struct radeon_asic rs690_asic = { .pm_finish = &rs600_pm_finish, .pm_init_profile = &r420_pm_init_profile, .pm_get_dynpm_state = &r100_pm_get_dynpm_state, + .pre_page_flip = &r100_pre_page_flip, + .page_flip = &rs600_page_flip, + .post_page_flip = &r100_post_page_flip, };
static struct radeon_asic rv515_asic = { @@ -529,6 +553,9 @@ static struct radeon_asic rv515_asic = { .pm_finish = &rs600_pm_finish, .pm_init_profile = &r420_pm_init_profile, .pm_get_dynpm_state = &r100_pm_get_dynpm_state, + .pre_page_flip = &r100_pre_page_flip, + .page_flip = &rs600_page_flip, + .post_page_flip = &r100_post_page_flip, };
static struct radeon_asic r520_asic = { @@ -574,6 +601,9 @@ static struct radeon_asic r520_asic = { .pm_finish = &rs600_pm_finish, .pm_init_profile = &r420_pm_init_profile, .pm_get_dynpm_state = &r100_pm_get_dynpm_state, + .pre_page_flip = &r100_pre_page_flip, + .page_flip = &rs600_page_flip, + .post_page_flip = &r100_post_page_flip, };
static struct radeon_asic r600_asic = { @@ -618,6 +648,9 @@ static struct radeon_asic r600_asic = { .pm_finish = &rs600_pm_finish, .pm_init_profile = &r600_pm_init_profile, .pm_get_dynpm_state = &r600_pm_get_dynpm_state, + .pre_page_flip = &r100_pre_page_flip, + .page_flip = &r600_page_flip, + .post_page_flip = &r100_post_page_flip, };
static struct radeon_asic rs780_asic = { @@ -662,6 +695,9 @@ static struct radeon_asic rs780_asic = { .pm_finish = &rs600_pm_finish, .pm_init_profile = &rs780_pm_init_profile, .pm_get_dynpm_state = &r600_pm_get_dynpm_state, + .pre_page_flip = &r100_pre_page_flip, + .page_flip = &r600_page_flip, + .post_page_flip = &r100_post_page_flip, };
static struct radeon_asic rv770_asic = { @@ -706,6 +742,9 @@ static struct radeon_asic rv770_asic = { .pm_finish = &rs600_pm_finish, .pm_init_profile = &r600_pm_init_profile, .pm_get_dynpm_state = &r600_pm_get_dynpm_state, + .pre_page_flip = &r100_pre_page_flip, + .page_flip = &rv770_page_flip, + .post_page_flip = &r100_post_page_flip, };
static struct radeon_asic evergreen_asic = { @@ -749,6 +788,9 @@ static struct radeon_asic evergreen_asic = { .pm_finish = &evergreen_pm_finish, .pm_init_profile = &r600_pm_init_profile, .pm_get_dynpm_state = &r600_pm_get_dynpm_state, + .pre_page_flip = &r100_pre_page_flip, + .page_flip = &evergreen_page_flip, + .post_page_flip = &r100_post_page_flip, };
int radeon_asic_init(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 7409882..c531850 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -130,6 +130,9 @@ extern void r100_pm_prepare(struct radeon_device *rdev); extern void r100_pm_finish(struct radeon_device *rdev); extern void r100_pm_init_profile(struct radeon_device *rdev); extern void r100_pm_get_dynpm_state(struct radeon_device *rdev); +extern void r100_pre_page_flip(struct radeon_device *rdev, int crtc); +extern void r100_page_flip(struct drm_crtc *crtc, u64 crtc_base); +extern void r100_post_page_flip(struct radeon_device *rdev, int crtc);
/* * r200,rv250,rs300,rv280 @@ -205,6 +208,7 @@ void rs600_hpd_set_polarity(struct radeon_device *rdev, extern void rs600_pm_misc(struct radeon_device *rdev); extern void rs600_pm_prepare(struct radeon_device *rdev); extern void rs600_pm_finish(struct radeon_device *rdev); +extern void rs600_page_flip(struct drm_crtc *crtc, u64 crtc_base);
/* * rs690,rs740 @@ -278,6 +282,7 @@ extern void r600_pm_misc(struct radeon_device *rdev); extern void r600_pm_init_profile(struct radeon_device *rdev); extern void rs780_pm_init_profile(struct radeon_device *rdev); extern void r600_pm_get_dynpm_state(struct radeon_device *rdev); +extern void r600_page_flip(struct drm_crtc *crtc, u64 crtc_base);
/* * rv770,rv730,rv710,rv740 @@ -287,6 +292,7 @@ void rv770_fini(struct radeon_device *rdev); int rv770_suspend(struct radeon_device *rdev); int rv770_resume(struct radeon_device *rdev); extern void rv770_pm_misc(struct radeon_device *rdev); +extern void rv770_page_flip(struct drm_crtc *crtc, u64 crtc_base);
/* * evergreen @@ -314,5 +320,6 @@ extern int evergreen_cs_parse(struct radeon_cs_parser *p); extern void evergreen_pm_misc(struct radeon_device *rdev); extern void evergreen_pm_prepare(struct radeon_device *rdev); extern void evergreen_pm_finish(struct radeon_device *rdev); +extern void evergreen_page_flip(struct drm_crtc *crtc, u64 crtc_base);
#endif diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 0383631..345828a 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -183,12 +183,113 @@ static void radeon_crtc_destroy(struct drm_crtc *crtc) kfree(radeon_crtc); }
+void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id) +{ + struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id]; + struct drm_pending_vblank_event *e = radeon_crtc->event; + struct timeval now; + unsigned long flags; + + spin_lock_irqsave(&rdev->ddev->event_lock, flags); + if (radeon_crtc->flip_old_bo && + radeon_fence_signaled(radeon_crtc->pflip_fence)) { + + drm_vblank_put(rdev->ddev, crtc_id); + + /* wakeup userspace */ + e = radeon_crtc->event; + radeon_crtc->event = NULL; + if (e) { + do_gettimeofday(&now); + e->event.sequence = drm_vblank_count(rdev->ddev, crtc_id); + e->event.tv_sec = now.tv_sec; + e->event.tv_usec = now.tv_usec; + list_add_tail(&e->base.link, &e->base.file_priv->event_list); + wake_up_interruptible(&e->base.file_priv->event_wait); + } + } + spin_unlock_irqrestore(&rdev->ddev->event_lock, flags); +} + +static int radeon_crtc_page_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct radeon_framebuffer *old_radeon_fb; + struct radeon_framebuffer *new_radeon_fb; + struct drm_gem_object *obj; + struct radeon_bo *rbo; + struct radeon_fence *fence; + unsigned long flags; + u64 base; + int r; + + spin_lock_irqsave(&dev->event_lock, flags); + if (radeon_crtc->flip_old_bo) { + spin_unlock_irqrestore(&dev->event_lock, flags); + return -EBUSY; + } + spin_unlock_irqrestore(&dev->event_lock, flags); + + old_radeon_fb = to_radeon_framebuffer(crtc->fb); + new_radeon_fb = to_radeon_framebuffer(fb); + + /* pin the new buffer */ + obj = new_radeon_fb->obj; + rbo = obj->driver_private; + r = radeon_bo_reserve(rbo, false); + if (unlikely(r != 0)) + return r; + r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &base); + if (unlikely(r != 0)) { + radeon_bo_unreserve(rbo); + return -EINVAL; + } + radeon_bo_unreserve(rbo); + + /* update crtc fb */ + crtc->fb = fb; + + r = drm_vblank_get(dev, radeon_crtc->crtc_id); + if (r) + return r; + + /* 64 ought to cover us */ + r = radeon_ring_lock(rdev, 64); + if (r) + return r; + + /* set the proper interrupt */ + radeon_pre_page_flip(rdev, radeon_crtc->crtc_id); + /* put the flip on the ring */ + radeon_page_flip(crtc, base); + /* emit a fence */ + radeon_fence_create(rdev, &fence); + radeon_fence_emit(rdev, fence); + radeon_ring_unlock_commit(rdev); + + /* schedule unpin of the old buffer */ + obj = old_radeon_fb->obj; + rbo = obj->driver_private; + spin_lock_irqsave(&dev->event_lock, flags); + radeon_crtc->flip_old_bo = rbo; + radeon_crtc->event = event; + radeon_crtc->pflip_fence = fence; + spin_unlock_irqrestore(&dev->event_lock, flags); + + return 0; +} + static const struct drm_crtc_funcs radeon_crtc_funcs = { .cursor_set = radeon_crtc_cursor_set, .cursor_move = radeon_crtc_cursor_move, .gamma_set = radeon_crtc_gamma_set, .set_config = drm_crtc_helper_set_config, .destroy = radeon_crtc_destroy, + .page_flip = radeon_crtc_page_flip, };
static void radeon_crtc_init(struct drm_device *dev, int index) diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index c20669b..0d2bcd7 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -47,7 +47,7 @@ * - 2.4.0 - add crtc id query * - 2.5.0 - add get accel 2 to work around ddx breakage for evergreen * - 2.6.0 - add tiling config query (r6xx+), add initial HiZ support (r300->r500) - * 2.7.0 - fixups for r600 2D tiling support. (no external ABI change), add eg dyn gpr regs + * 2.7.0 - fixups for r600 2D tiling support. (no external ABI change), add eg dyn gpr regs, pageflip support */ #define KMS_DRIVER_MAJOR 2 #define KMS_DRIVER_MINOR 7 diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index d2c9647..fa034c8 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -59,12 +59,43 @@ static void radeon_hotplug_work_func(struct work_struct *work) drm_helper_hpd_irq_event(dev); }
+/* + * Handle unpin events outside the interrupt handler proper. + */ +static void radeon_unpin_work_func(struct work_struct *work) +{ + struct radeon_device *rdev = container_of(work, struct radeon_device, + unpin_work); + struct radeon_crtc *radeon_crtc; + int i, r; + + for (i = 0; i < rdev->num_crtc; i++) { + radeon_crtc = rdev->mode_info.crtcs[i]; + if (radeon_crtc->flip_old_bo && + radeon_fence_signaled(radeon_crtc->pflip_fence)) { + radeon_post_page_flip(rdev, radeon_crtc->crtc_id); + /* unpin of the old buffer */ + r = radeon_bo_reserve(radeon_crtc->flip_old_bo, false); + if (likely(r == 0)) { + r = radeon_bo_unpin(radeon_crtc->flip_old_bo); + if (unlikely(r != 0)) { + DRM_ERROR("failed to unpin buffer after flip\n"); + } + radeon_bo_unreserve(radeon_crtc->flip_old_bo); + radeon_crtc->flip_old_bo = NULL; + radeon_fence_unref(&radeon_crtc->pflip_fence); + } + } + } +} + void radeon_driver_irq_preinstall_kms(struct drm_device *dev) { struct radeon_device *rdev = dev->dev_private; unsigned i;
INIT_WORK(&rdev->hotplug_work, radeon_hotplug_work_func); + INIT_WORK(&rdev->unpin_work, radeon_unpin_work_func);
/* Disable *all* interrupts */ rdev->irq.sw_int = false; @@ -179,3 +210,34 @@ void radeon_irq_kms_sw_irq_put(struct radeon_device *rdev) spin_unlock_irqrestore(&rdev->irq.sw_lock, irqflags); }
+void radeon_irq_kms_pflip_irq_get(struct radeon_device *rdev, int crtc) +{ + unsigned long irqflags; + + if (crtc < 0 || crtc >= rdev->num_crtc) + return; + + spin_lock_irqsave(&rdev->irq.pflip_lock[crtc], irqflags); + if (rdev->ddev->irq_enabled && (++rdev->irq.pflip_refcount[crtc] == 1)) { + rdev->irq.pflip[crtc] = true; + radeon_irq_set(rdev); + } + spin_unlock_irqrestore(&rdev->irq.pflip_lock[crtc], irqflags); +} + +void radeon_irq_kms_pflip_irq_put(struct radeon_device *rdev, int crtc) +{ + unsigned long irqflags; + + if (crtc < 0 || crtc >= rdev->num_crtc) + return; + + spin_lock_irqsave(&rdev->irq.pflip_lock[crtc], irqflags); + BUG_ON(rdev->ddev->irq_enabled && rdev->irq.pflip_refcount[crtc] <= 0); + if (rdev->ddev->irq_enabled && (--rdev->irq.pflip_refcount[crtc] == 0)) { + rdev->irq.pflip[crtc] = false; + radeon_irq_set(rdev); + } + spin_unlock_irqrestore(&rdev->irq.pflip_lock[crtc], irqflags); +} + diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 61b9243..edaf692 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -278,6 +278,10 @@ struct radeon_crtc { fixed20_12 hsc; struct drm_display_mode native_mode; int pll_id; + /* page flipping */ + struct radeon_bo *flip_old_bo; + struct drm_pending_vblank_event *event; + struct radeon_fence *pflip_fence; };
struct radeon_encoder_primary_dac { @@ -653,4 +657,7 @@ int radeon_fbdev_total_size(struct radeon_device *rdev); bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj);
void radeon_fb_output_poll_changed(struct radeon_device *rdev); + +void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id); + #endif diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index 834a3a9..d7e29d8 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c @@ -46,6 +46,22 @@ void rs600_gpu_init(struct radeon_device *rdev); int rs600_mc_wait_for_idle(struct radeon_device *rdev);
+void rs600_page_flip(struct drm_crtc *crtc, u64 crtc_base) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); +#if 0 + radeon_ring_write(rdev, + PACKET0(AVIVO_D1GRPH_SECONDARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, 0)); + radeon_ring_write(rdev, (u32)crtc_base); +#endif + radeon_ring_write(rdev, + PACKET0(AVIVO_D1GRPH_PRIMARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, 0)); + radeon_ring_write(rdev, (u32)crtc_base); + +} + void rs600_pm_misc(struct radeon_device *rdev) { int requested_index = rdev->pm.requested_power_state_index; @@ -515,10 +531,12 @@ int rs600_irq_set(struct radeon_device *rdev) 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] || + rdev->irq.pflip[0]) { mode_int |= S_006540_D1MODE_VBLANK_INT_MASK(1); } - if (rdev->irq.crtc_vblank_int[1]) { + if (rdev->irq.crtc_vblank_int[1] || + rdev->irq.pflip[1]) { mode_int |= S_006540_D2MODE_VBLANK_INT_MASK(1); } if (rdev->irq.hpd[0]) { @@ -589,6 +607,7 @@ int rs600_irq_process(struct radeon_device *rdev) { u32 status, msi_rearm; bool queue_hotplug = false; + bool queue_unpin = false;
/* reset gui idle ack. the status bit is broken */ rdev->irq.gui_idle_acked = false; @@ -609,14 +628,26 @@ int rs600_irq_process(struct radeon_device *rdev) } /* Vertical blank interrupts */ if (G_007EDC_LB_D1_VBLANK_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) { - drm_handle_vblank(rdev->ddev, 0); - rdev->pm.vblank_sync = true; - wake_up(&rdev->irq.vblank_queue); + if (rdev->irq.crtc_vblank_int[0]) { + drm_handle_vblank(rdev->ddev, 0); + rdev->pm.vblank_sync = true; + wake_up(&rdev->irq.vblank_queue); + } + if (rdev->irq.pflip[0]) { + radeon_crtc_handle_flip(rdev, 0); + queue_unpin = true; + } } if (G_007EDC_LB_D2_VBLANK_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) { - drm_handle_vblank(rdev->ddev, 1); - rdev->pm.vblank_sync = true; - wake_up(&rdev->irq.vblank_queue); + if (rdev->irq.crtc_vblank_int[1]) { + drm_handle_vblank(rdev->ddev, 1); + rdev->pm.vblank_sync = true; + wake_up(&rdev->irq.vblank_queue); + } + if (rdev->irq.pflip[1]) { + radeon_crtc_handle_flip(rdev, 1); + queue_unpin = true; + } } if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(rdev->irq.stat_regs.r500.disp_int)) { queue_hotplug = true; @@ -632,6 +663,8 @@ int rs600_irq_process(struct radeon_device *rdev) rdev->irq.gui_idle_acked = false; if (queue_hotplug) queue_work(rdev->wq, &rdev->hotplug_work); + if (queue_unpin) + queue_work(rdev->wq, &rdev->unpin_work); if (rdev->msi_enabled) { switch (rdev->family) { case CHIP_RS600: diff --git a/drivers/gpu/drm/radeon/rs600d.h b/drivers/gpu/drm/radeon/rs600d.h index a27c13a..ebe478b 100644 --- a/drivers/gpu/drm/radeon/rs600d.h +++ b/drivers/gpu/drm/radeon/rs600d.h @@ -28,6 +28,19 @@ #ifndef __RS600D_H__ #define __RS600D_H__
+/* PM4 */ +#define PACKET_TYPE0 0 +#define PACKET_TYPE1 1 +#define PACKET_TYPE2 2 +#define PACKET_TYPE3 3 + +#define PACKET0(reg, n) ((PACKET_TYPE0 << 30) | \ + (((reg) >> 2) & 0xFFFF) | \ + ((n) & 0x3FFF) << 16) +#define PACKET3(op, n) ((PACKET_TYPE3 << 30) | \ + (((op) & 0xFF) << 8) | \ + ((n) & 0x3FFF) << 16) + /* Registers */ #define R_000040_GEN_INT_CNTL 0x000040 #define S_000040_SCRATCH_INT_MASK(x) (((x) & 0x1) << 18) diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index 245374e..09fa5cd 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -42,6 +42,40 @@ static void rv770_gpu_init(struct radeon_device *rdev); void rv770_fini(struct radeon_device *rdev);
+void rv770_page_flip(struct drm_crtc *crtc, u64 crtc_base) +{ + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); +#if 0 + if (radeon_crtc->crtc_id) { + radeon_ring_write(rdev, + PACKET0(D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, 0)); + radeon_ring_write(rdev, upper_32_bits(crtc_base)); + } else { + radeon_ring_write(rdev, + PACKET0(D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH, 0)); + radeon_ring_write(rdev, upper_32_bits(crtc_base)); + } + radeon_ring_write(rdev, + PACKET0(D1GRPH_SECONDARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, 0)); + radeon_ring_write(rdev, (u32)crtc_base); +#endif + if (radeon_crtc->crtc_id) { + radeon_ring_write(rdev, + PACKET0(D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, 0)); + radeon_ring_write(rdev, upper_32_bits(crtc_base)); + } else { + radeon_ring_write(rdev, + PACKET0(D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH, 0)); + radeon_ring_write(rdev, upper_32_bits(crtc_base)); + } + radeon_ring_write(rdev, + PACKET0(D1GRPH_PRIMARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset, 0)); + radeon_ring_write(rdev, (u32)crtc_base); + +} + /* get temperature in millidegrees */ u32 rv770_get_temp(struct radeon_device *rdev) { diff --git a/drivers/gpu/drm/radeon/rv770d.h b/drivers/gpu/drm/radeon/rv770d.h index b7a5a20..aa832cb 100644 --- a/drivers/gpu/drm/radeon/rv770d.h +++ b/drivers/gpu/drm/radeon/rv770d.h @@ -351,4 +351,43 @@
#define SRBM_STATUS 0x0E50
+#define D1GRPH_PRIMARY_SURFACE_ADDRESS 0x6110 +#define D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH 0x6914 +#define D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH 0x6114 +#define D1GRPH_SECONDARY_SURFACE_ADDRESS 0x6118 +#define D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH 0x691c +#define D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH 0x611c +#define D1GRPH_UPDATE 0x6144 +# define D1GRPH_SURFACE_UPDATE_PENDING (1 << 2) +# define D1GRPH_UPDATE_LOCK (1 << 16) + +/* PM4 */ +#define PACKET_TYPE0 0 +#define PACKET_TYPE1 1 +#define PACKET_TYPE2 2 +#define PACKET_TYPE3 3 + +#define PACKET0(reg, n) ((PACKET_TYPE0 << 30) | \ + (((reg) >> 2) & 0xFFFF) | \ + ((n) & 0x3FFF) << 16) +#define PACKET3(op, n) ((PACKET_TYPE3 << 30) | \ + (((op) & 0xFF) << 8) | \ + ((n) & 0x3FFF) << 16) + +#define PACKET3_WAIT_REG_MEM 0x3C +# define PACKET3_WAIT_REG_MEM_ENGINE(x) ((x) << 8) +# define ENGINE_ME 0 +# define ENGINE_PFP 1 +# define PACKET3_WAIT_REG_MEM_MEM_SPACE(x) ((x) << 4) +# define MEM_SPACE_REG 0 +# define MEM_SPACE_MEM 1 +# define PACKET3_WAIT_REG_MEM_FUNCTION(x) ((x) << 0) +# define FUNCTION_ALWAYS 0 +# define FUNCTION_LT 1 +# define FUNCTION_LTE 2 +# define FUNCTION_E 3 +# define FUNCTION_NE 4 +# define FUNCTION_GTE 5 +# define FUNCTION_GT 6 + #endif
requires drm 2.7.0 or higher
Signed-off-by: Alex Deucher alexdeucher@gmail.com Signed-off-by: Jerome Glisse jglisse@redhat.com --- src/drmmode_display.c | 78 ++++++++++++++++++++- src/drmmode_display.h | 4 + src/radeon.h | 2 + src/radeon_dri2.c | 188 ++++++++++++++++++++++++++++++++++++++++++++----- src/radeon_dri2.h | 2 + src/radeon_kms.c | 8 ++ 6 files changed, 264 insertions(+), 18 deletions(-)
diff --git a/src/drmmode_display.c b/src/drmmode_display.c index 37010cd..f6a4217 100644 --- a/src/drmmode_display.c +++ b/src/drmmode_display.c @@ -29,6 +29,7 @@ #include "config.h" #endif
+#include <errno.h> #ifdef XF86DRM_MODE #include <sys/ioctl.h> #include "micmap.h" @@ -1195,6 +1196,25 @@ drmmode_vblank_handler(int fd, unsigned int frame, unsigned int tv_sec, }
static void +drmmode_flip_handler(int fd, unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data) +{ + drmmode_ptr drmmode = event_data; + + drmmode->flip_count--; + if (drmmode->flip_count > 0) + return; + + drmModeRmFB(drmmode->fd, drmmode->old_fb_id); + + if (drmmode->event_data == NULL) + return; + + radeon_dri2_flip_event_handler(frame, tv_sec, tv_usec, drmmode->event_data); +} + + +static void drm_wakeup_handler(pointer data, int err, pointer p) { drmmode_ptr drmmode = data; @@ -1236,8 +1256,9 @@ Bool drmmode_pre_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int cpp) drmmode->flip_count = 0; drmmode->event_context.version = DRM_EVENT_CONTEXT_VERSION; drmmode->event_context.vblank_handler = drmmode_vblank_handler; - drmmode->event_context.page_flip_handler = NULL; + drmmode->event_context.page_flip_handler = drmmode_flip_handler; if (info->dri->pKernelDRMVersion->version_minor >= 4) { + drmmode->flip_count = 0; AddGeneralSocket(drmmode->fd); RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA, drm_wakeup_handler, drmmode); @@ -1476,4 +1497,59 @@ void drmmode_uevent_fini(ScrnInfoPtr scrn, drmmode_ptr drmmode) #endif }
+Bool radeon_do_pageflip(ScrnInfoPtr scrn, struct radeon_bo *new_front, void *data) +{ + RADEONInfoPtr info = RADEONPTR(scrn); + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); + drmmode_crtc_private_ptr drmmode_crtc = config->crtc[0]->driver_private; + drmmode_ptr drmmode = drmmode_crtc->drmmode; + unsigned int pitch = scrn->displayWidth * info->CurrentLayout.pixel_bytes; + int i, old_fb_id; + + /* + * Create a new handle for the back buffer + */ + old_fb_id = drmmode->fb_id; + if (drmModeAddFB(drmmode->fd, scrn->virtualX, scrn->virtualY, + scrn->depth, scrn->bitsPerPixel, pitch, + new_front->handle, &drmmode->fb_id)) + goto error_out; + + /* + * Queue flips on all enabled CRTCs + * Note that if/when we get per-CRTC buffers, we'll have to update this. + * Right now it assumes a single shared fb across all CRTCs, with the + * kernel fixing up the offset of each CRTC as necessary. + * + * Also, flips queued on disabled or incorrectly configured displays + * may never complete; this is a configuration error. + */ + for (i = 0; i < config->num_crtc; i++) { + if (!config->crtc[i]->enabled) + continue; + + drmmode->event_data = data; + drmmode->flip_count++; + drmmode_crtc = config->crtc[i]->driver_private; + if (drmModePageFlip(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, + drmmode->fb_id, DRM_MODE_PAGE_FLIP_EVENT, drmmode)) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "flip queue failed: %s\n", strerror(errno)); + goto error_undo; + } + } + + drmmode->old_fb_id = old_fb_id; + return TRUE; + +error_undo: + drmModeRmFB(drmmode->fd, drmmode->fb_id); + drmmode->fb_id = old_fb_id; + +error_out: + xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Page flip failed: %s\n", + strerror(errno)); + return FALSE; +} + #endif diff --git a/src/drmmode_display.h b/src/drmmode_display.h index a9891b2..2226075 100644 --- a/src/drmmode_display.h +++ b/src/drmmode_display.h @@ -39,6 +39,7 @@ typedef struct { int fd; unsigned fb_id; + unsigned old_fb_id; drmModeResPtr mode_res; drmModeFBPtr mode_fb; int cpp; @@ -50,6 +51,7 @@ typedef struct { #endif drmEventContext event_context; int flip_count; + void *event_data; } drmmode_rec, *drmmode_ptr;
typedef struct { @@ -95,6 +97,8 @@ extern Bool drmmode_setup_colormap(ScreenPtr pScreen, ScrnInfoPtr pScrn); extern void drmmode_uevent_init(ScrnInfoPtr scrn, drmmode_ptr drmmode); extern void drmmode_uevent_fini(ScrnInfoPtr scrn, drmmode_ptr drmmode);
+Bool radeon_do_pageflip(ScrnInfoPtr scrn, struct radeon_bo *new_front, void *data); + #endif
#endif diff --git a/src/radeon.h b/src/radeon.h index 21c6d1c..8ace884 100644 --- a/src/radeon.h +++ b/src/radeon.h @@ -1071,6 +1071,8 @@ typedef struct { struct radeon_bo *bicubic_bo; void *bicubic_memory; int bicubic_offset; + /* kms pageflipping */ + Bool allowPageFlip; } RADEONInfoRec, *RADEONInfoPtr;
#define RADEONWaitForFifo(pScrn, entries) \ diff --git a/src/radeon_dri2.c b/src/radeon_dri2.c index 2668812..fb0d83d 100644 --- a/src/radeon_dri2.c +++ b/src/radeon_dri2.c @@ -549,12 +549,100 @@ radeon_dri2_unref_buffer(BufferPtr buffer) } }
+static Bool +radeon_dri2_schedule_flip(ScrnInfoPtr scrn, ClientPtr client, + DrawablePtr draw, DRI2BufferPtr front, + DRI2BufferPtr back, DRI2SwapEventPtr func, + void *data) +{ + struct dri2_buffer_priv *back_priv; + struct radeon_exa_pixmap_priv *exa_priv; + DRI2FrameEventPtr flip_info; + + flip_info = calloc(1, sizeof(DRI2FrameEventRec)); + if (!flip_info) + return FALSE; + + flip_info->drawable_id = draw->id; + flip_info->client = client; + flip_info->type = DRI2_SWAP; + flip_info->event_complete = func; + flip_info->event_data = data; + xf86DrvMsg(scrn->scrnIndex, X_INFO, "%s:%d fevent[%p]\n", __func__, __LINE__, flip_info); + + /* Page flip the full screen buffer */ + back_priv = back->driverPrivate; + exa_priv = exaGetPixmapDriverPrivate(back_priv->pixmap); + return radeon_do_pageflip(scrn, exa_priv->bo, flip_info); +} + +static Bool +can_exchange(DRI2BufferPtr front, DRI2BufferPtr back) +{ + struct dri2_buffer_priv *front_priv = front->driverPrivate; + struct dri2_buffer_priv *back_priv = back->driverPrivate; + PixmapPtr front_pixmap = front_priv->pixmap; + PixmapPtr back_pixmap = back_priv->pixmap; + + if (front_pixmap->drawable.width != back_pixmap->drawable.width) + return FALSE; + + if (front_pixmap->drawable.height != back_pixmap->drawable.height) + return FALSE; + + /* XXX should we be checking depth instead of bpp? */ +#if 0 + if (front_pixmap->drawable.depth != back_pixmap->drawable.depth) + return FALSE; +#else + if (front_pixmap->drawable.bitsPerPixel != back_pixmap->drawable.bitsPerPixel) + return FALSE; +#endif + + return TRUE; +} + +static void +radeon_dri2_exchange_buffers(DrawablePtr draw, DRI2BufferPtr front, DRI2BufferPtr back) +{ + struct dri2_buffer_priv *front_priv = front->driverPrivate; + struct dri2_buffer_priv *back_priv = back->driverPrivate; + struct radeon_exa_pixmap_priv *front_radeon, *back_radeon; + ScreenPtr screen; + RADEONInfoPtr info; + struct radeon_bo *bo; + int tmp; + + /* Swap BO names so DRI works */ + tmp = front->name; + front->name = back->name; + back->name = tmp; + + /* Swap pixmap bos */ + front_radeon = exaGetPixmapDriverPrivate(front_priv->pixmap); + back_radeon = exaGetPixmapDriverPrivate(back_priv->pixmap); + bo = back_radeon->bo; + back_radeon->bo = front_radeon->bo; + front_radeon->bo = bo; + + /* Do we need to update the Screen? */ + screen = draw->pScreen; + info = RADEONPTR(xf86Screens[screen->myNum]); + if (front_radeon->bo == info->front_bo) { + radeon_bo_unref(info->front_bo); + info->front_bo = back_radeon->bo; + radeon_bo_ref(info->front_bo); + front_radeon = exaGetPixmapDriverPrivate(screen->GetScreenPixmap(screen)); + front_radeon->bo = bo; + } +} + void radeon_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec, unsigned int tv_usec, void *event_data) { DRI2FrameEventPtr event = event_data; + RADEONInfoPtr info; DrawablePtr drawable; - ClientPtr client; ScreenPtr screen; ScrnInfoPtr scrn; int status; @@ -563,7 +651,7 @@ void radeon_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec, RegionRec region;
if (!event->valid) - goto cleanup; + goto cleanup;
status = dixLookupDrawable(&drawable, event->drawable_id, serverClient, M_ANY, DixWriteAccess); @@ -572,25 +660,45 @@ void radeon_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec,
screen = drawable->pScreen; scrn = xf86Screens[screen->myNum]; - client = event->client; + info = RADEONPTR(scrn);
switch (event->type) { case DRI2_FLIP: + if (info->allowPageFlip && + DRI2CanFlip(drawable) && + can_exchange(event->front, event->back) && + radeon_dri2_schedule_flip(scrn, + event->client, + drawable, + event->front, + event->back, + event->event_complete, + event->event_data)) { + radeon_dri2_exchange_buffers(drawable, event->front, event->back); + break; + } + /* else fall through to exchange/blit */ case DRI2_SWAP: - box.x1 = 0; - box.y1 = 0; - box.x2 = drawable->width; - box.y2 = drawable->height; - REGION_INIT(pScreen, ®ion, &box, 0); - radeon_dri2_copy_region(drawable, ®ion, event->front, event->back); - swap_type = DRI2_BLIT_COMPLETE; - - DRI2SwapComplete(client, drawable, frame, tv_sec, tv_usec, + if (DRI2CanExchange(drawable) && + can_exchange(event->front, event->back)) { + radeon_dri2_exchange_buffers(drawable, event->front, event->back); + swap_type = DRI2_EXCHANGE_COMPLETE; + } else { + box.x1 = 0; + box.y1 = 0; + box.x2 = drawable->width; + box.y2 = drawable->height; + REGION_INIT(pScreen, ®ion, &box, 0); + radeon_dri2_copy_region(drawable, ®ion, event->front, event->back); + swap_type = DRI2_BLIT_COMPLETE; + } + + DRI2SwapComplete(event->client, drawable, frame, tv_sec, tv_usec, swap_type, event->event_complete, event->event_data);
break; case DRI2_WAITMSC: - DRI2WaitMSCComplete(client, drawable, frame, tv_sec, tv_usec); + DRI2WaitMSCComplete(event->client, drawable, frame, tv_sec, tv_usec); break; default: /* Unknown type */ @@ -638,7 +746,7 @@ static int radeon_dri2_get_msc(DrawablePtr draw, CARD64 *ust, CARD64 *msc) RADEONInfoPtr info = RADEONPTR(scrn); drmVBlank vbl; int ret; - int crtc= radeon_dri2_drawable_crtc(draw); + int crtc = radeon_dri2_drawable_crtc(draw);
/* Drawable not displayed, make up a value */ if (crtc == -1) { @@ -794,6 +902,46 @@ out_complete: return TRUE; }
+void radeon_dri2_flip_event_handler(unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data) +{ + DRI2FrameEventPtr flip = event_data; + DrawablePtr drawable; + ScreenPtr screen; + ScrnInfoPtr scrn; + int status; + PixmapPtr pixmap; + + status = dixLookupDrawable(&drawable, flip->drawable_id, serverClient, + M_ANY, DixWriteAccess); + if (status != Success) { + free(flip); + return; + } + + screen = drawable->pScreen; + scrn = xf86Screens[screen->myNum]; + + pixmap = screen->GetScreenPixmap(screen); + xf86DrvMsg(scrn->scrnIndex, X_INFO, "%s:%d fevent[%p] width %d pitch %d (/4 %d)\n", + __func__, __LINE__, flip, pixmap->drawable.width, pixmap->devKind, pixmap->devKind/4); + + /* We assume our flips arrive in order, so we don't check the frame */ + switch (flip->type) { + case DRI2_SWAP: + DRI2SwapComplete(flip->client, drawable, frame, tv_sec, tv_usec, + DRI2_FLIP_COMPLETE, flip->event_complete, + flip->event_data); + break; + default: + xf86DrvMsg(scrn->scrnIndex, X_WARNING, "%s: unknown vblank event received\n", __func__); + /* Unknown type */ + break; + } + + free(flip); +} + /* * ScheduleSwap is responsible for requesting a DRM vblank event for the * appropriate frame. @@ -877,6 +1025,15 @@ static int radeon_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, }
current_msc = vbl.reply.sequence; + + /* Flips need to be submitted one frame before */ + if (info->allowPageFlip && + DRI2CanFlip(draw) && + can_exchange(front, back)) { + swap_type = DRI2_FLIP; + flip = 1; + } + swap_info->type = swap_type;
/* Correct target_msc by 'flip' if swap_type == DRI2_FLIP. @@ -893,9 +1050,6 @@ static int radeon_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, */ if (divisor == 0 || current_msc < *target_msc) { vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; - if (crtc > 0) - vbl.request.type |= DRM_VBLANK_SECONDARY; - /* If non-pageflipping, but blitting/exchanging, we need to use * DRM_VBLANK_NEXTONMISS to avoid unreliable timestamping later * on. diff --git a/src/radeon_dri2.h b/src/radeon_dri2.h index 688530f..7995286 100644 --- a/src/radeon_dri2.h +++ b/src/radeon_dri2.h @@ -44,5 +44,7 @@ xf86CrtcPtr radeon_covering_crtc(ScrnInfoPtr pScrn, BoxPtr box, xf86CrtcPtr desired, BoxPtr crtc_box_ret); void radeon_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec, unsigned int tv_usec, void *event_data); +void radeon_dri2_flip_event_handler(unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data);
#endif diff --git a/src/radeon_kms.c b/src/radeon_kms.c index b94544e..7773d02 100644 --- a/src/radeon_kms.c +++ b/src/radeon_kms.c @@ -70,6 +70,7 @@ const OptionInfoRec RADEONOptions_KMS[] = { { OPTION_EXA_VSYNC, "EXAVSync", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_EXA_PIXMAPS, "EXAPixmaps", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_ZAPHOD_HEADS, "ZaphodHeads", OPTV_STRING, {0}, FALSE }, + { OPTION_PAGE_FLIP, "EnablePageFlip", OPTV_BOOLEAN, {0}, FALSE }, { -1, NULL, OPTV_NONE, {0}, FALSE } };
@@ -616,6 +617,13 @@ Bool RADEONPreInit_KMS(ScrnInfoPtr pScrn, int flags) xf86DrvMsg(pScrn->scrnIndex, X_INFO, "KMS Color Tiling: %sabled\n", info->allowColorTiling ? "en" : "dis");
+ if (info->dri->pKernelDRMVersion->version_minor >= 7) { + info->allowPageFlip = xf86ReturnOptValBool(info->Options, + OPTION_PAGE_FLIP, TRUE); + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "KMS Pageflipping: %sabled\n", info->allowPageFlip ? "en" : "dis"); + } + if (drmmode_pre_init(pScrn, &info->drmmode, pScrn->bitsPerPixel / 8) == FALSE) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Kernel modesetting setup failed\n"); goto fail;
On Tue, Oct 26, 2010 at 4:30 AM, Alex Deucher alexdeucher@gmail.com wrote:
This set of patches adds pageflipping support for radeon. There are two drm patches and one ddx patch required. The drm patches are against Dave's drm-next branch and the ddx patch is against git master. So far it's been lightly tested with openarena on a variety of radeons. Feedback appreciated.
Just a heads up, this doesn't work with tiling enabled on 6xx/7xx/evergreen yet.
Alex
On Tue, Oct 26, 2010 at 4:55 AM, Alex Deucher alexdeucher@gmail.com wrote:
On Tue, Oct 26, 2010 at 4:30 AM, Alex Deucher alexdeucher@gmail.com wrote:
This set of patches adds pageflipping support for radeon. There are two drm patches and one ddx patch required. The drm patches are against Dave's drm-next branch and the ddx patch is against git master. So far it's been lightly tested with openarena on a variety of radeons. Feedback appreciated.
Just a heads up, this doesn't work with tiling enabled on 6xx/7xx/evergreen yet.
Fixed now with this additional patch: http://people.freedesktop.org/~agd5f/pflip/0002-radeon-kms-allow-tiled-front...
Alex
Alex
dri-devel@lists.freedesktop.org