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 linux-samsung-soc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html