summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/radeon/atombios_dp.c12
-rw-r--r--drivers/gpu/drm/radeon/r100.c9
-rw-r--r--drivers/gpu/drm/radeon/r600.c21
-rw-r--r--drivers/gpu/drm/radeon/radeon.h2
-rw-r--r--drivers/gpu/drm/radeon/radeon_connectors.c20
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c9
-rw-r--r--drivers/gpu/drm/radeon/radeon_display.c3
-rw-r--r--drivers/gpu/drm/radeon/radeon_irq_kms.c21
-rw-r--r--drivers/gpu/drm/radeon/radeon_mode.h2
-rw-r--r--drivers/gpu/drm/radeon/rs600.c9
10 files changed, 98 insertions, 10 deletions
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
index 784ba80afcb6..0d63c4436e7c 100644
--- a/drivers/gpu/drm/radeon/atombios_dp.c
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
@@ -507,6 +507,18 @@ static bool atom_dp_get_link_status(struct radeon_connector *radeon_connector,
 	return true;
 }
 
+bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector)
+{
+	struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
+	u8 link_status[DP_LINK_STATUS_SIZE];
+
+	if (!atom_dp_get_link_status(radeon_connector, link_status))
+		return false;
+	if (dp_channel_eq_ok(link_status, dig_connector->dp_lane_count))
+		return false;
+	return true;
+}
+
 static void dp_set_power(struct radeon_connector *radeon_connector, u8 power_state)
 {
 	struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index 2b534c528aaf..b7baf16c11d7 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -289,6 +289,7 @@ static inline uint32_t r100_irq_ack(struct radeon_device *rdev)
 int r100_irq_process(struct radeon_device *rdev)
 {
 	uint32_t status, msi_rearm;
+	bool queue_hotplug = false;
 
 	status = r100_irq_ack(rdev);
 	if (!status) {
@@ -310,13 +311,17 @@ int r100_irq_process(struct radeon_device *rdev)
 			drm_handle_vblank(rdev->ddev, 1);
 		}
 		if (status & RADEON_FP_DETECT_STAT) {
-			DRM_INFO("HPD1\n");
+			queue_hotplug = true;
+			DRM_DEBUG("HPD1\n");
 		}
 		if (status & RADEON_FP2_DETECT_STAT) {
-			DRM_INFO("HPD2\n");
+			queue_hotplug = true;
+			DRM_DEBUG("HPD2\n");
 		}
 		status = r100_irq_ack(rdev);
 	}
+	if (queue_hotplug)
+		queue_work(rdev->wq, &rdev->hotplug_work);
 	if (rdev->msi_enabled) {
 		switch (rdev->family) {
 		case CHIP_RS400:
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index c11715fb29c7..250ec3fe1a16 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -2674,6 +2674,7 @@ int r600_irq_process(struct radeon_device *rdev)
 	u32 last_entry = rdev->ih.ring_size - 16;
 	u32 ring_index, disp_int, disp_int_cont, disp_int_cont2;
 	unsigned long flags;
+	bool queue_hotplug = false;
 
 	DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr);
 
@@ -2745,37 +2746,43 @@ restart_ih:
 			case 0:
 				if (disp_int & DC_HPD1_INTERRUPT) {
 					disp_int &= ~DC_HPD1_INTERRUPT;
-					DRM_INFO("IH: HPD1\n");
+					queue_hotplug = true;
+					DRM_DEBUG("IH: HPD1\n");
 				}
 				break;
 			case 1:
 				if (disp_int & DC_HPD2_INTERRUPT) {
 					disp_int &= ~DC_HPD2_INTERRUPT;
-					DRM_INFO("IH: HPD2\n");
+					queue_hotplug = true;
+					DRM_DEBUG("IH: HPD2\n");
 				}
 				break;
 			case 4:
 				if (disp_int_cont & DC_HPD3_INTERRUPT) {
 					disp_int_cont &= ~DC_HPD3_INTERRUPT;
-					DRM_INFO("IH: HPD3\n");
+					queue_hotplug = true;
+					DRM_DEBUG("IH: HPD3\n");
 				}
 				break;
 			case 5:
 				if (disp_int_cont & DC_HPD4_INTERRUPT) {
 					disp_int_cont &= ~DC_HPD4_INTERRUPT;
-					DRM_INFO("IH: HPD4\n");
+					queue_hotplug = true;
+					DRM_DEBUG("IH: HPD4\n");
 				}
 				break;
 			case 10:
 				if (disp_int_cont2 & DC_HPD5_INTERRUPT) {
 					disp_int_cont &= ~DC_HPD5_INTERRUPT;
-					DRM_INFO("IH: HPD5\n");
+					queue_hotplug = true;
+					DRM_DEBUG("IH: HPD5\n");
 				}
 				break;
 			case 12:
 				if (disp_int_cont2 & DC_HPD6_INTERRUPT) {
 					disp_int_cont &= ~DC_HPD6_INTERRUPT;
-					DRM_INFO("IH: HPD6\n");
+					queue_hotplug = true;
+					DRM_DEBUG("IH: HPD6\n");
 				}
 				break;
 			default:
@@ -2807,6 +2814,8 @@ restart_ih:
 	wptr = r600_get_ih_wptr(rdev);
 	if (wptr != rdev->ih.wptr)
 		goto restart_ih;
+	if (queue_hotplug)
+		queue_work(rdev->wq, &rdev->hotplug_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/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 29c6e0af3755..a15cf9ceb9a7 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -809,6 +809,8 @@ struct radeon_device {
 	struct r600_blit r600_blit;
 	int msi_enabled; /* msi enabled */
 	struct r600_ih ih; /* r6/700 interrupt ring */
+	struct workqueue_struct *wq;
+	struct work_struct hotplug_work;
 };
 
 int radeon_device_init(struct radeon_device *rdev,
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 7328d1528a85..cfa2ebb259fe 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -40,6 +40,26 @@ radeon_atombios_connected_scratch_regs(struct drm_connector *connector,
 				       struct drm_encoder *encoder,
 				       bool connected);
 
+void radeon_connector_hotplug(struct drm_connector *connector)
+{
+	struct drm_device *dev = connector->dev;
+	struct radeon_device *rdev = dev->dev_private;
+	struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+
+	if (radeon_connector->hpd.hpd != RADEON_HPD_NONE)
+		radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
+
+	if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
+		if (radeon_dp_getsinktype(radeon_connector) == CONNECTOR_OBJECT_ID_DISPLAYPORT) {
+			if (radeon_dp_needs_link_train(radeon_connector)) {
+				if (connector->encoder)
+					dp_link_train(connector->encoder, connector);
+			}
+		}
+	}
+
+}
+
 static void radeon_property_change_mode(struct drm_encoder *encoder)
 {
 	struct drm_crtc *crtc = encoder->crtc;
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 60ee6a8b4f7f..7e55647f118e 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -570,6 +570,11 @@ int radeon_device_init(struct radeon_device *rdev,
 	rwlock_init(&rdev->fence_drv.lock);
 	INIT_LIST_HEAD(&rdev->gem.objects);
 
+	/* setup workqueue */
+	rdev->wq = create_workqueue("radeon");
+	if (rdev->wq == NULL)
+		return -ENOMEM;
+
 	/* Set asic functions */
 	r = radeon_asic_init(rdev);
 	if (r) {
@@ -643,6 +648,7 @@ void radeon_device_fini(struct radeon_device *rdev)
 	DRM_INFO("radeon: finishing device.\n");
 	rdev->shutdown = true;
 	radeon_fini(rdev);
+	destroy_workqueue(rdev->wq);
 	vga_client_register(rdev->pdev, NULL, NULL, NULL);
 	iounmap(rdev->rmmio);
 	rdev->rmmio = NULL;
@@ -689,6 +695,7 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state)
 	radeon_save_bios_scratch_regs(rdev);
 
 	radeon_suspend(rdev);
+	radeon_hpd_fini(rdev);
 	/* evict remaining vram memory */
 	radeon_bo_evict_vram(rdev);
 
@@ -723,6 +730,8 @@ int radeon_resume_kms(struct drm_device *dev)
 	fb_set_suspend(rdev->fbdev_info, 0);
 	release_console_sem();
 
+	/* reset hpd state */
+	radeon_hpd_init(rdev);
 	/* blat the mode back in */
 	drm_helper_resume_force_mode(dev);
 	return 0;
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index d4f4fb1c54c7..c115f2e442eb 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -741,6 +741,8 @@ int radeon_modeset_init(struct radeon_device *rdev)
 	if (!ret) {
 		return ret;
 	}
+	/* initialize hpd */
+	radeon_hpd_init(rdev);
 	drm_helper_initial_config(rdev->ddev);
 	return 0;
 }
@@ -748,6 +750,7 @@ int radeon_modeset_init(struct radeon_device *rdev)
 void radeon_modeset_fini(struct radeon_device *rdev)
 {
 	if (rdev->mode_info.mode_config_initialized) {
+		radeon_hpd_fini(rdev);
 		drm_mode_config_cleanup(rdev->ddev);
 		rdev->mode_info.mode_config_initialized = false;
 	}
diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c
index 26789970c5cf..9223296fe37b 100644
--- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
@@ -39,11 +39,32 @@ irqreturn_t radeon_driver_irq_handler_kms(DRM_IRQ_ARGS)
 	return radeon_irq_process(rdev);
 }
 
+/*
+ * Handle hotplug events outside the interrupt handler proper.
+ */
+static void radeon_hotplug_work_func(struct work_struct *work)
+{
+	struct radeon_device *rdev = container_of(work, struct radeon_device,
+						  hotplug_work);
+	struct drm_device *dev = rdev->ddev;
+	struct drm_mode_config *mode_config = &dev->mode_config;
+	struct drm_connector *connector;
+
+	if (mode_config->num_connector) {
+		list_for_each_entry(connector, &mode_config->connector_list, head)
+			radeon_connector_hotplug(connector);
+	}
+	/* Just fire off a uevent and let userspace tell us what to do */
+	drm_sysfs_hotplug_event(dev);
+}
+
 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);
+
 	/* Disable *all* interrupts */
 	rdev->irq.sw_int = false;
 	for (i = 0; i < 2; i++) {
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 61b90343f794..15ec7ca18a95 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -392,6 +392,8 @@ struct radeon_framebuffer {
 	struct drm_gem_object *obj;
 };
 
+extern void radeon_connector_hotplug(struct drm_connector *connector);
+extern bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector);
 extern int radeon_dp_mode_valid_helper(struct radeon_connector *radeon_connector,
 				       struct drm_display_mode *mode);
 extern void radeon_dp_set_link_config(struct drm_connector *connector,
diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c
index 6364ba1d4153..fd5ab01f6ad1 100644
--- a/drivers/gpu/drm/radeon/rs600.c
+++ b/drivers/gpu/drm/radeon/rs600.c
@@ -388,6 +388,7 @@ int rs600_irq_process(struct radeon_device *rdev)
 {
 	uint32_t status, msi_rearm;
 	uint32_t r500_disp_int;
+	bool queue_hotplug = false;
 
 	status = rs600_irq_ack(rdev, &r500_disp_int);
 	if (!status && !r500_disp_int) {
@@ -403,13 +404,17 @@ int rs600_irq_process(struct radeon_device *rdev)
 		if (G_007EDC_LB_D2_VBLANK_INTERRUPT(r500_disp_int))
 			drm_handle_vblank(rdev->ddev, 1);
 		if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(r500_disp_int)) {
-			DRM_INFO("HPD1\n");
+			queue_hotplug = true;
+			DRM_DEBUG("HPD1\n");
 		}
 		if (G_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(r500_disp_int)) {
-			DRM_INFO("HPD2\n");
+			queue_hotplug = true;
+			DRM_DEBUG("HPD2\n");
 		}
 		status = rs600_irq_ack(rdev, &r500_disp_int);
 	}
+	if (queue_hotplug)
+		queue_work(rdev->wq, &rdev->hotplug_work);
 	if (rdev->msi_enabled) {
 		switch (rdev->family) {
 		case CHIP_RS600: