@@ -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;
@@ -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 },
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@samsung.com> --- 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(-)