[Intel-gfx] [PATCH 2/2] drm/i915: Lane count change detection

Shubhangi Shrivastava shubhangi.shrivastava at intel.com
Fri May 13 06:23:43 UTC 2016


DP panel can issue short pulse interrupts during which it can
modify the number of lanes supported by it. This will result in
change in capabilities of the display and limit the max
resoultion possible on it. This should be informed to the
user mode as well since HWC will issue the modeset assuming
old capabilities.

This patch detects lane count change and simulates a detatch
and attach, so to the user mode or anyone else tracking the
display it will appear as unplug and replug of different
display.

v2: moved queuing of delayed thread to intel_dp where we
set simulate_disconnect_connect flag. There is a chance
for short pulse to be hit even before long pulse detection
is complete, in such a scenario the delayed thread won't
be queued resulting in flag never getting cleared.
Since we set the flag and queue it immediately we can be
sure that the flag will be cleared.

Signed-off-by: Sivakumar Thulasimani <sivakumar.thulasimani at intel.com>
Signed-off-by: Shubhangi Shrivastava <shubhangi.shrivastava at intel.com>
---
 drivers/gpu/drm/i915/i915_drv.h      |  1 +
 drivers/gpu/drm/i915/i915_irq.c      |  2 ++
 drivers/gpu/drm/i915/intel_dp.c      | 47 ++++++++++++++++++++++++++++++---
 drivers/gpu/drm/i915/intel_drv.h     |  4 +++
 drivers/gpu/drm/i915/intel_hotplug.c | 50 ++++++++++++++++++++++++++++++++++++
 5 files changed, 100 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 7a0b513..30d3636 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -280,6 +280,7 @@ struct i915_hotplug {
 	u32 long_port_mask;
 	u32 short_port_mask;
 	struct work_struct dig_port_work;
+	struct delayed_work simulate_work;
 
 	/*
 	 * if we get a HPD irq from DP and a HPD irq from non-DP
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index f0d9414..74fe755 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -4572,6 +4572,8 @@ void intel_irq_init(struct drm_i915_private *dev_priv)
 
 	INIT_WORK(&dev_priv->rps.work, gen6_pm_rps_work);
 	INIT_WORK(&dev_priv->l3_parity.error_work, ivybridge_parity_work);
+	INIT_DELAYED_WORK(&dev_priv->hotplug.simulate_work,
+			  intel_hpd_simulate_reconnect_work);
 
 	/* Let's track the enabled rps events */
 	if (IS_VALLEYVIEW(dev_priv))
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index d5ed84f..ebd525e 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -3783,6 +3783,26 @@ update_status:
 	}
 }
 
+static void intel_dp_update_simulate_detach_info(struct intel_dp *intel_dp)
+{
+	struct intel_connector *intel_connector = intel_dp->attached_connector;
+	struct drm_device *dev = intel_dp_to_dev(intel_dp);
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	/*
+	 * Queue thread only if it is not already done.
+	 * The flag is cleared inside the callback function
+	 * hence we can be sure that there is no race condition
+	 * under which it may remain set.
+	 */
+	if (!intel_connector->simulate_disconnect_connect) {
+		intel_connector->simulate_disconnect_connect = true;
+		DRM_DEBUG_KMS("Queue simulate work func\n");
+		mod_delayed_work(system_wq, &dev_priv->hotplug.simulate_work,
+				 msecs_to_jiffies(100));
+	}
+}
+
 static int
 intel_dp_check_mst_status(struct intel_dp *intel_dp)
 {
@@ -3893,6 +3913,7 @@ intel_dp_short_pulse(struct intel_dp *intel_dp)
 	struct drm_device *dev = intel_dp_to_dev(intel_dp);
 	u8 sink_irq_vector;
 	u8 old_sink_count = intel_dp->sink_count;
+	u8 old_lane_count = intel_dp->dpcd[DP_MAX_LANE_COUNT];
 	bool ret;
 
 	/*
@@ -3924,6 +3945,20 @@ intel_dp_short_pulse(struct intel_dp *intel_dp)
 			sink_irq_vector &= ~DP_AUTOMATED_TEST_REQUEST;
 		}
 
+		/*
+		 * If lane count has changed, we need to inform
+		 * user mode of new capablities, this is done by setting
+		 * our flag to do a fake disconnect and connect so it
+		 * will appear to the user mode that a new panel is
+		 * connected and will use the new capabilties of the
+		 * panel
+		 */
+		if (old_lane_count != intel_dp->dpcd[DP_MAX_LANE_COUNT]) {
+			DRM_DEBUG_KMS("Lane count changed\n");
+			intel_dp_update_simulate_detach_info(intel_dp);
+			return false;
+		}
+
 		if (sink_irq_vector & (DP_CP_IRQ | DP_SINK_SPECIFIC_IRQ))
 			DRM_DEBUG_DRIVER("CP or sink specific irq unhandled\n");
 
@@ -4213,13 +4248,17 @@ intel_dp_long_pulse(struct intel_connector *intel_connector)
 	intel_display_power_get(to_i915(dev), power_domain);
 
 	/* Can't disconnect eDP, but you can close the lid... */
-	if (is_edp(intel_dp))
+	if (is_edp(intel_dp)) {
 		status = edp_detect(intel_dp);
-	else if (intel_digital_port_connected(to_i915(dev),
-					      dp_to_dig_port(intel_dp)))
+	} else if (intel_connector->simulate_disconnect_connect) {
+		DRM_DEBUG_KMS("Simulating disconnect\n");
+		status = connector_status_disconnected;
+	} else if (intel_digital_port_connected(to_i915(dev),
+					      dp_to_dig_port(intel_dp))) {
 		status = intel_dp_detect_dpcd(intel_dp);
-	else
+	} else {
 		status = connector_status_disconnected;
+	}
 
 	if (status != connector_status_connected) {
 		intel_dp->compliance_test_active = 0;
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 8daadc5..995f0da 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -257,6 +257,8 @@ struct intel_connector {
 	struct edid *edid;
 	struct edid *detect_edid;
 
+	bool simulate_disconnect_connect;
+
 	/* 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;
@@ -1420,6 +1422,8 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
 			       struct intel_crtc_state *pipe_config);
 void intel_dp_dual_mode_set_tmds_output(struct intel_hdmi *hdmi, bool enable);
 
+/* intel_hotplug.c */
+void intel_hpd_simulate_reconnect_work(struct work_struct *work);
 
 /* intel_lvds.c */
 void intel_lvds_init(struct drm_device *dev);
diff --git a/drivers/gpu/drm/i915/intel_hotplug.c b/drivers/gpu/drm/i915/intel_hotplug.c
index 38eeca7..7e346d7 100644
--- a/drivers/gpu/drm/i915/intel_hotplug.c
+++ b/drivers/gpu/drm/i915/intel_hotplug.c
@@ -247,6 +247,55 @@ static bool intel_hpd_irq_event(struct drm_device *dev,
 	return true;
 }
 
+/*
+ * This function is the second half of logic to perform fake
+ * disconnect-connect. The first half was the connector setting
+ * a flag, returning status as disconnected and queing this function.
+ *
+ * This function uses simulate_disconnect_connect flag to identify the
+ * connector that should be detected again. Since this is executed after
+ * a delay if the panel is still plugged in it will be reported as
+ * connected to user mode
+ */
+void intel_hpd_simulate_reconnect_work(struct work_struct *work)
+{
+	struct drm_i915_private *dev_priv =
+		container_of(work, struct drm_i915_private,
+			     hotplug.simulate_work.work);
+	struct drm_device *dev = dev_priv->dev;
+	struct intel_encoder *intel_encoder;
+	struct intel_connector *intel_connector;
+	struct drm_connector *connector;
+	struct drm_mode_config *mode_config = &dev->mode_config;
+	enum port port;
+
+	DRM_DEBUG_KMS("\n");
+	mutex_lock(&mode_config->mutex);
+
+	list_for_each_entry(connector, &mode_config->connector_list, head) {
+		intel_connector = to_intel_connector(connector);
+		if (!intel_connector->simulate_disconnect_connect)
+			continue;
+
+		intel_connector->simulate_disconnect_connect = false;
+
+		if (!intel_connector->encoder)
+			continue;
+
+		intel_encoder = intel_connector->encoder;
+
+		port = intel_ddi_get_encoder_port(intel_encoder);
+
+		spin_lock_irq(&dev_priv->irq_lock);
+		dev_priv->hotplug.long_port_mask |= (1<<port);
+		spin_unlock_irq(&dev_priv->irq_lock);
+	}
+
+	mutex_unlock(&mode_config->mutex);
+
+	queue_work(dev_priv->hotplug.dp_wq, &dev_priv->hotplug.dig_port_work);
+}
+
 static void i915_digport_work_func(struct work_struct *work)
 {
 	struct drm_i915_private *dev_priv =
@@ -509,4 +558,5 @@ void intel_hpd_cancel_work(struct drm_i915_private *dev_priv)
 	cancel_work_sync(&dev_priv->hotplug.dig_port_work);
 	cancel_work_sync(&dev_priv->hotplug.hotplug_work);
 	cancel_delayed_work_sync(&dev_priv->hotplug.reenable_work);
+	cancel_delayed_work_sync(&dev_priv->hotplug.simulate_work);
 }
-- 
2.6.1



More information about the Intel-gfx mailing list