Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -1418,6 +1418,7 @@ int evergreen_irq_set(struct radeon_device *rdev)
	u32 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE;
	u32 crtc1 = 0, crtc2 = 0, crtc3 = 0, crtc4 = 0, crtc5 = 0, crtc6 = 0;
	u32 hpd1, hpd2, hpd3, hpd4, hpd5, hpd6;
	u32 grbm_int_cntl = 0;

	if (!rdev->irq.installed) {
		WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n");
@@ -1490,8 +1491,13 @@ int evergreen_irq_set(struct radeon_device *rdev)
		DRM_DEBUG("evergreen_irq_set: hpd 6\n");
		hpd6 |= DC_HPDx_INT_EN;
	}
	if (rdev->irq.gui_idle) {
		DRM_DEBUG("gui idle\n");
		grbm_int_cntl |= GUI_IDLE_INT_ENABLE;
	}

	WREG32(CP_INT_CNTL, cp_int_cntl);
	WREG32(GRBM_INT_CNTL, grbm_int_cntl);

	WREG32(INT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, crtc1);
	WREG32(INT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, crtc2);
@@ -1853,6 +1859,11 @@ int evergreen_irq_process(struct radeon_device *rdev)
		case 181: /* CP EOP event */
			DRM_DEBUG("IH: CP EOP\n");
			break;
		case 233: /* GUI IDLE */
			DRM_DEBUG("IH: CP EOP\n");
			rdev->pm.gui_idle = true;
			wake_up(&rdev->irq.idle_queue);
			break;
		default:
			DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
			break;
+20 −0
Original line number Diff line number Diff line
@@ -262,6 +262,9 @@ int r100_irq_set(struct radeon_device *rdev)
	if (rdev->irq.sw_int) {
		tmp |= RADEON_SW_INT_ENABLE;
	}
	if (rdev->irq.gui_idle) {
		tmp |= RADEON_GUI_IDLE_MASK;
	}
	if (rdev->irq.crtc_vblank_int[0]) {
		tmp |= RADEON_CRTC_VBLANK_MASK;
	}
@@ -296,6 +299,12 @@ static inline uint32_t r100_irq_ack(struct radeon_device *rdev)
		RADEON_CRTC_VBLANK_STAT | RADEON_CRTC2_VBLANK_STAT |
		RADEON_FP_DETECT_STAT | RADEON_FP2_DETECT_STAT;

	/* the interrupt works, but the status bit is permanently asserted */
	if (rdev->irq.gui_idle && radeon_gui_idle(rdev)) {
		if (!rdev->irq.gui_idle_acked)
			irq_mask |= RADEON_GUI_IDLE_STAT;
	}

	if (irqs) {
		WREG32(RADEON_GEN_INT_STATUS, irqs);
	}
@@ -307,6 +316,9 @@ int r100_irq_process(struct radeon_device *rdev)
	uint32_t status, msi_rearm;
	bool queue_hotplug = false;

	/* reset gui idle ack.  the status bit is broken */
	rdev->irq.gui_idle_acked = false;

	status = r100_irq_ack(rdev);
	if (!status) {
		return IRQ_NONE;
@@ -319,6 +331,12 @@ int r100_irq_process(struct radeon_device *rdev)
		if (status & RADEON_SW_INT_TEST) {
			radeon_fence_process(rdev);
		}
		/* gui idle interrupt */
		if (status & RADEON_GUI_IDLE_STAT) {
			rdev->irq.gui_idle_acked = true;
			rdev->pm.gui_idle = true;
			wake_up(&rdev->irq.idle_queue);
		}
		/* Vertical blank interrupts */
		if (status & RADEON_CRTC_VBLANK_STAT) {
			drm_handle_vblank(rdev->ddev, 0);
@@ -340,6 +358,8 @@ int r100_irq_process(struct radeon_device *rdev)
		}
		status = r100_irq_ack(rdev);
	}
	/* reset gui idle ack.  the status bit is broken */
	rdev->irq.gui_idle_acked = false;
	if (queue_hotplug)
		queue_work(rdev->wq, &rdev->hotplug_work);
	if (rdev->msi_enabled) {
+11 −0
Original line number Diff line number Diff line
@@ -2535,6 +2535,7 @@ int r600_irq_set(struct radeon_device *rdev)
	u32 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE;
	u32 mode_int = 0;
	u32 hpd1, hpd2, hpd3, hpd4 = 0, hpd5 = 0, hpd6 = 0;
	u32 grbm_int_cntl = 0;
	u32 hdmi1, hdmi2;

	if (!rdev->irq.installed) {
@@ -2611,9 +2612,14 @@ int r600_irq_set(struct radeon_device *rdev)
		DRM_DEBUG("r600_irq_set: hdmi 2\n");
		hdmi2 |= R600_HDMI_INT_EN;
	}
	if (rdev->irq.gui_idle) {
		DRM_DEBUG("gui idle\n");
		grbm_int_cntl |= GUI_IDLE_INT_ENABLE;
	}

	WREG32(CP_INT_CNTL, cp_int_cntl);
	WREG32(DxMODE_INT_MASK, mode_int);
	WREG32(GRBM_INT_CNTL, grbm_int_cntl);
	WREG32(R600_HDMI_BLOCK1 + R600_HDMI_CNTL, hdmi1);
	if (ASIC_IS_DCE3(rdev)) {
		WREG32(R600_HDMI_BLOCK3 + R600_HDMI_CNTL, hdmi2);
@@ -2929,6 +2935,11 @@ int r600_irq_process(struct radeon_device *rdev)
		case 181: /* CP EOP event */
			DRM_DEBUG("IH: CP EOP\n");
			break;
		case 233: /* GUI IDLE */
			DRM_DEBUG("IH: CP EOP\n");
			rdev->pm.gui_idle = true;
			wake_up(&rdev->irq.idle_queue);
			break;
		default:
			DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
			break;
+4 −0
Original line number Diff line number Diff line
@@ -376,6 +376,9 @@ struct radeon_irq {
	wait_queue_head_t	vblank_queue;
	/* FIXME: use defines for max hpd/dacs */
	bool            hpd[6];
	bool            gui_idle;
	bool            gui_idle_acked;
	wait_queue_head_t	idle_queue;
	/* FIXME: use defines for max HDMI blocks */
	bool		hdmi[2];
	spinlock_t sw_lock;
@@ -694,6 +697,7 @@ struct radeon_pm {
	int			active_crtcs;
	int			req_vblank;
	bool			vblank_sync;
	bool			gui_idle;
	fixed20_12		max_bandwidth;
	fixed20_12		igp_sideport_mclk;
	fixed20_12		igp_system_mclk;
+1 −0
Original line number Diff line number Diff line
@@ -602,6 +602,7 @@ int radeon_device_init(struct radeon_device *rdev,
	rwlock_init(&rdev->fence_drv.lock);
	INIT_LIST_HEAD(&rdev->gem.objects);
	init_waitqueue_head(&rdev->irq.vblank_queue);
	init_waitqueue_head(&rdev->irq.idle_queue);

	/* setup workqueue */
	rdev->wq = create_workqueue("radeon");
Loading