[Intel-gfx] [PATCH] drm/i915: Add a hotplug connector property

Chris Wilson chris at chris-wilson.co.uk
Wed Jun 5 23:59:23 CEST 2013


It is useful for userspace to know when it may be able to skip a forced
detection cycle as the connector maintains an accurate status. It also
provides status feedback to the user of the hotplug storm detection,
and the ability to override the method used for detecting changes in
connection status.

Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/i915_drv.h    |    4 +-
 drivers/gpu/drm/i915/i915_irq.c    |   29 ++++++++--
 drivers/gpu/drm/i915/intel_crt.c   |    8 +++
 drivers/gpu/drm/i915/intel_dp.c    |    3 +-
 drivers/gpu/drm/i915/intel_drv.h   |    7 +++
 drivers/gpu/drm/i915/intel_dvo.c   |    1 +
 drivers/gpu/drm/i915/intel_hdmi.c  |    4 +-
 drivers/gpu/drm/i915/intel_lvds.c  |    9 ++++
 drivers/gpu/drm/i915/intel_modes.c |  104 ++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_sdvo.c  |    4 +-
 drivers/gpu/drm/i915/intel_tv.c    |    7 +--
 11 files changed, 168 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 496cd37..41e173e 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1012,7 +1012,8 @@ typedef struct drm_i915_private {
 		enum {
 			HPD_ENABLED = 0,
 			HPD_DISABLED = 1,
-			HPD_MARK_DISABLED = 2
+			HPD_MARK_DISABLED = 2,
+			HPD_USER_DISABLED = 3
 		} hpd_mark;
 	} hpd_stats[HPD_NUM_PINS];
 	u32 hpd_event_bits;
@@ -1120,6 +1121,7 @@ typedef struct drm_i915_private {
 
 	struct drm_property *broadcast_rgb_property;
 	struct drm_property *force_audio_property;
+	struct drm_property *hotplug_property;
 
 	bool hw_contexts_disabled;
 	uint32_t hw_context_size;
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 63996aa..85694d7 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -586,19 +586,25 @@ static void i915_hotplug_work_func(struct work_struct *work)
 	hpd_event_bits = dev_priv->hpd_event_bits;
 	dev_priv->hpd_event_bits = 0;
 	list_for_each_entry(connector, &mode_config->connector_list, head) {
+		int hpd_pin;
+
 		intel_connector = to_intel_connector(connector);
 		intel_encoder = intel_connector->encoder;
-		if (intel_encoder->hpd_pin > HPD_NONE &&
-		    dev_priv->hpd_stats[intel_encoder->hpd_pin].hpd_mark == HPD_MARK_DISABLED &&
+		hpd_pin = intel_encoder->hpd_pin;
+
+		if (hpd_pin > HPD_NONE &&
+		    dev_priv->hpd_stats[hpd_pin].hpd_mark == HPD_MARK_DISABLED &&
 		    connector->polled == DRM_CONNECTOR_POLL_HPD) {
 			DRM_INFO("HPD interrupt storm detected on connector %s: "
 				 "switching from hotplug detection to polling\n",
 				drm_get_connector_name(connector));
-			dev_priv->hpd_stats[intel_encoder->hpd_pin].hpd_mark = HPD_DISABLED;
-			connector->polled = DRM_CONNECTOR_POLL_CONNECT
-				| DRM_CONNECTOR_POLL_DISCONNECT;
+			dev_priv->hpd_stats[hpd_pin].hpd_mark = HPD_DISABLED;
+			connector->polled =
+				DRM_CONNECTOR_POLL_CONNECT |
+				DRM_CONNECTOR_POLL_DISCONNECT;
 			hpd_disabled = true;
 		}
+
 		if (hpd_event_bits & (1 << intel_encoder->hpd_pin)) {
 			DRM_DEBUG_KMS("Connector %s (pin %i) received hotplug event.\n",
 				      drm_get_connector_name(connector), intel_encoder->hpd_pin);
@@ -625,6 +631,10 @@ static void i915_hotplug_work_func(struct work_struct *work)
 				changed = true;
 		}
 	}
+
+	if (hpd_disabled)
+		intel_update_hotplug_property(dev);
+
 	mutex_unlock(&mode_config->mutex);
 
 	if (changed)
@@ -3479,6 +3489,7 @@ static void i915_reenable_hotplug_timer_func(unsigned long data)
 	struct drm_device *dev = dev_priv->dev;
 	struct drm_mode_config *mode_config = &dev->mode_config;
 	unsigned long irqflags;
+	bool update = false;
 	int i;
 
 	spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
@@ -3489,6 +3500,7 @@ static void i915_reenable_hotplug_timer_func(unsigned long data)
 			continue;
 
 		dev_priv->hpd_stats[i].hpd_mark = HPD_ENABLED;
+		update = true;
 
 		list_for_each_entry(connector, &mode_config->connector_list, head) {
 			struct intel_connector *intel_connector = to_intel_connector(connector);
@@ -3506,6 +3518,12 @@ static void i915_reenable_hotplug_timer_func(unsigned long data)
 	if (dev_priv->display.hpd_irq_setup)
 		dev_priv->display.hpd_irq_setup(dev);
 	spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+
+	if (update) {
+		mutex_lock(&dev->mode_config.mutex);
+		intel_update_hotplug_property(dev);
+		mutex_unlock(&dev->mode_config.mutex);
+	}
 }
 
 void intel_irq_init(struct drm_device *dev)
@@ -3606,4 +3624,5 @@ void intel_hpd_init(struct drm_device *dev)
 	}
 	if (dev_priv->display.hpd_irq_setup)
 		dev_priv->display.hpd_irq_setup(dev);
+	intel_update_hotplug_property(dev);
 }
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index 3acec8c..fb7fae3 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -679,6 +679,13 @@ static int intel_crt_set_property(struct drm_connector *connector,
 				  struct drm_property *property,
 				  uint64_t value)
 {
+	int ret;
+
+	ret = intel_connector_set_property(to_intel_connector(connector),
+					   property, value);
+	if (ret)
+		return ret;
+
 	return 0;
 }
 
@@ -818,6 +825,7 @@ void intel_crt_init(struct drm_device *dev)
 
 	if (!I915_HAS_HOTPLUG(dev))
 		intel_connector->polled = DRM_CONNECTOR_POLL_CONNECT;
+	intel_connector_attach_hotplug_property(intel_connector);
 
 	/*
 	 * Configure the automatic hotplug detection stuff
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 759a1c5..79b4568 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -2603,7 +2603,7 @@ intel_dp_set_property(struct drm_connector *connector,
 	struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base);
 	int ret;
 
-	ret = drm_object_property_set_value(&connector->base, property, val);
+	ret = intel_connector_set_property(intel_connector, property, val);
 	if (ret)
 		return ret;
 
@@ -3191,4 +3191,5 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
 	intel_encoder->hot_plug = intel_dp_hot_plug;
 
 	intel_dp_init_connector(intel_dig_port, intel_connector);
+	intel_connector_attach_hotplug_property(intel_connector);
 }
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 2f87652..1f04b7f 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -545,9 +545,16 @@ int intel_connector_update_modes(struct drm_connector *connector,
 				struct edid *edid);
 int intel_ddc_get_modes(struct drm_connector *c, struct i2c_adapter *adapter);
 
+extern void intel_connector_attach_hotplug_property(struct intel_connector *connector);
 extern void intel_attach_force_audio_property(struct drm_connector *connector);
 extern void intel_attach_broadcast_rgb_property(struct drm_connector *connector);
 
+extern int intel_connector_set_property(struct intel_connector *connnector,
+					struct drm_property *property,
+					uint64_t value);
+
+extern void intel_update_hotplug_property(struct drm_device *dev);
+
 extern bool intel_pipe_has_type(struct drm_crtc *crtc, int type);
 extern void intel_crt_init(struct drm_device *dev);
 extern void intel_hdmi_init(struct drm_device *dev,
diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c
index eb2020e..2aecae0 100644
--- a/drivers/gpu/drm/i915/intel_dvo.c
+++ b/drivers/gpu/drm/i915/intel_dvo.c
@@ -554,6 +554,7 @@ void intel_dvo_init(struct drm_device *dev)
 			intel_dvo->panel_wants_dither = true;
 		}
 
+		intel_connector_attach_hotplug_property(intel_connector);
 		drm_sysfs_connector_add(connector);
 		return;
 	}
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index bc12518..6fe4476 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -941,7 +941,8 @@ intel_hdmi_set_property(struct drm_connector *connector,
 	struct drm_i915_private *dev_priv = connector->dev->dev_private;
 	int ret;
 
-	ret = drm_object_property_set_value(&connector->base, property, val);
+	ret = intel_connector_set_property(to_intel_connector(connector),
+					   property, val);
 	if (ret)
 		return ret;
 
@@ -1248,4 +1249,5 @@ void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port)
 	intel_dig_port->dp.output_reg = 0;
 
 	intel_hdmi_init_connector(intel_dig_port, intel_connector);
+	intel_connector_attach_hotplug_property(intel_connector);
 }
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index 10c3d56..90b6870 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -465,6 +465,11 @@ static int intel_lvds_set_property(struct drm_connector *connector,
 {
 	struct intel_connector *intel_connector = to_intel_connector(connector);
 	struct drm_device *dev = connector->dev;
+	int ret;
+
+	ret = intel_connector_set_property(intel_connector, property, value);
+	if (ret)
+		return ret;
 
 	if (property == dev->mode_config.scaling_mode_property) {
 		struct drm_crtc *crtc;
@@ -1089,6 +1094,10 @@ out:
 		DRM_DEBUG_KMS("lid notifier registration failed\n");
 		lvds_connector->lid_notifier.notifier_call = NULL;
 	}
+
+	intel_connector->polled = DRM_CONNECTOR_POLL_HPD;
+	intel_connector_attach_hotplug_property(intel_connector);
+
 	drm_sysfs_connector_add(connector);
 
 	intel_panel_init(&intel_connector->panel, fixed_mode);
diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c
index 0e860f3..34de1c1 100644
--- a/drivers/gpu/drm/i915/intel_modes.c
+++ b/drivers/gpu/drm/i915/intel_modes.c
@@ -126,3 +126,107 @@ intel_attach_broadcast_rgb_property(struct drm_connector *connector)
 
 	drm_object_attach_property(&connector->base, prop, 0);
 }
+
+static const struct drm_prop_enum_list hotplug_names[] = {
+	{ -1, "polled" },
+	{ 0, "disabled" },
+	{ 1, "hardware" },
+};
+
+static uint64_t polled_to_hotplug(struct intel_connector *connector)
+{
+	uint64_t value;
+
+	if (connector->base.polled == 0)
+		value = 0;
+	else if (connector->base.polled == DRM_CONNECTOR_POLL_HPD)
+		value = 1;
+	else
+		value = -1;
+
+	return value;
+}
+
+void intel_update_hotplug_property(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct drm_connector *connector;
+
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+		drm_object_property_set_value(&connector->base,
+					      dev_priv->hotplug_property,
+					      polled_to_hotplug(to_intel_connector(connector)));
+}
+
+void intel_connector_attach_hotplug_property(struct intel_connector *connector)
+{
+	struct drm_i915_private *dev_priv = connector->base.dev->dev_private;
+	struct drm_property *prop;
+
+	prop = dev_priv->hotplug_property;
+	if (prop == NULL) {
+		prop = drm_property_create_enum(connector->base.dev,
+						DRM_MODE_PROP_ENUM,
+						"hotplug",
+						hotplug_names,
+						ARRAY_SIZE(hotplug_names));
+		if (prop == NULL)
+			return;
+
+		dev_priv->hotplug_property = prop;
+	}
+	drm_object_attach_property(&connector->base.base, prop,
+				   polled_to_hotplug(connector));
+}
+
+int intel_connector_set_property(struct intel_connector *connector,
+				 struct drm_property *property,
+				 uint64_t value)
+{
+	struct drm_device *dev = connector->base.dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	int ret;
+
+	ret = drm_object_property_set_value(&connector->base.base, property, value);
+	if (ret)
+		return ret;
+
+	if (property == dev_priv->hotplug_property) {
+		unsigned long irqflags;
+		int hpd_pin = intel_attached_encoder(&connector->base)->hpd_pin;
+		bool has_hpd = hpd_pin != HPD_NONE;
+		int v = (int)value;
+
+		spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+		if (v == 0) {
+			if (has_hpd)
+				dev_priv->hpd_stats[hpd_pin].hpd_mark = HPD_USER_DISABLED;
+			connector->base.polled = 0;
+		} else {
+			if (!has_hpd)
+				v = -1;
+			if (v < 0) {
+				if (has_hpd)
+					dev_priv->hpd_stats[hpd_pin].hpd_mark = HPD_USER_DISABLED;
+				connector->base.polled =
+					DRM_CONNECTOR_POLL_CONNECT |
+					DRM_CONNECTOR_POLL_DISCONNECT;
+			} else {
+				connector->base.polled = connector->polled;
+				if (!connector->base.polled)
+					connector->base.polled = DRM_CONNECTOR_POLL_HPD;
+				if (connector->base.polled == DRM_CONNECTOR_POLL_HPD)
+					dev_priv->hpd_stats[hpd_pin].hpd_mark = HPD_ENABLED;
+				if (dev_priv->display.hpd_irq_setup)
+					dev_priv->display.hpd_irq_setup(dev);
+			}
+		}
+		spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+
+		intel_update_hotplug_property(dev);
+		drm_kms_helper_poll_enable(dev);
+		return 1;
+	}
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index 7068195..00487bf 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -1976,7 +1976,8 @@ intel_sdvo_set_property(struct drm_connector *connector,
 	uint8_t cmd;
 	int ret;
 
-	ret = drm_object_property_set_value(&connector->base, property, val);
+	ret = intel_connector_set_property(to_intel_connector(connector),
+					   property, val);
 	if (ret)
 		return ret;
 
@@ -2313,6 +2314,7 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
 	connector->base.get_hw_state = intel_sdvo_connector_get_hw_state;
 
 	intel_connector_attach_encoder(&connector->base, &encoder->base);
+	intel_connector_attach_hotplug_property(&connector->base);
 	drm_sysfs_connector_add(&connector->base.base);
 }
 
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
index 39debd8..b71505c 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -1442,9 +1442,10 @@ intel_tv_set_property(struct drm_connector *connector, struct drm_property *prop
 	int ret = 0;
 	bool changed = false;
 
-	ret = drm_object_property_set_value(&connector->base, property, val);
-	if (ret < 0)
-		goto out;
+	ret = intel_connector_set_property(to_intel_connector(connector),
+					   property, val);
+	if (ret)
+		return ret;
 
 	if (property == dev->mode_config.tv_left_margin_property &&
 		intel_tv->margin[TV_MARGIN_LEFT] != val) {
-- 
1.7.10.4




More information about the Intel-gfx mailing list