[RFC 13/15] drm/exynos/dpi: convert to restrack API

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




exynos_dpi implements drm encoder and connector. Currently its probe
was defered if the associated panel was not yet present. It is inefficient
behavior - it could start normally with connector status set to disconnected
and notify drm about status change when panel appears/disapeears.
Unfortunately drm_panel API does not provide such notifications.
restrack solves the issue above.
After converting to restrack driver have following advantages:
- correctly handles removal of resources,
- do not need to defer probing, so as a result the whole drm system
  initialization will not be postponed to late initcall, unless other
  components delays it,
- it starts/stops tracking panel presence only when necessary.

Signed-off-by: Andrzej Hajda <a.hajda@xxxxxxxxxxx>
---
 drivers/gpu/drm/exynos/exynos_drm_dpi.c  | 80 +++++++++++++++++++++++---------
 drivers/gpu/drm/exynos/exynos_drm_fimd.c |  7 +++
 2 files changed, 64 insertions(+), 23 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
index 91a29e1..75ee578 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
@@ -13,9 +13,9 @@
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_panel.h>
-
 #include <linux/of_graph.h>
 #include <linux/regulator/consumer.h>
+#include <linux/restrack.h>
 
 #include <video/of_videomode.h>
 #include <video/videomode.h>
@@ -34,7 +34,6 @@ enum {
 struct exynos_dpi {
 	struct exynos_drm_display display;
 	struct device *dev;
-	struct device_node *panel_node;
 
 	struct drm_panel *panel;
 	struct drm_connector connector;
@@ -42,10 +41,13 @@ struct exynos_dpi {
 
 	struct videomode *vm;
 	int dpms_mode;
+	struct restrack_ctx *rtrack;
 };
 
 #define connector_to_dpi(c) container_of(c, struct exynos_dpi, connector)
 
+struct exynos_drm_display *fimd_dev_to_display(struct device *dev);
+
 static inline struct exynos_dpi *display_to_dpi(struct exynos_drm_display *d)
 {
 	return container_of(d, struct exynos_dpi, display);
@@ -56,14 +58,18 @@ exynos_dpi_detect(struct drm_connector *connector, bool force)
 {
 	struct exynos_dpi *ctx = connector_to_dpi(connector);
 
-	if (ctx->panel && !ctx->panel->connector)
-		drm_panel_attach(ctx->panel, &ctx->connector);
+	if (!ctx->vm && IS_ERR(ctx->panel))
+		return connector_status_disconnected;
 
 	return connector_status_connected;
 }
 
 static void exynos_dpi_connector_destroy(struct drm_connector *connector)
 {
+	struct exynos_dpi *ctx = connector_to_dpi(connector);
+
+	if (!IS_ERR(ctx->rtrack))
+		restrack_unregister(ctx->rtrack);
 	drm_connector_unregister(connector);
 	drm_connector_cleanup(connector);
 }
@@ -94,7 +100,7 @@ static int exynos_dpi_get_modes(struct drm_connector *connector)
 		return 1;
 	}
 
-	if (ctx->panel)
+	if (!IS_ERR(ctx->panel))
 		return ctx->panel->funcs->get_modes(ctx->panel);
 
 	return 0;
@@ -113,6 +119,38 @@ static struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = {
 	.best_encoder = exynos_dpi_best_encoder,
 };
 
+static void exynos_dpi_dpms(struct exynos_drm_display *display, int mode);
+
+void exynos_dpi_notifier(struct device *dev, int ret)
+{
+	struct exynos_drm_display *display = fimd_dev_to_display(dev);
+	struct exynos_dpi *ctx = display_to_dpi(display);
+	struct drm_connector *connector = &ctx->connector;
+	struct drm_device *drm_dev = connector->dev;
+	bool poll_enabled = drm_dev->mode_config.poll_enabled;
+
+	mutex_lock(&drm_dev->mode_config.mutex);
+
+	if (ret == 0) {
+		drm_panel_attach(ctx->panel, connector);
+	} else if (ret == -EPROBE_DEFER) {
+		exynos_dpi_dpms(&ctx->display, DRM_MODE_DPMS_OFF);
+		drm_panel_detach(ctx->panel);
+		ctx->panel = ERR_PTR(-EPROBE_DEFER);
+	} else {
+		dev_err(dev, "restrack error %d\n", ret);
+		poll_enabled = false;
+	}
+
+	if (poll_enabled)
+		connector->status = exynos_dpi_detect(connector, true);
+
+	mutex_unlock(&drm_dev->mode_config.mutex);
+
+	if (poll_enabled)
+		drm_kms_helper_hotplug_event(drm_dev);
+}
+
 static int exynos_dpi_create_connector(struct exynos_drm_display *display,
 				       struct drm_encoder *encoder)
 {
@@ -136,12 +174,23 @@ static int exynos_dpi_create_connector(struct exynos_drm_display *display,
 	drm_connector_register(connector);
 	drm_mode_connector_attach_encoder(connector, encoder);
 
+	if (ctx->vm)
+		return 0;
+
+	ctx->rtrack = restrack_register(ctx->dev, exynos_dpi_notifier,
+		drm_panel_restrack_desc(&ctx->panel, NULL, FIMD_PORT_RGB)
+	);
+
+	if (IS_ERR(ctx->rtrack))
+		DRM_ERROR("error installing panel tracker: %ld\n",
+			  PTR_ERR(ctx->rtrack));
+
 	return 0;
 }
 
 static void exynos_dpi_poweron(struct exynos_dpi *ctx)
 {
-	if (ctx->panel) {
+	if (!IS_ERR(ctx->panel)) {
 		drm_panel_prepare(ctx->panel);
 		drm_panel_enable(ctx->panel);
 	}
@@ -149,7 +198,7 @@ static void exynos_dpi_poweron(struct exynos_dpi *ctx)
 
 static void exynos_dpi_poweroff(struct exynos_dpi *ctx)
 {
-	if (ctx->panel) {
+	if (!IS_ERR(ctx->panel)) {
 		drm_panel_disable(ctx->panel);
 		drm_panel_unprepare(ctx->panel);
 	}
@@ -217,10 +266,9 @@ static int exynos_dpi_parse_dt(struct exynos_dpi *ctx)
 		if (ep.port != FIMD_PORT_RGB)
 			continue;
 
-		ctx->panel_node = of_graph_get_remote_port_parent(np);
 		of_node_put(np);
 
-		return ctx->panel_node ? 0 : -EINVAL;
+		return 0;
 	}
 
 	return -EINVAL;
@@ -252,15 +300,6 @@ struct exynos_drm_display *exynos_dpi_probe(struct device *dev)
 		goto err_del_component;
 	}
 
-	if (ctx->panel_node) {
-		ctx->panel = of_drm_find_panel(ctx->panel_node);
-		if (!ctx->panel) {
-			exynos_drm_component_del(dev,
-						EXYNOS_DEVICE_TYPE_CONNECTOR);
-			return ERR_PTR(-EPROBE_DEFER);
-		}
-	}
-
 	return &ctx->display;
 
 err_del_component:
@@ -273,11 +312,6 @@ int exynos_dpi_remove(struct exynos_drm_display *display)
 {
 	struct exynos_dpi *ctx = display_to_dpi(display);
 
-	exynos_dpi_dpms(&ctx->display, DRM_MODE_DPMS_OFF);
-
-	if (ctx->panel)
-		drm_panel_detach(ctx->panel);
-
 	exynos_drm_component_del(ctx->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
 
 	return 0;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
index e5810d1..05cf4e2 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -190,6 +190,13 @@ static inline struct fimd_context *mgr_to_fimd(struct exynos_drm_manager *mgr)
 	return container_of(mgr, struct fimd_context, manager);
 }
 
+struct exynos_drm_display *fimd_dev_to_display(struct device *dev)
+{
+	struct fimd_context *ctx = dev_get_drvdata(dev);
+
+	return ctx->display;
+}
+
 static const struct of_device_id fimd_driver_dt_match[] = {
 	{ .compatible = "samsung,s3c6400-fimd",
 	  .data = &s3c64xx_fimd_driver_data },
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]
  Powered by Linux