[PATCH v2 10/16] drm/exynos: implement a drm bridge
Michael Tretter
m.tretter at pengutronix.de
Fri Sep 11 13:54:07 UTC 2020
Make the exynos_dsi driver a full drm bridge that can be found and used
from other drivers.
Other drivers can only attach to the bridge, if a mipi dsi device
already attached to the bridge. This allows to defer the probe of the
display pipe until the downstream bridges are available, too.
Signed-off-by: Michael Tretter <m.tretter at pengutronix.de>
---
v2:
- move attach of out_bridge to bridge_attach
- add bridge_detach
- don't cleanup encoder if create_connector failed
---
drivers/gpu/drm/exynos/exynos_drm_dsi.c | 103 +++++++++++++++++-------
1 file changed, 75 insertions(+), 28 deletions(-)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 2d75f9877dc0..5e7c1a99a3ee 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -266,6 +266,7 @@ struct exynos_dsi_driver_data {
struct exynos_dsi {
struct drm_encoder encoder;
+ struct drm_bridge bridge;
struct mipi_dsi_host dsi_host;
struct drm_connector connector;
struct drm_panel *panel;
@@ -1602,6 +1603,60 @@ static const struct drm_encoder_helper_funcs exynos_dsi_encoder_helper_funcs = {
.disable = exynos_dsi_disable,
};
+static int exynos_dsi_bridge_attach(struct drm_bridge *bridge,
+ enum drm_bridge_attach_flags flags)
+{
+ struct exynos_dsi *dsi = bridge->driver_private;
+ struct drm_encoder *encoder = bridge->encoder;
+ int ret;
+
+ if (!dsi->out_bridge && !dsi->panel)
+ return -EPROBE_DEFER;
+
+ if (dsi->out_bridge) {
+ ret = drm_bridge_attach(encoder, dsi->out_bridge,
+ bridge, flags);
+ if (ret)
+ return ret;
+ list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain);
+ } else {
+ ret = exynos_dsi_create_connector(encoder);
+ if (ret)
+ return ret;
+
+ if (dsi->panel) {
+ dsi->connector.status = connector_status_connected;
+ }
+ }
+
+ return 0;
+}
+
+static void exynos_dsi_bridge_detach(struct drm_bridge *bridge)
+{
+ struct exynos_dsi *dsi = bridge->driver_private;
+ struct drm_encoder *encoder = bridge->encoder;
+ struct drm_device *drm = encoder->dev;
+
+ if (dsi->panel) {
+ mutex_lock(&drm->mode_config.mutex);
+ exynos_dsi_disable(&dsi->encoder);
+ dsi->panel = NULL;
+ dsi->connector.status = connector_status_disconnected;
+ mutex_unlock(&drm->mode_config.mutex);
+ } else {
+ if (dsi->out_bridge->funcs->detach)
+ dsi->out_bridge->funcs->detach(dsi->out_bridge);
+ dsi->out_bridge = NULL;
+ INIT_LIST_HEAD(&dsi->bridge_chain);
+ }
+}
+
+static const struct drm_bridge_funcs exynos_dsi_bridge_funcs = {
+ .attach = exynos_dsi_bridge_attach,
+ .detach = exynos_dsi_bridge_detach,
+};
+
MODULE_DEVICE_TABLE(of, exynos_dsi_of_match);
static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
@@ -1609,25 +1664,12 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
{
struct exynos_dsi *dsi = host_to_dsi(host);
const struct exynos_dsi_host_ops *ops = dsi->driver_data->host_ops;
- struct drm_encoder *encoder = &dsi->encoder;
struct drm_bridge *out_bridge;
- out_bridge = of_drm_find_bridge(device->dev.of_node);
+ out_bridge = of_drm_find_bridge(device->dev.of_node);
if (out_bridge) {
- drm_bridge_attach(encoder, out_bridge, NULL, 0);
dsi->out_bridge = out_bridge;
- list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain);
} else {
- int ret = exynos_dsi_create_connector(encoder);
-
- if (ret) {
- DRM_DEV_ERROR(dsi->dev,
- "failed to create connector ret = %d\n",
- ret);
- drm_encoder_cleanup(encoder);
- return ret;
- }
-
dsi->panel = of_drm_find_panel(device->dev.of_node);
if (IS_ERR(dsi->panel))
dsi->panel = NULL;
@@ -1662,20 +1704,6 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
{
struct exynos_dsi *dsi = host_to_dsi(host);
const struct exynos_dsi_host_ops *ops = dsi->driver_data->host_ops;
- struct drm_device *drm = dsi->encoder.dev;
-
- if (dsi->panel) {
- mutex_lock(&drm->mode_config.mutex);
- exynos_dsi_disable(&dsi->encoder);
- dsi->panel = NULL;
- dsi->connector.status = connector_status_disconnected;
- mutex_unlock(&drm->mode_config.mutex);
- } else {
- if (dsi->out_bridge->funcs->detach)
- dsi->out_bridge->funcs->detach(dsi->out_bridge);
- dsi->out_bridge = NULL;
- INIT_LIST_HEAD(&dsi->bridge_chain);
- }
if (ops && ops->detach)
ops->detach(dsi->dsi_host.dev, device);
@@ -1786,7 +1814,15 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
of_node_put(in_bridge_node);
}
+ ret = drm_bridge_attach(encoder, &dsi->bridge, in_bridge, 0);
+ if (ret)
+ goto err;
+
return 0;
+
+err:
+ drm_encoder_cleanup(encoder);
+ return ret;
}
static void exynos_dsi_unbind(struct device *dev, struct device *master,
@@ -1796,6 +1832,8 @@ static void exynos_dsi_unbind(struct device *dev, struct device *master,
struct drm_encoder *encoder = &dsi->encoder;
exynos_dsi_disable(encoder);
+
+ drm_encoder_cleanup(encoder);
}
static const struct component_ops exynos_dsi_component_ops = {
@@ -1806,6 +1844,7 @@ static const struct component_ops exynos_dsi_component_ops = {
static struct exynos_dsi *__exynos_dsi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct drm_bridge *bridge;
struct resource *res;
struct exynos_dsi *dsi;
int ret, i;
@@ -1894,11 +1933,19 @@ static struct exynos_dsi *__exynos_dsi_probe(struct platform_device *pdev)
if (ret)
return ERR_PTR(ret);
+ bridge = &dsi->bridge;
+ bridge->driver_private = dsi;
+ bridge->funcs = &exynos_dsi_bridge_funcs;
+ bridge->of_node = dev->of_node;
+ drm_bridge_add(bridge);
+
return dsi;
}
static void __exynos_dsi_remove(struct exynos_dsi *dsi)
{
+ drm_bridge_remove(&dsi->bridge);
+
mipi_dsi_host_unregister(&dsi->dsi_host);
}
--
2.20.1
More information about the dri-devel
mailing list