drm/omap: Don't call HPD registration operations recursively

Instead of calling the hot-plug detection callback registration
operations (.register_hpd_cb() and .unregister_hpd_cb()) recursively
from the display device back to the first device that provides hot plug
detection support, iterate over the devices manually in the DRM
connector code. This moves the complexity to a single central location
and simplifies the logic in omap_dss_device drivers.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.co.uk>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c
index 4729af3..05734c9 100644
--- a/drivers/gpu/drm/omapdrm/omap_connector.c
+++ b/drivers/gpu/drm/omapdrm/omap_connector.c
@@ -57,6 +57,21 @@
 	return omap_connector->hdmi_mode;
 }
 
+static struct omap_dss_device *
+omap_connector_find_device(struct drm_connector *connector,
+			   enum omap_dss_device_ops_flag op)
+{
+	struct omap_connector *omap_connector = to_omap_connector(connector);
+	struct omap_dss_device *dssdev;
+
+	for (dssdev = omap_connector->dssdev; dssdev; dssdev = dssdev->src) {
+		if (dssdev->ops_flags & op)
+			return dssdev;
+	}
+
+	return NULL;
+}
+
 static enum drm_connector_status omap_connector_detect(
 		struct drm_connector *connector, bool force)
 {
@@ -64,10 +79,8 @@
 	struct omap_dss_device *dssdev;
 	enum drm_connector_status status;
 
-	for (dssdev = omap_connector->dssdev; dssdev; dssdev = dssdev->src) {
-		if (dssdev->ops_flags & OMAP_DSS_DEVICE_OP_DETECT)
-			break;
-	}
+	dssdev = omap_connector_find_device(connector,
+					    OMAP_DSS_DEVICE_OP_DETECT);
 
 	if (dssdev) {
 		if (dssdev->ops->detect(dssdev))
@@ -96,18 +109,21 @@
 static void omap_connector_destroy(struct drm_connector *connector)
 {
 	struct omap_connector *omap_connector = to_omap_connector(connector);
-	struct omap_dss_device *dssdev = omap_connector->dssdev;
+	struct omap_dss_device *dssdev;
 
 	DBG("%s", omap_connector->dssdev->name);
-	if (connector->polled == DRM_CONNECTOR_POLL_HPD &&
-	    dssdev->ops->unregister_hpd_cb) {
+
+	if (connector->polled == DRM_CONNECTOR_POLL_HPD) {
+		dssdev = omap_connector_find_device(connector,
+						    OMAP_DSS_DEVICE_OP_HPD);
 		dssdev->ops->unregister_hpd_cb(dssdev);
 	}
+
 	drm_connector_unregister(connector);
 	drm_connector_cleanup(connector);
 	kfree(omap_connector);
 
-	omapdss_device_put(dssdev);
+	omapdss_device_put(omap_connector->dssdev);
 }
 
 #define MAX_EDID  512
@@ -257,45 +273,50 @@
 {
 	struct drm_connector *connector = NULL;
 	struct omap_connector *omap_connector;
-	bool hpd_supported = false;
 
 	DBG("%s", dssdev->name);
 
-	omapdss_device_get(dssdev);
-
 	omap_connector = kzalloc(sizeof(*omap_connector), GFP_KERNEL);
 	if (!omap_connector)
 		goto fail;
 
-	omap_connector->dssdev = dssdev;
+	omap_connector->dssdev = omapdss_device_get(dssdev);
 
 	connector = &omap_connector->base;
+	connector->interlace_allowed = 1;
+	connector->doublescan_allowed = 0;
 
 	drm_connector_init(dev, connector, &omap_connector_funcs,
 				connector_type);
 	drm_connector_helper_add(connector, &omap_connector_helper_funcs);
 
-	if (dssdev->ops->register_hpd_cb) {
-		int ret = dssdev->ops->register_hpd_cb(dssdev,
-						       omap_connector_hpd_cb,
-						       omap_connector);
-		if (!ret)
-			hpd_supported = true;
-		else if (ret != -ENOTSUPP)
+	/*
+	 * Initialize connector status handling. First try to find a device that
+	 * supports hot-plug reporting. If it fails, fall back to a device that
+	 * support polling. If that fails too, we don't support hot-plug
+	 * detection at all.
+	 */
+	dssdev = omap_connector_find_device(connector, OMAP_DSS_DEVICE_OP_HPD);
+	if (dssdev) {
+		int ret;
+
+		ret = dssdev->ops->register_hpd_cb(dssdev,
+						   omap_connector_hpd_cb,
+						   omap_connector);
+		if (ret < 0)
 			DBG("%s: Failed to register HPD callback (%d).",
 			    dssdev->name, ret);
+		else
+			connector->polled = DRM_CONNECTOR_POLL_HPD;
 	}
 
-	if (hpd_supported)
-		connector->polled = DRM_CONNECTOR_POLL_HPD;
-	else if (dssdev->ops->detect)
-		connector->polled = DRM_CONNECTOR_POLL_CONNECT |
-				    DRM_CONNECTOR_POLL_DISCONNECT;
-	else
-		connector->polled = 0;
-
-	connector->interlace_allowed = 1;
-	connector->doublescan_allowed = 0;
+	if (!connector->polled) {
+		dssdev = omap_connector_find_device(connector,
+						    OMAP_DSS_DEVICE_OP_DETECT);
+		if (dssdev)
+			connector->polled = DRM_CONNECTOR_POLL_CONNECT |
+					    DRM_CONNECTOR_POLL_DISCONNECT;
+	}
 
 	return connector;