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

Commit fb98257a authored by Christian Koenig's avatar Christian Koenig Committed by Christian König
Browse files

drm/radeon: apply Murphy's law to the kms irq code v3



1. It is really dangerous to have more than one
   spinlock protecting the same information.

2. radeon_irq_set sometimes wasn't called with lock
   protection, so it can happen that more than one
   CPU would tamper with the irq regs at the same
   time.

3. The pm.gui_idle variable was assuming that the 3D
   engine wasn't becoming idle between testing the
   register and setting the variable. So just remove
   it and test the register directly.

v2: Also handle the hpd irq code the same way.
v3: Rename hpd parameter for clarification.

Signed-off-by: default avatarChristian Koenig <christian.koenig@amd.com>
Reviewed-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent c20dc369
Loading
Loading
Loading
Loading
+6 −15
Original line number Original line Diff line number Diff line
@@ -428,6 +428,7 @@ void evergreen_hpd_init(struct radeon_device *rdev)
{
{
	struct drm_device *dev = rdev->ddev;
	struct drm_device *dev = rdev->ddev;
	struct drm_connector *connector;
	struct drm_connector *connector;
	unsigned enabled = 0;
	u32 tmp = DC_HPDx_CONNECTION_TIMER(0x9c4) |
	u32 tmp = DC_HPDx_CONNECTION_TIMER(0x9c4) |
		DC_HPDx_RX_INT_TIMER(0xfa) | DC_HPDx_EN;
		DC_HPDx_RX_INT_TIMER(0xfa) | DC_HPDx_EN;


@@ -436,73 +437,64 @@ void evergreen_hpd_init(struct radeon_device *rdev)
		switch (radeon_connector->hpd.hpd) {
		switch (radeon_connector->hpd.hpd) {
		case RADEON_HPD_1:
		case RADEON_HPD_1:
			WREG32(DC_HPD1_CONTROL, tmp);
			WREG32(DC_HPD1_CONTROL, tmp);
			rdev->irq.hpd[0] = true;
			break;
			break;
		case RADEON_HPD_2:
		case RADEON_HPD_2:
			WREG32(DC_HPD2_CONTROL, tmp);
			WREG32(DC_HPD2_CONTROL, tmp);
			rdev->irq.hpd[1] = true;
			break;
			break;
		case RADEON_HPD_3:
		case RADEON_HPD_3:
			WREG32(DC_HPD3_CONTROL, tmp);
			WREG32(DC_HPD3_CONTROL, tmp);
			rdev->irq.hpd[2] = true;
			break;
			break;
		case RADEON_HPD_4:
		case RADEON_HPD_4:
			WREG32(DC_HPD4_CONTROL, tmp);
			WREG32(DC_HPD4_CONTROL, tmp);
			rdev->irq.hpd[3] = true;
			break;
			break;
		case RADEON_HPD_5:
		case RADEON_HPD_5:
			WREG32(DC_HPD5_CONTROL, tmp);
			WREG32(DC_HPD5_CONTROL, tmp);
			rdev->irq.hpd[4] = true;
			break;
			break;
		case RADEON_HPD_6:
		case RADEON_HPD_6:
			WREG32(DC_HPD6_CONTROL, tmp);
			WREG32(DC_HPD6_CONTROL, tmp);
			rdev->irq.hpd[5] = true;
			break;
			break;
		default:
		default:
			break;
			break;
		}
		}
		radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
		radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
		enabled |= 1 << radeon_connector->hpd.hpd;
	}
	}
	if (rdev->irq.installed)
	radeon_irq_kms_enable_hpd(rdev, enabled);
		evergreen_irq_set(rdev);
}
}


void evergreen_hpd_fini(struct radeon_device *rdev)
void evergreen_hpd_fini(struct radeon_device *rdev)
{
{
	struct drm_device *dev = rdev->ddev;
	struct drm_device *dev = rdev->ddev;
	struct drm_connector *connector;
	struct drm_connector *connector;
	unsigned disabled = 0;


	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
		struct radeon_connector *radeon_connector = to_radeon_connector(connector);
		struct radeon_connector *radeon_connector = to_radeon_connector(connector);
		switch (radeon_connector->hpd.hpd) {
		switch (radeon_connector->hpd.hpd) {
		case RADEON_HPD_1:
		case RADEON_HPD_1:
			WREG32(DC_HPD1_CONTROL, 0);
			WREG32(DC_HPD1_CONTROL, 0);
			rdev->irq.hpd[0] = false;
			break;
			break;
		case RADEON_HPD_2:
		case RADEON_HPD_2:
			WREG32(DC_HPD2_CONTROL, 0);
			WREG32(DC_HPD2_CONTROL, 0);
			rdev->irq.hpd[1] = false;
			break;
			break;
		case RADEON_HPD_3:
		case RADEON_HPD_3:
			WREG32(DC_HPD3_CONTROL, 0);
			WREG32(DC_HPD3_CONTROL, 0);
			rdev->irq.hpd[2] = false;
			break;
			break;
		case RADEON_HPD_4:
		case RADEON_HPD_4:
			WREG32(DC_HPD4_CONTROL, 0);
			WREG32(DC_HPD4_CONTROL, 0);
			rdev->irq.hpd[3] = false;
			break;
			break;
		case RADEON_HPD_5:
		case RADEON_HPD_5:
			WREG32(DC_HPD5_CONTROL, 0);
			WREG32(DC_HPD5_CONTROL, 0);
			rdev->irq.hpd[4] = false;
			break;
			break;
		case RADEON_HPD_6:
		case RADEON_HPD_6:
			WREG32(DC_HPD6_CONTROL, 0);
			WREG32(DC_HPD6_CONTROL, 0);
			rdev->irq.hpd[5] = false;
			break;
			break;
		default:
		default:
			break;
			break;
		}
		}
		disabled |= 1 << radeon_connector->hpd.hpd;
	}
	}
	radeon_irq_kms_disable_hpd(rdev, disabled);
}
}


/* watermark setup */
/* watermark setup */
@@ -2984,7 +2976,6 @@ int evergreen_irq_process(struct radeon_device *rdev)
			break;
			break;
		case 233: /* GUI IDLE */
		case 233: /* GUI IDLE */
			DRM_DEBUG("IH: GUI idle\n");
			DRM_DEBUG("IH: GUI idle\n");
			rdev->pm.gui_idle = true;
			wake_up(&rdev->irq.idle_queue);
			wake_up(&rdev->irq.idle_queue);
			break;
			break;
		default:
		default:
+6 −23
Original line number Original line Diff line number Diff line
@@ -567,43 +567,27 @@ void r100_hpd_init(struct radeon_device *rdev)
{
{
	struct drm_device *dev = rdev->ddev;
	struct drm_device *dev = rdev->ddev;
	struct drm_connector *connector;
	struct drm_connector *connector;
	unsigned enable = 0;


	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
		struct radeon_connector *radeon_connector = to_radeon_connector(connector);
		struct radeon_connector *radeon_connector = to_radeon_connector(connector);
		switch (radeon_connector->hpd.hpd) {
		enable |= 1 << radeon_connector->hpd.hpd;
		case RADEON_HPD_1:
			rdev->irq.hpd[0] = true;
			break;
		case RADEON_HPD_2:
			rdev->irq.hpd[1] = true;
			break;
		default:
			break;
		}
		radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
		radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
	}
	}
	if (rdev->irq.installed)
	radeon_irq_kms_enable_hpd(rdev, enable);
		r100_irq_set(rdev);
}
}


void r100_hpd_fini(struct radeon_device *rdev)
void r100_hpd_fini(struct radeon_device *rdev)
{
{
	struct drm_device *dev = rdev->ddev;
	struct drm_device *dev = rdev->ddev;
	struct drm_connector *connector;
	struct drm_connector *connector;
	unsigned disable = 0;


	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
		struct radeon_connector *radeon_connector = to_radeon_connector(connector);
		struct radeon_connector *radeon_connector = to_radeon_connector(connector);
		switch (radeon_connector->hpd.hpd) {
		disable |= 1 << radeon_connector->hpd.hpd;
		case RADEON_HPD_1:
			rdev->irq.hpd[0] = false;
			break;
		case RADEON_HPD_2:
			rdev->irq.hpd[1] = false;
			break;
		default:
			break;
		}
	}
	}
	radeon_irq_kms_disable_hpd(rdev, disable);
}
}


/*
/*
@@ -782,7 +766,6 @@ int r100_irq_process(struct radeon_device *rdev)
		/* gui idle interrupt */
		/* gui idle interrupt */
		if (status & RADEON_GUI_IDLE_STAT) {
		if (status & RADEON_GUI_IDLE_STAT) {
			rdev->irq.gui_idle_acked = true;
			rdev->irq.gui_idle_acked = true;
			rdev->pm.gui_idle = true;
			wake_up(&rdev->irq.idle_queue);
			wake_up(&rdev->irq.idle_queue);
		}
		}
		/* Vertical blank interrupts */
		/* Vertical blank interrupts */
+10 −28
Original line number Original line Diff line number Diff line
@@ -709,6 +709,7 @@ void r600_hpd_init(struct radeon_device *rdev)
{
{
	struct drm_device *dev = rdev->ddev;
	struct drm_device *dev = rdev->ddev;
	struct drm_connector *connector;
	struct drm_connector *connector;
	unsigned enable = 0;


	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
		struct radeon_connector *radeon_connector = to_radeon_connector(connector);
		struct radeon_connector *radeon_connector = to_radeon_connector(connector);
@@ -729,28 +730,22 @@ void r600_hpd_init(struct radeon_device *rdev)
			switch (radeon_connector->hpd.hpd) {
			switch (radeon_connector->hpd.hpd) {
			case RADEON_HPD_1:
			case RADEON_HPD_1:
				WREG32(DC_HPD1_CONTROL, tmp);
				WREG32(DC_HPD1_CONTROL, tmp);
				rdev->irq.hpd[0] = true;
				break;
				break;
			case RADEON_HPD_2:
			case RADEON_HPD_2:
				WREG32(DC_HPD2_CONTROL, tmp);
				WREG32(DC_HPD2_CONTROL, tmp);
				rdev->irq.hpd[1] = true;
				break;
				break;
			case RADEON_HPD_3:
			case RADEON_HPD_3:
				WREG32(DC_HPD3_CONTROL, tmp);
				WREG32(DC_HPD3_CONTROL, tmp);
				rdev->irq.hpd[2] = true;
				break;
				break;
			case RADEON_HPD_4:
			case RADEON_HPD_4:
				WREG32(DC_HPD4_CONTROL, tmp);
				WREG32(DC_HPD4_CONTROL, tmp);
				rdev->irq.hpd[3] = true;
				break;
				break;
				/* DCE 3.2 */
				/* DCE 3.2 */
			case RADEON_HPD_5:
			case RADEON_HPD_5:
				WREG32(DC_HPD5_CONTROL, tmp);
				WREG32(DC_HPD5_CONTROL, tmp);
				rdev->irq.hpd[4] = true;
				break;
				break;
			case RADEON_HPD_6:
			case RADEON_HPD_6:
				WREG32(DC_HPD6_CONTROL, tmp);
				WREG32(DC_HPD6_CONTROL, tmp);
				rdev->irq.hpd[5] = true;
				break;
				break;
			default:
			default:
				break;
				break;
@@ -759,85 +754,73 @@ void r600_hpd_init(struct radeon_device *rdev)
			switch (radeon_connector->hpd.hpd) {
			switch (radeon_connector->hpd.hpd) {
			case RADEON_HPD_1:
			case RADEON_HPD_1:
				WREG32(DC_HOT_PLUG_DETECT1_CONTROL, DC_HOT_PLUG_DETECTx_EN);
				WREG32(DC_HOT_PLUG_DETECT1_CONTROL, DC_HOT_PLUG_DETECTx_EN);
				rdev->irq.hpd[0] = true;
				break;
				break;
			case RADEON_HPD_2:
			case RADEON_HPD_2:
				WREG32(DC_HOT_PLUG_DETECT2_CONTROL, DC_HOT_PLUG_DETECTx_EN);
				WREG32(DC_HOT_PLUG_DETECT2_CONTROL, DC_HOT_PLUG_DETECTx_EN);
				rdev->irq.hpd[1] = true;
				break;
				break;
			case RADEON_HPD_3:
			case RADEON_HPD_3:
				WREG32(DC_HOT_PLUG_DETECT3_CONTROL, DC_HOT_PLUG_DETECTx_EN);
				WREG32(DC_HOT_PLUG_DETECT3_CONTROL, DC_HOT_PLUG_DETECTx_EN);
				rdev->irq.hpd[2] = true;
				break;
				break;
			default:
			default:
				break;
				break;
			}
			}
		}
		}
		enable |= 1 << radeon_connector->hpd.hpd;
		radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
		radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
	}
	}
	if (rdev->irq.installed)
	radeon_irq_kms_enable_hpd(rdev, enable);
		r600_irq_set(rdev);
}
}


void r600_hpd_fini(struct radeon_device *rdev)
void r600_hpd_fini(struct radeon_device *rdev)
{
{
	struct drm_device *dev = rdev->ddev;
	struct drm_device *dev = rdev->ddev;
	struct drm_connector *connector;
	struct drm_connector *connector;
	unsigned disable = 0;


	if (ASIC_IS_DCE3(rdev)) {
	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
		struct radeon_connector *radeon_connector = to_radeon_connector(connector);
		struct radeon_connector *radeon_connector = to_radeon_connector(connector);
		if (ASIC_IS_DCE3(rdev)) {
			switch (radeon_connector->hpd.hpd) {
			switch (radeon_connector->hpd.hpd) {
			case RADEON_HPD_1:
			case RADEON_HPD_1:
				WREG32(DC_HPD1_CONTROL, 0);
				WREG32(DC_HPD1_CONTROL, 0);
				rdev->irq.hpd[0] = false;
				break;
				break;
			case RADEON_HPD_2:
			case RADEON_HPD_2:
				WREG32(DC_HPD2_CONTROL, 0);
				WREG32(DC_HPD2_CONTROL, 0);
				rdev->irq.hpd[1] = false;
				break;
				break;
			case RADEON_HPD_3:
			case RADEON_HPD_3:
				WREG32(DC_HPD3_CONTROL, 0);
				WREG32(DC_HPD3_CONTROL, 0);
				rdev->irq.hpd[2] = false;
				break;
				break;
			case RADEON_HPD_4:
			case RADEON_HPD_4:
				WREG32(DC_HPD4_CONTROL, 0);
				WREG32(DC_HPD4_CONTROL, 0);
				rdev->irq.hpd[3] = false;
				break;
				break;
				/* DCE 3.2 */
				/* DCE 3.2 */
			case RADEON_HPD_5:
			case RADEON_HPD_5:
				WREG32(DC_HPD5_CONTROL, 0);
				WREG32(DC_HPD5_CONTROL, 0);
				rdev->irq.hpd[4] = false;
				break;
				break;
			case RADEON_HPD_6:
			case RADEON_HPD_6:
				WREG32(DC_HPD6_CONTROL, 0);
				WREG32(DC_HPD6_CONTROL, 0);
				rdev->irq.hpd[5] = false;
				break;
				break;
			default:
			default:
				break;
				break;
			}
			}
		}
		} else {
		} else {
		list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
			struct radeon_connector *radeon_connector = to_radeon_connector(connector);
			switch (radeon_connector->hpd.hpd) {
			switch (radeon_connector->hpd.hpd) {
			case RADEON_HPD_1:
			case RADEON_HPD_1:
				WREG32(DC_HOT_PLUG_DETECT1_CONTROL, 0);
				WREG32(DC_HOT_PLUG_DETECT1_CONTROL, 0);
				rdev->irq.hpd[0] = false;
				break;
				break;
			case RADEON_HPD_2:
			case RADEON_HPD_2:
				WREG32(DC_HOT_PLUG_DETECT2_CONTROL, 0);
				WREG32(DC_HOT_PLUG_DETECT2_CONTROL, 0);
				rdev->irq.hpd[1] = false;
				break;
				break;
			case RADEON_HPD_3:
			case RADEON_HPD_3:
				WREG32(DC_HOT_PLUG_DETECT3_CONTROL, 0);
				WREG32(DC_HOT_PLUG_DETECT3_CONTROL, 0);
				rdev->irq.hpd[2] = false;
				break;
				break;
			default:
			default:
				break;
				break;
			}
			}
		}
		}
		disable |= 1 << radeon_connector->hpd.hpd;
	}
	}
	radeon_irq_kms_disable_hpd(rdev, disable);
}
}


/*
/*
@@ -3476,7 +3459,6 @@ int r600_irq_process(struct radeon_device *rdev)
			break;
			break;
		case 233: /* GUI IDLE */
		case 233: /* GUI IDLE */
			DRM_DEBUG("IH: GUI idle\n");
			DRM_DEBUG("IH: GUI idle\n");
			rdev->pm.gui_idle = true;
			wake_up(&rdev->irq.idle_queue);
			wake_up(&rdev->irq.idle_queue);
			break;
			break;
		default:
		default:
+2 −4
Original line number Original line Diff line number Diff line
@@ -519,8 +519,7 @@ void r600_hdmi_enable(struct drm_encoder *encoder)


	if (rdev->irq.installed) {
	if (rdev->irq.installed) {
		/* if irq is available use it */
		/* if irq is available use it */
		rdev->irq.afmt[dig->afmt->id] = true;
		radeon_irq_kms_enable_afmt(rdev, dig->afmt->id);
		radeon_irq_set(rdev);
	}
	}


	dig->afmt->enabled = true;
	dig->afmt->enabled = true;
@@ -556,8 +555,7 @@ void r600_hdmi_disable(struct drm_encoder *encoder)
		  offset, radeon_encoder->encoder_id);
		  offset, radeon_encoder->encoder_id);


	/* disable irq */
	/* disable irq */
	rdev->irq.afmt[dig->afmt->id] = false;
	radeon_irq_kms_disable_afmt(rdev, dig->afmt->id);
	radeon_irq_set(rdev);


	/* Older chipsets not handled by AtomBIOS */
	/* Older chipsets not handled by AtomBIOS */
	if (rdev->family >= CHIP_R600 && !ASIC_IS_DCE3(rdev)) {
	if (rdev->family >= CHIP_R600 && !ASIC_IS_DCE3(rdev)) {
+19 −16
Original line number Original line Diff line number Diff line
@@ -616,20 +616,19 @@ union radeon_irq_stat_regs {


struct radeon_irq {
struct radeon_irq {
	bool				installed;
	bool				installed;
	spinlock_t			lock;
	bool				sw_int[RADEON_NUM_RINGS];
	bool				sw_int[RADEON_NUM_RINGS];
	int				sw_refcount[RADEON_NUM_RINGS];
	bool				crtc_vblank_int[RADEON_MAX_CRTCS];
	bool				crtc_vblank_int[RADEON_MAX_CRTCS];
	bool				pflip[RADEON_MAX_CRTCS];
	bool				pflip[RADEON_MAX_CRTCS];
	int				pflip_refcount[RADEON_MAX_CRTCS];
	wait_queue_head_t		vblank_queue;
	wait_queue_head_t		vblank_queue;
	bool				hpd[RADEON_MAX_HPD_PINS];
	bool				hpd[RADEON_MAX_HPD_PINS];
	bool				gui_idle;
	bool				gui_idle;
	bool				gui_idle_acked;
	bool				gui_idle_acked;
	wait_queue_head_t		idle_queue;
	wait_queue_head_t		idle_queue;
	bool				afmt[RADEON_MAX_AFMT_BLOCKS];
	bool				afmt[RADEON_MAX_AFMT_BLOCKS];
	spinlock_t sw_lock;
	int sw_refcount[RADEON_NUM_RINGS];
	union radeon_irq_stat_regs	stat_regs;
	union radeon_irq_stat_regs	stat_regs;
	spinlock_t pflip_lock[RADEON_MAX_CRTCS];
	int pflip_refcount[RADEON_MAX_CRTCS];
};
};


int radeon_irq_kms_init(struct radeon_device *rdev);
int radeon_irq_kms_init(struct radeon_device *rdev);
@@ -638,6 +637,11 @@ void radeon_irq_kms_sw_irq_get(struct radeon_device *rdev, int ring);
void radeon_irq_kms_sw_irq_put(struct radeon_device *rdev, int ring);
void radeon_irq_kms_sw_irq_put(struct radeon_device *rdev, int ring);
void radeon_irq_kms_pflip_irq_get(struct radeon_device *rdev, int crtc);
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);
void radeon_irq_kms_pflip_irq_put(struct radeon_device *rdev, int crtc);
void radeon_irq_kms_enable_afmt(struct radeon_device *rdev, int block);
void radeon_irq_kms_disable_afmt(struct radeon_device *rdev, int block);
void radeon_irq_kms_enable_hpd(struct radeon_device *rdev, unsigned hpd_mask);
void radeon_irq_kms_disable_hpd(struct radeon_device *rdev, unsigned hpd_mask);
int radeon_irq_kms_wait_gui_idle(struct radeon_device *rdev);


/*
/*
 * CP & rings.
 * CP & rings.
@@ -1062,7 +1066,6 @@ struct radeon_pm {
	int			active_crtc_count;
	int			active_crtc_count;
	int			req_vblank;
	int			req_vblank;
	bool			vblank_sync;
	bool			vblank_sync;
	bool			gui_idle;
	fixed20_12		max_bandwidth;
	fixed20_12		max_bandwidth;
	fixed20_12		igp_sideport_mclk;
	fixed20_12		igp_sideport_mclk;
	fixed20_12		igp_system_mclk;
	fixed20_12		igp_system_mclk;
Loading