[PATCH] drm: don't loose HPD events

Rob Clark robdclark at gmail.com
Tue Jan 20 10:03:51 PST 2015


Since drm_helper_probe_single_connector_modes_merge_bits() and other
places also update connector->status, the output_poll_execute() and/or
drm_helper_hpd_irq_event() logic can get confused and forget to send
a HPD event.

There are two possible solutions: (1) keep track of state at last HPD
check separately, or (2) send events in more places.  The latter
approach isn't so convenient to avoid per-connector HPD events (in
case multiple connectors change connection state at the same time) so
I went for the first option.

Signed-off-by: Rob Clark <robdclark at gmail.com>
---
 drivers/gpu/drm/drm_crtc.c         | 1 +
 drivers/gpu/drm/drm_probe_helper.c | 9 ++++++---
 include/drm/drm_crtc.h             | 9 +++++++++
 3 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 7c1786d..615edee 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -865,6 +865,7 @@ int drm_connector_init(struct drm_device *dev,
 	INIT_LIST_HEAD(&connector->modes);
 	connector->edid_blob_ptr = NULL;
 	connector->status = connector_status_unknown;
+	connector->hpd_status = connector_status_unknown;
 
 	drm_connector_get_cmdline_mode(connector);
 
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index 2fbdcca..c227285 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -293,14 +293,16 @@ static void output_poll_execute(struct work_struct *work)
 
 		repoll = true;
 
-		old_status = connector->status;
 		/* if we are connected and don't want to poll for disconnect
 		   skip it */
-		if (old_status == connector_status_connected &&
+		if (connector->status == connector_status_connected &&
 		    !(connector->polled & DRM_CONNECTOR_POLL_DISCONNECT))
 			continue;
 
+		old_status = connector->hpd_status;
+
 		connector->status = connector->funcs->detect(connector, false);
+		connector->hpd_status = connector->status;
 		if (old_status != connector->status) {
 			const char *old, *new;
 
@@ -450,9 +452,10 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev)
 		if (!(connector->polled & DRM_CONNECTOR_POLL_HPD))
 			continue;
 
-		old_status = connector->status;
+		old_status = connector->hpd_status;
 
 		connector->status = connector->funcs->detect(connector, false);
+		connector->hpd_status = connector->status;
 		DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n",
 			      connector->base.id,
 			      connector->name,
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index f444263..cb1899b 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -619,6 +619,7 @@ struct drm_encoder {
  * @stereo_allowed: can this connector handle stereo modes?
  * @modes: modes available on this connector (from fill_modes() + user)
  * @status: one of the drm_connector_status enums (connected, not, or unknown)
+ * @hpd_status: status as of last hpd/poll
  * @probed_modes: list of modes derived directly from the display
  * @display_info: information about attached display (e.g. from EDID)
  * @funcs: connector control functions
@@ -676,6 +677,14 @@ struct drm_connector {
 
 	enum drm_connector_status status;
 
+	/* since connector->status can change in various other places
+	 * (other than hotplug poll/irq), we need to separately keep
+	 * track of the connection status at last should-I-send-an-
+	 * hpd-event check, to avoid forgetting to send an hpd event
+	 * to userspace
+	 */
+	enum drm_connector_status hpd_status;
+
 	/* these are modes added by probing with DDC or the BIOS */
 	struct list_head probed_modes;
 
-- 
2.1.0



More information about the dri-devel mailing list