[PATCH 2/2] drm/i915: Detect monitor change from EDID change after resume

Paul Kocialkowski paul.kocialkowski at linux.intel.com
Mon Jul 24 14:54:47 UTC 2017


This introduces a dedicated work and related helpers to detect whether
a monitor was replaced by another one during suspend. This detection is
based on EDID change detection, using the associated generic drm helper.

This requires storing more information to the intel_connector structure,
such as the i2c adapter required to get the EDID.

Support for this is introduced for DP, HDMI and VGA.

Signed-off-by: Paul Kocialkowski <paul.kocialkowski at linux.intel.com>
---
 drivers/gpu/drm/i915/i915_drv.c      |  2 ++
 drivers/gpu/drm/i915/i915_drv.h      |  5 ++++
 drivers/gpu/drm/i915/intel_crt.c     |  3 +++
 drivers/gpu/drm/i915/intel_display.c | 50 ++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_dp.c      |  3 +++
 drivers/gpu/drm/i915/intel_drv.h     |  6 +++++
 drivers/gpu/drm/i915/intel_hdmi.c    |  3 +++
 7 files changed, 72 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 3ac8215c0e36..b3cf4112fc65 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -1702,6 +1702,8 @@ static int i915_drm_resume(struct drm_device *dev)
 
 	intel_display_resume(dev);
 
+	intel_edid_changes_detect(dev);
+
 	drm_kms_helper_poll_enable(dev);
 
 	/*
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index b051122c960b..2c189a5b714e 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -3904,6 +3904,11 @@ intel_display_capture_error_state(struct drm_i915_private *dev_priv);
 extern void intel_display_print_error_state(struct drm_i915_error_state_buf *e,
 					    struct intel_display_error_state *error);
 
+/* edid change */
+void intel_edid_change_init(struct intel_connector *connector,
+			    struct i2c_adapter *adapter);
+void intel_edid_changes_detect(struct drm_device *dev);
+
 int sandybridge_pcode_read(struct drm_i915_private *dev_priv, u32 mbox, u32 *val);
 int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u32 mbox, u32 val);
 int skl_pcode_request(struct drm_i915_private *dev_priv, u32 mbox, u32 request,
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index 84a1f5e85153..63e2184a6ea4 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -925,6 +925,9 @@ void intel_crt_init(struct drm_i915_private *dev_priv)
 	 */
 	crt->force_hotplug_required = 0;
 
+	intel_edid_change_init(intel_connector,
+			       intel_gmbus_get_adapter(dev_priv, dev_priv->vbt.crt_ddc_pin));
+
 	/*
 	 * TODO: find a proper way to discover whether we need to set the the
 	 * polarity and link reversal bits or not, instead of relying on the
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 6c823cc02db3..69e306759a02 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -15016,3 +15016,53 @@ intel_display_print_error_state(struct drm_i915_error_state_buf *m,
 }
 
 #endif
+
+static void intel_edid_change_work_func(struct work_struct *work)
+{
+	struct intel_connector *connector =
+		container_of(work, struct intel_connector, edid_change_work);
+	bool changed;
+
+	changed = drm_check_edid_changed(&connector->base, connector->adapter);
+	if (changed) {
+		DRM_DEBUG_KMS("[CONNECTOR:%d:%s] EDID change detected\n",
+			      connector->base.base.id, connector->base.name);
+
+		drm_kms_helper_hotplug_event(connector->base.dev);
+	}
+}
+
+/**
+ * intel_edid_change_init - init the EDID change data
+ * @connector: DRM connector device to use
+ * @adapter: i2c adapter
+ *
+ * Store the i2c adapter and initialize the EDID change detection work.
+ */
+void intel_edid_change_init(struct intel_connector *connector,
+			    struct i2c_adapter *adapter)
+{
+	connector->adapter = adapter;
+
+	INIT_WORK(&connector->edid_change_work, intel_edid_change_work_func);
+}
+
+/**
+ * intel_edid_changes_detect - detect EDID changes to connectors
+ * @dev: DRM device to get connectors from
+ *
+ * Schedule the EDID detection change work for all registered connectors.
+ */
+void intel_edid_changes_detect(struct drm_device *dev)
+{
+	struct intel_connector *connector;
+	struct drm_connector_list_iter conn_iter;
+
+	drm_connector_list_iter_begin(dev, &conn_iter);
+	for_each_intel_connector_iter(connector, &conn_iter) {
+		if (!connector->adapter)
+			continue;
+
+		schedule_work(&connector->edid_change_work);
+	}
+}
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 2d42d09428c9..bc4dbb076096 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -6063,6 +6063,9 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
 		goto fail;
 	}
 
+	if (!is_edp(intel_dp))
+		intel_edid_change_init(intel_connector, &intel_dp->aux.ddc);
+
 	intel_dp_add_properties(intel_dp, connector);
 
 	/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 021cc5487853..47d350d2f993 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -319,6 +319,12 @@ struct intel_connector {
 	struct edid *edid;
 	struct edid *detect_edid;
 
+	/* I2C adapter to retrieve the EDID. */
+	struct i2c_adapter *adapter;
+
+	/* Work to detect EDID change. */
+	struct work_struct edid_change_work;
+
 	/* since POLL and HPD connectors may use the same HPD line keep the native
 	   state of connector->polled in case hotplug storm detection changes it */
 	u8 polled;
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 2f831cfdd243..a6ce77f81668 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -1909,6 +1909,9 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
 	else
 		intel_connector->get_hw_state = intel_connector_get_hw_state;
 
+	intel_edid_change_init(intel_connector,
+			       intel_gmbus_get_adapter(dev_priv, intel_hdmi->ddc_bus));
+
 	intel_hdmi_add_properties(intel_hdmi, connector);
 
 	intel_connector_attach_encoder(intel_connector, intel_encoder);
-- 
2.13.2



More information about the dri-devel mailing list