dri-devel.lists.freedesktop.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 00/16]  drm/exynos: Convert driver to drm bridge
@ 2020-09-11 13:53 ` Michael Tretter
  2020-09-11 13:53   ` [PATCH v2 01/16] drm/encoder: remove obsolete documentation of bridge Michael Tretter
                     ` (16 more replies)
  0 siblings, 17 replies; 61+ messages in thread
From: Michael Tretter @ 2020-09-11 13:53 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: jy0922.shim, b.zolnierkie, narmstrong, sw0312.kim,
	Michael Tretter, krzk, a.hajda, Laurent.pinchart, kernel,
	sylvester.nawrocki

This is v2 of the series to convert the Exynos MIPI DSI driver into a drm
bridge and make it usable with other drivers. Although the driver is
converted, it still supports the component framework API to stay compliant
with the Exynos DRM driver.

The Exynos MIPI DSI Phy is also found on the i.MX8M Mini. However, on the
i.MX8M Mini, the bridge is driven by an LCDIF display controller instead of
the Exynos Decon. The driver for the LCDIF does not use the component
framework, but uses drm bridges.

I don't have any Exynos SoC to actually test the series. I build a dummy to
test the bridge with a component driver, to make sure that at least the
initialization is working. Furthermore, tested the driver as a bridge with a
few additional unfinished patches on the i.MX8M Mini EVK. However, somebody
should verify that the driver is still working on Exynos hardware.

I also changed the order of the patches to first make the driver more platform
independent (patches 2 to 8), then convert to a drm bridge driver (patches 10
to 13) and finally expose the API, split the code and move the platform
independent driver to the bridges (patches 14 - 16). Hopefully this simplifies
testing/bisecting and helps me to understand potential error reports.

Also I added host_ops for attach/detach and the tearing effect handler to make
the calls into the platform code more visible.

Furthermore, the series should now apply to linux-next and correctly build the
exynos_defconfig.

Thanks,

Michael

Changelog:

v2:
- rebase on linux-next
- verify with exynos_defconfig
- fix crashes reported by Marek Szyprowski Exynos3250-based Rinato
- reorder patches
- add host_ops for platform specific code
- roughly test component framework integration with dummy

Michael Tretter (16):
  drm/encoder: remove obsolete documentation of bridge
  drm/exynos: remove in_bridge_node from exynos_dsi
  drm/exynos: use exynos_dsi as drvdata
  drm/exynos: extract helper functions for probe
  drm/exynos: move dsi host registration to probe
  drm/exynos: shift register values to fields on write
  drm/exynos: use identifier instead of register offsets
  drm/exynos: add host_ops callback for platform drivers
  drm/exynos: add callback for tearing effect handler
  drm/exynos: implement a drm bridge
  drm/exynos: convert encoder functions to bridge function
  drm/exynos: configure mode on drm bridge
  drm/exynos: get encoder from bridge whenever possible
  drm/exynos: add API functions for platform drivers
  drm/exynos: split out platform specific code
  drm/exynos: move bridge driver to bridges

 drivers/gpu/drm/bridge/Kconfig          |    9 +
 drivers/gpu/drm/bridge/Makefile         |    1 +
 drivers/gpu/drm/bridge/samsung-dsim.c   | 1790 +++++++++++++++++++++
 drivers/gpu/drm/exynos/Kconfig          |    5 +-
 drivers/gpu/drm/exynos/exynos_drm_dsi.c | 1927 ++---------------------
 include/drm/bridge/samsung-dsim.h       |   64 +
 include/drm/drm_encoder.h               |    1 -
 7 files changed, 2027 insertions(+), 1770 deletions(-)
 create mode 100644 drivers/gpu/drm/bridge/samsung-dsim.c
 create mode 100644 include/drm/bridge/samsung-dsim.h

-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* [PATCH v2 01/16] drm/encoder: remove obsolete documentation of bridge
  2020-09-11 13:53 ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Michael Tretter
@ 2020-09-11 13:53   ` Michael Tretter
  2020-11-07 15:07     ` Adam Ford
  2020-11-07 22:17     ` Sam Ravnborg
  2020-09-11 13:53   ` [PATCH v2 02/16] drm/exynos: remove in_bridge_node from exynos_dsi Michael Tretter
                     ` (15 subsequent siblings)
  16 siblings, 2 replies; 61+ messages in thread
From: Michael Tretter @ 2020-09-11 13:53 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: jy0922.shim, b.zolnierkie, narmstrong, sw0312.kim,
	Michael Tretter, krzk, a.hajda, Laurent Pinchart, kernel,
	sylvester.nawrocki

In commit 05193dc38197 ("drm/bridge: Make the bridge chain a
double-linked list") the bridge has been removed and replaced by a
private field. Remove the leftover documentation of the removed field.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
v2: none
---
 include/drm/drm_encoder.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h
index a60f5f1555ac..5dfa5f7a80a7 100644
--- a/include/drm/drm_encoder.h
+++ b/include/drm/drm_encoder.h
@@ -89,7 +89,6 @@ struct drm_encoder_funcs {
  * @head: list management
  * @base: base KMS object
  * @name: human readable name, can be overwritten by the driver
- * @bridge: bridge associated to the encoder
  * @funcs: control functions
  * @helper_private: mid-layer private data
  *
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 61+ messages in thread

* [PATCH v2 02/16] drm/exynos: remove in_bridge_node from exynos_dsi
  2020-09-11 13:53 ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Michael Tretter
  2020-09-11 13:53   ` [PATCH v2 01/16] drm/encoder: remove obsolete documentation of bridge Michael Tretter
@ 2020-09-11 13:53   ` Michael Tretter
  2020-11-07 22:19     ` Sam Ravnborg
  2020-09-11 13:54   ` [PATCH v2 03/16] drm/exynos: use exynos_dsi as drvdata Michael Tretter
                     ` (14 subsequent siblings)
  16 siblings, 1 reply; 61+ messages in thread
From: Michael Tretter @ 2020-09-11 13:53 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: jy0922.shim, b.zolnierkie, narmstrong, sw0312.kim,
	Michael Tretter, krzk, a.hajda, Laurent.pinchart, kernel,
	sylvester.nawrocki

We do not need to keep a reference to the in_bridge_node, but we can
simply drop it, once we found and attached the previous bridge.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
v2: none
---
 drivers/gpu/drm/exynos/exynos_drm_dsi.c | 15 +++++----------
 1 file changed, 5 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 1a1a2853a842..29f941b02210 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -282,7 +282,6 @@ struct exynos_dsi {
 	struct list_head transfer_list;
 
 	const struct exynos_dsi_driver_data *driver_data;
-	struct device_node *in_bridge_node;
 };
 
 #define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host)
@@ -1684,8 +1683,6 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
 	if (ret < 0)
 		return ret;
 
-	dsi->in_bridge_node = of_graph_get_remote_node(node, DSI_PORT_IN, 0);
-
 	return 0;
 }
 
@@ -1695,6 +1692,7 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
 	struct drm_encoder *encoder = dev_get_drvdata(dev);
 	struct exynos_dsi *dsi = encoder_to_dsi(encoder);
 	struct drm_device *drm_dev = data;
+	struct device_node *in_bridge_node;
 	struct drm_bridge *in_bridge;
 	int ret;
 
@@ -1706,10 +1704,12 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
 	if (ret < 0)
 		return ret;
 
-	if (dsi->in_bridge_node) {
-		in_bridge = of_drm_find_bridge(dsi->in_bridge_node);
+	in_bridge_node = of_graph_get_remote_node(dev->of_node, DSI_PORT_IN, 0);
+	if (in_bridge_node) {
+		in_bridge = of_drm_find_bridge(in_bridge_node);
 		if (in_bridge)
 			drm_bridge_attach(encoder, in_bridge, NULL, 0);
+		of_node_put(in_bridge_node);
 	}
 
 	return mipi_dsi_host_register(&dsi->dsi_host);
@@ -1830,17 +1830,12 @@ static int exynos_dsi_probe(struct platform_device *pdev)
 
 err_disable_runtime:
 	pm_runtime_disable(dev);
-	of_node_put(dsi->in_bridge_node);
 
 	return ret;
 }
 
 static int exynos_dsi_remove(struct platform_device *pdev)
 {
-	struct exynos_dsi *dsi = platform_get_drvdata(pdev);
-
-	of_node_put(dsi->in_bridge_node);
-
 	pm_runtime_disable(&pdev->dev);
 
 	component_del(&pdev->dev, &exynos_dsi_component_ops);
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 61+ messages in thread

* [PATCH v2 03/16] drm/exynos: use exynos_dsi as drvdata
  2020-09-11 13:53 ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Michael Tretter
  2020-09-11 13:53   ` [PATCH v2 01/16] drm/encoder: remove obsolete documentation of bridge Michael Tretter
  2020-09-11 13:53   ` [PATCH v2 02/16] drm/exynos: remove in_bridge_node from exynos_dsi Michael Tretter
@ 2020-09-11 13:54   ` Michael Tretter
  2020-11-07 22:24     ` Sam Ravnborg
  2020-09-11 13:54   ` [PATCH v2 04/16] drm/exynos: extract helper functions for probe Michael Tretter
                     ` (13 subsequent siblings)
  16 siblings, 1 reply; 61+ messages in thread
From: Michael Tretter @ 2020-09-11 13:54 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: jy0922.shim, b.zolnierkie, narmstrong, sw0312.kim,
	Michael Tretter, krzk, a.hajda, Laurent.pinchart, kernel,
	sylvester.nawrocki

Use the exynos_dsi as drvdata instead of the encoder to further decouple
the driver from the encoder.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
v2: drop removal of encoder_to_dsi
---
 drivers/gpu/drm/exynos/exynos_drm_dsi.c | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 29f941b02210..ed04064afbb2 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -1689,8 +1689,8 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
 static int exynos_dsi_bind(struct device *dev, struct device *master,
 				void *data)
 {
-	struct drm_encoder *encoder = dev_get_drvdata(dev);
-	struct exynos_dsi *dsi = encoder_to_dsi(encoder);
+	struct exynos_dsi *dsi = dev_get_drvdata(dev);
+	struct drm_encoder *encoder = &dsi->encoder;
 	struct drm_device *drm_dev = data;
 	struct device_node *in_bridge_node;
 	struct drm_bridge *in_bridge;
@@ -1718,8 +1718,8 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
 static void exynos_dsi_unbind(struct device *dev, struct device *master,
 				void *data)
 {
-	struct drm_encoder *encoder = dev_get_drvdata(dev);
-	struct exynos_dsi *dsi = encoder_to_dsi(encoder);
+	struct exynos_dsi *dsi = dev_get_drvdata(dev);
+	struct drm_encoder *encoder = &dsi->encoder;
 
 	exynos_dsi_disable(encoder);
 
@@ -1818,7 +1818,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
-	platform_set_drvdata(pdev, &dsi->encoder);
+	platform_set_drvdata(pdev, dsi);
 
 	pm_runtime_enable(dev);
 
@@ -1845,8 +1845,7 @@ static int exynos_dsi_remove(struct platform_device *pdev)
 
 static int __maybe_unused exynos_dsi_suspend(struct device *dev)
 {
-	struct drm_encoder *encoder = dev_get_drvdata(dev);
-	struct exynos_dsi *dsi = encoder_to_dsi(encoder);
+	struct exynos_dsi *dsi = dev_get_drvdata(dev);
 	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
 	int ret, i;
 
@@ -1876,8 +1875,7 @@ static int __maybe_unused exynos_dsi_suspend(struct device *dev)
 
 static int __maybe_unused exynos_dsi_resume(struct device *dev)
 {
-	struct drm_encoder *encoder = dev_get_drvdata(dev);
-	struct exynos_dsi *dsi = encoder_to_dsi(encoder);
+	struct exynos_dsi *dsi = dev_get_drvdata(dev);
 	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
 	int ret, i;
 
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 61+ messages in thread

* [PATCH v2 04/16] drm/exynos: extract helper functions for probe
  2020-09-11 13:53 ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Michael Tretter
                     ` (2 preceding siblings ...)
  2020-09-11 13:54   ` [PATCH v2 03/16] drm/exynos: use exynos_dsi as drvdata Michael Tretter
@ 2020-09-11 13:54   ` Michael Tretter
  2020-11-07 22:27     ` Sam Ravnborg
  2020-09-11 13:54   ` [PATCH v2 05/16] drm/exynos: move dsi host registration to probe Michael Tretter
                     ` (12 subsequent siblings)
  16 siblings, 1 reply; 61+ messages in thread
From: Michael Tretter @ 2020-09-11 13:54 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: jy0922.shim, b.zolnierkie, narmstrong, sw0312.kim,
	Michael Tretter, krzk, a.hajda, Laurent.pinchart, kernel,
	sylvester.nawrocki

As the driver shall be usable with drivers that use the component
framework and drivers that don't, split the common probing code into a
separate function that can be called from different locations.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
v2:
- move pm_runtime_enable from helper to calling function
---
 drivers/gpu/drm/exynos/exynos_drm_dsi.c | 40 ++++++++++++++++++-------
 1 file changed, 30 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index ed04064afbb2..f8e64b74a6dd 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -1731,7 +1731,7 @@ static const struct component_ops exynos_dsi_component_ops = {
 	.unbind	= exynos_dsi_unbind,
 };
 
-static int exynos_dsi_probe(struct platform_device *pdev)
+static struct exynos_dsi *__exynos_dsi_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct resource *res;
@@ -1740,7 +1740,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)
 
 	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
 	if (!dsi)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	/* To be checked as invalid one */
 	dsi->te_gpio = -ENOENT;
@@ -1763,14 +1763,14 @@ static int exynos_dsi_probe(struct platform_device *pdev)
 	if (ret) {
 		if (ret != -EPROBE_DEFER)
 			dev_info(dev, "failed to get regulators: %d\n", ret);
-		return ret;
+		return ERR_PTR(ret);
 	}
 
 	dsi->clks = devm_kcalloc(dev,
 			dsi->driver_data->num_clks, sizeof(*dsi->clks),
 			GFP_KERNEL);
 	if (!dsi->clks)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	for (i = 0; i < dsi->driver_data->num_clks; i++) {
 		dsi->clks[i] = devm_clk_get(dev, clk_names[i]);
@@ -1784,7 +1784,7 @@ static int exynos_dsi_probe(struct platform_device *pdev)
 
 			dev_info(dev, "failed to get the clock: %s\n",
 					clk_names[i]);
-			return PTR_ERR(dsi->clks[i]);
+			return ERR_PTR(PTR_ERR(dsi->clks[i]));
 		}
 	}
 
@@ -1792,18 +1792,18 @@ static int exynos_dsi_probe(struct platform_device *pdev)
 	dsi->reg_base = devm_ioremap_resource(dev, res);
 	if (IS_ERR(dsi->reg_base)) {
 		dev_err(dev, "failed to remap io region\n");
-		return PTR_ERR(dsi->reg_base);
+		return dsi->reg_base;
 	}
 
 	dsi->phy = devm_phy_get(dev, "dsim");
 	if (IS_ERR(dsi->phy)) {
 		dev_info(dev, "failed to get dsim phy\n");
-		return PTR_ERR(dsi->phy);
+		return ERR_PTR(PTR_ERR(dsi->phy));
 	}
 
 	dsi->irq = platform_get_irq(pdev, 0);
 	if (dsi->irq < 0)
-		return dsi->irq;
+		return ERR_PTR(dsi->irq);
 
 	irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
 	ret = devm_request_threaded_irq(dev, dsi->irq, NULL,
@@ -1811,13 +1811,29 @@ static int exynos_dsi_probe(struct platform_device *pdev)
 					dev_name(dev), dsi);
 	if (ret) {
 		dev_err(dev, "failed to request dsi irq\n");
-		return ret;
+		return ERR_PTR(ret);
 	}
 
 	ret = exynos_dsi_parse_dt(dsi);
 	if (ret)
-		return ret;
+		return ERR_PTR(ret);
+
+	return dsi;
+}
+
+static void __exynos_dsi_remove(struct exynos_dsi *dsi)
+{
+}
 
+static int exynos_dsi_probe(struct platform_device *pdev)
+{
+	struct exynos_dsi *dsi;
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	dsi = __exynos_dsi_probe(pdev);
+	if (IS_ERR(dsi))
+		return PTR_ERR(dsi);
 	platform_set_drvdata(pdev, dsi);
 
 	pm_runtime_enable(dev);
@@ -1836,8 +1852,12 @@ static int exynos_dsi_probe(struct platform_device *pdev)
 
 static int exynos_dsi_remove(struct platform_device *pdev)
 {
+	struct exynos_dsi *dsi = platform_get_drvdata(pdev);
+
 	pm_runtime_disable(&pdev->dev);
 
+	__exynos_dsi_remove(dsi);
+
 	component_del(&pdev->dev, &exynos_dsi_component_ops);
 
 	return 0;
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 61+ messages in thread

* [PATCH v2 05/16] drm/exynos: move dsi host registration to probe
  2020-09-11 13:53 ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Michael Tretter
                     ` (3 preceding siblings ...)
  2020-09-11 13:54   ` [PATCH v2 04/16] drm/exynos: extract helper functions for probe Michael Tretter
@ 2020-09-11 13:54   ` Michael Tretter
  2020-09-11 13:54   ` [PATCH v2 06/16] drm/exynos: shift register values to fields on write Michael Tretter
                     ` (11 subsequent siblings)
  16 siblings, 0 replies; 61+ messages in thread
From: Michael Tretter @ 2020-09-11 13:54 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: jy0922.shim, b.zolnierkie, narmstrong, sw0312.kim,
	Michael Tretter, krzk, a.hajda, Laurent.pinchart, kernel,
	sylvester.nawrocki

Once the driver implements a drm_bridge, the bridge will be attached
during bind. The driver has to register the mipi dsi host before making
the driver available at the component framework, because the bridge is
only initialized when a mipi dsi device attaches.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
v2: none
---
 drivers/gpu/drm/exynos/exynos_drm_dsi.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index f8e64b74a6dd..41000214a5fe 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -1712,7 +1712,7 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
 		of_node_put(in_bridge_node);
 	}
 
-	return mipi_dsi_host_register(&dsi->dsi_host);
+	return 0;
 }
 
 static void exynos_dsi_unbind(struct device *dev, struct device *master,
@@ -1722,8 +1722,6 @@ static void exynos_dsi_unbind(struct device *dev, struct device *master,
 	struct drm_encoder *encoder = &dsi->encoder;
 
 	exynos_dsi_disable(encoder);
-
-	mipi_dsi_host_unregister(&dsi->dsi_host);
 }
 
 static const struct component_ops exynos_dsi_component_ops = {
@@ -1818,11 +1816,16 @@ static struct exynos_dsi *__exynos_dsi_probe(struct platform_device *pdev)
 	if (ret)
 		return ERR_PTR(ret);
 
+	ret = mipi_dsi_host_register(&dsi->dsi_host);
+	if (ret)
+		return ERR_PTR(ret);
+
 	return dsi;
 }
 
 static void __exynos_dsi_remove(struct exynos_dsi *dsi)
 {
+	mipi_dsi_host_unregister(&dsi->dsi_host);
 }
 
 static int exynos_dsi_probe(struct platform_device *pdev)
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 61+ messages in thread

* [PATCH v2 06/16] drm/exynos: shift register values to fields on write
  2020-09-11 13:53 ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Michael Tretter
                     ` (4 preceding siblings ...)
  2020-09-11 13:54   ` [PATCH v2 05/16] drm/exynos: move dsi host registration to probe Michael Tretter
@ 2020-09-11 13:54   ` Michael Tretter
  2020-11-07 22:39     ` Sam Ravnborg
  2020-09-11 13:54   ` [PATCH v2 07/16] drm/exynos: use identifier instead of register offsets Michael Tretter
                     ` (10 subsequent siblings)
  16 siblings, 1 reply; 61+ messages in thread
From: Michael Tretter @ 2020-09-11 13:54 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: jy0922.shim, b.zolnierkie, narmstrong, sw0312.kim,
	Michael Tretter, krzk, a.hajda, Laurent.pinchart, kernel,
	sylvester.nawrocki

The phy timings are already shifted to the field position. If the driver
is reused on multiple platforms, this exposes the field positions to the
platform code.

Store only the timing values in the platform data and shift the value to
the field when writing the fields to the registers.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
v2: none
---
 drivers/gpu/drm/exynos/exynos_drm_dsi.c | 88 +++++++++++++------------
 1 file changed, 46 insertions(+), 42 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 41000214a5fe..0f2cac7ed944 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -399,54 +399,54 @@ static const unsigned int reg_values[] = {
 	[RESET_TYPE] = DSIM_SWRST,
 	[PLL_TIMER] = 500,
 	[STOP_STATE_CNT] = 0xf,
-	[PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0x0af),
+	[PHYCTRL_ULPS_EXIT] = 0x0af,
 	[PHYCTRL_VREG_LP] = 0,
 	[PHYCTRL_SLEW_UP] = 0,
-	[PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x06),
-	[PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0b),
-	[PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x07),
-	[PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x27),
-	[PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0d),
-	[PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x08),
-	[PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x09),
-	[PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x0d),
-	[PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0b),
+	[PHYTIMING_LPX] = 0x06,
+	[PHYTIMING_HS_EXIT] = 0x0b,
+	[PHYTIMING_CLK_PREPARE] = 0x07,
+	[PHYTIMING_CLK_ZERO] = 0x27,
+	[PHYTIMING_CLK_POST] = 0x0d,
+	[PHYTIMING_CLK_TRAIL] = 0x08,
+	[PHYTIMING_HS_PREPARE] = 0x09,
+	[PHYTIMING_HS_ZERO] = 0x0d,
+	[PHYTIMING_HS_TRAIL] = 0x0b,
 };
 
 static const unsigned int exynos5422_reg_values[] = {
 	[RESET_TYPE] = DSIM_SWRST,
 	[PLL_TIMER] = 500,
 	[STOP_STATE_CNT] = 0xf,
-	[PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0xaf),
+	[PHYCTRL_ULPS_EXIT] = 0xaf,
 	[PHYCTRL_VREG_LP] = 0,
 	[PHYCTRL_SLEW_UP] = 0,
-	[PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x08),
-	[PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0d),
-	[PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x09),
-	[PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x30),
-	[PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0e),
-	[PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x0a),
-	[PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x0c),
-	[PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x11),
-	[PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0d),
+	[PHYTIMING_LPX] = 0x08,
+	[PHYTIMING_HS_EXIT] = 0x0d,
+	[PHYTIMING_CLK_PREPARE] = 0x09,
+	[PHYTIMING_CLK_ZERO] = 0x30,
+	[PHYTIMING_CLK_POST] = 0x0e,
+	[PHYTIMING_CLK_TRAIL] = 0x0a,
+	[PHYTIMING_HS_PREPARE] = 0x0c,
+	[PHYTIMING_HS_ZERO] = 0x11,
+	[PHYTIMING_HS_TRAIL] = 0x0d,
 };
 
 static const unsigned int exynos5433_reg_values[] = {
 	[RESET_TYPE] = DSIM_FUNCRST,
 	[PLL_TIMER] = 22200,
 	[STOP_STATE_CNT] = 0xa,
-	[PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0x190),
-	[PHYCTRL_VREG_LP] = DSIM_PHYCTRL_B_DPHYCTL_VREG_LP,
-	[PHYCTRL_SLEW_UP] = DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP,
-	[PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x07),
-	[PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0c),
-	[PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x09),
-	[PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x2d),
-	[PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0e),
-	[PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x09),
-	[PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x0b),
-	[PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x10),
-	[PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0c),
+	[PHYCTRL_ULPS_EXIT] = 0x190,
+	[PHYCTRL_VREG_LP] = 1,
+	[PHYCTRL_SLEW_UP] = 1,
+	[PHYTIMING_LPX] = 0x07,
+	[PHYTIMING_HS_EXIT] = 0x0c,
+	[PHYTIMING_CLK_PREPARE] = 0x09,
+	[PHYTIMING_CLK_ZERO] = 0x2d,
+	[PHYTIMING_CLK_POST] = 0x0e,
+	[PHYTIMING_CLK_TRAIL] = 0x09,
+	[PHYTIMING_HS_PREPARE] = 0x0b,
+	[PHYTIMING_HS_ZERO] = 0x10,
+	[PHYTIMING_HS_TRAIL] = 0x0c,
 };
 
 static const struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
@@ -698,8 +698,11 @@ static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi)
 		return;
 
 	/* B D-PHY: D-PHY Master & Slave Analog Block control */
-	reg = reg_values[PHYCTRL_ULPS_EXIT] | reg_values[PHYCTRL_VREG_LP] |
-		reg_values[PHYCTRL_SLEW_UP];
+	reg = DSIM_PHYCTRL_ULPS_EXIT(reg_values[PHYCTRL_ULPS_EXIT]);
+	if (reg_values[PHYCTRL_VREG_LP])
+		reg |= DSIM_PHYCTRL_B_DPHYCTL_VREG_LP;
+	if (reg_values[PHYCTRL_SLEW_UP])
+		reg |= DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP;
 	exynos_dsi_write(dsi, DSIM_PHYCTRL_REG, reg);
 
 	/*
@@ -707,7 +710,8 @@ static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi)
 	 * T HS-EXIT: Time that the transmitter drives LP-11 following a HS
 	 *	burst
 	 */
-	reg = reg_values[PHYTIMING_LPX] | reg_values[PHYTIMING_HS_EXIT];
+	reg = DSIM_PHYTIMING_LPX(reg_values[PHYTIMING_LPX]) |
+		DSIM_PHYTIMING_HS_EXIT(reg_values[PHYTIMING_HS_EXIT]);
 	exynos_dsi_write(dsi, DSIM_PHYTIMING_REG, reg);
 
 	/*
@@ -723,11 +727,10 @@ static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi)
 	 * T CLK-TRAIL: Time that the transmitter drives the HS-0 state after
 	 *	the last payload clock bit of a HS transmission burst
 	 */
-	reg = reg_values[PHYTIMING_CLK_PREPARE] |
-		reg_values[PHYTIMING_CLK_ZERO] |
-		reg_values[PHYTIMING_CLK_POST] |
-		reg_values[PHYTIMING_CLK_TRAIL];
-
+	reg = DSIM_PHYTIMING1_CLK_PREPARE(reg_values[PHYTIMING_CLK_PREPARE]) |
+		DSIM_PHYTIMING1_CLK_ZERO(reg_values[PHYTIMING_CLK_ZERO]) |
+		DSIM_PHYTIMING1_CLK_POST(reg_values[PHYTIMING_CLK_POST]) |
+		DSIM_PHYTIMING1_CLK_TRAIL(reg_values[PHYTIMING_CLK_TRAIL]);
 	exynos_dsi_write(dsi, DSIM_PHYTIMING1_REG, reg);
 
 	/*
@@ -739,8 +742,9 @@ static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi)
 	 * T HS-TRAIL: Time that the transmitter drives the flipped differential
 	 *	state after last payload data bit of a HS transmission burst
 	 */
-	reg = reg_values[PHYTIMING_HS_PREPARE] | reg_values[PHYTIMING_HS_ZERO] |
-		reg_values[PHYTIMING_HS_TRAIL];
+	reg = DSIM_PHYTIMING2_HS_PREPARE(reg_values[PHYTIMING_HS_PREPARE]) |
+		DSIM_PHYTIMING2_HS_ZERO(reg_values[PHYTIMING_HS_ZERO]) |
+		DSIM_PHYTIMING2_HS_TRAIL(reg_values[PHYTIMING_HS_TRAIL]);
 	exynos_dsi_write(dsi, DSIM_PHYTIMING2_REG, reg);
 }
 
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 61+ messages in thread

* [PATCH v2 07/16] drm/exynos: use identifier instead of register offsets
  2020-09-11 13:53 ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Michael Tretter
                     ` (5 preceding siblings ...)
  2020-09-11 13:54   ` [PATCH v2 06/16] drm/exynos: shift register values to fields on write Michael Tretter
@ 2020-09-11 13:54   ` Michael Tretter
  2020-09-11 13:54   ` [PATCH v2 08/16] drm/exynos: add host_ops callback for platform drivers Michael Tretter
                     ` (9 subsequent siblings)
  16 siblings, 0 replies; 61+ messages in thread
From: Michael Tretter @ 2020-09-11 13:54 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: jy0922.shim, b.zolnierkie, narmstrong, sw0312.kim,
	Michael Tretter, krzk, a.hajda, Laurent.pinchart, kernel,
	sylvester.nawrocki

Different revisions of the MIPI-DSI PHY have slightly different register
layouts. Currently, the register layout was stored per platform, which
makes it necessary to define the layout for each new platform.

Keep the register layout in the driver and use identifiers to specify
which register layout shall be used on a platform.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
v2: none
---
 drivers/gpu/drm/exynos/exynos_drm_dsi.c | 54 ++++++++++++++++---------
 1 file changed, 36 insertions(+), 18 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 0f2cac7ed944..1a15ae71205d 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -239,8 +239,13 @@ struct exynos_dsi_transfer {
 #define DSIM_STATE_CMD_LPM		BIT(2)
 #define DSIM_STATE_VIDOUT_AVAILABLE	BIT(3)
 
+enum exynos_reg_offset {
+	EXYNOS_REG_OFS,
+	EXYNOS5433_REG_OFS
+};
+
 struct exynos_dsi_driver_data {
-	const unsigned int *reg_ofs;
+	enum exynos_reg_offset reg_ofs;
 	unsigned int plltmr_reg;
 	unsigned int has_freqband:1;
 	unsigned int has_clklane_stop:1;
@@ -317,18 +322,6 @@ enum reg_idx {
 	NUM_REGS
 };
 
-static inline void exynos_dsi_write(struct exynos_dsi *dsi, enum reg_idx idx,
-				    u32 val)
-{
-
-	writel(val, dsi->reg_base + dsi->driver_data->reg_ofs[idx]);
-}
-
-static inline u32 exynos_dsi_read(struct exynos_dsi *dsi, enum reg_idx idx)
-{
-	return readl(dsi->reg_base + dsi->driver_data->reg_ofs[idx]);
-}
-
 static const unsigned int exynos_reg_ofs[] = {
 	[DSIM_STATUS_REG] =  0x00,
 	[DSIM_SWRST_REG] =  0x04,
@@ -377,6 +370,31 @@ static const unsigned int exynos5433_reg_ofs[] = {
 	[DSIM_PHYTIMING2_REG] = 0xBC,
 };
 
+static inline void exynos_dsi_write(struct exynos_dsi *dsi, enum reg_idx idx,
+				    u32 val)
+{
+	const unsigned int *reg_ofs;
+
+	if (dsi->driver_data->reg_ofs == EXYNOS5433_REG_OFS)
+		reg_ofs = exynos5433_reg_ofs;
+	else
+		reg_ofs = exynos_reg_ofs;
+
+	writel(val, dsi->reg_base + reg_ofs[idx]);
+}
+
+static inline u32 exynos_dsi_read(struct exynos_dsi *dsi, enum reg_idx idx)
+{
+	const unsigned int *reg_ofs;
+
+	if (dsi->driver_data->reg_ofs == EXYNOS5433_REG_OFS)
+		reg_ofs = exynos5433_reg_ofs;
+	else
+		reg_ofs = exynos_reg_ofs;
+
+	return readl(dsi->reg_base + reg_ofs[idx]);
+}
+
 enum reg_value_idx {
 	RESET_TYPE,
 	PLL_TIMER,
@@ -450,7 +468,7 @@ static const unsigned int exynos5433_reg_values[] = {
 };
 
 static const struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
-	.reg_ofs = exynos_reg_ofs,
+	.reg_ofs = EXYNOS_REG_OFS,
 	.plltmr_reg = 0x50,
 	.has_freqband = 1,
 	.has_clklane_stop = 1,
@@ -462,7 +480,7 @@ static const struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
 };
 
 static const struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
-	.reg_ofs = exynos_reg_ofs,
+	.reg_ofs = EXYNOS_REG_OFS,
 	.plltmr_reg = 0x50,
 	.has_freqband = 1,
 	.has_clklane_stop = 1,
@@ -474,7 +492,7 @@ static const struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
 };
 
 static const struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
-	.reg_ofs = exynos_reg_ofs,
+	.reg_ofs = EXYNOS_REG_OFS,
 	.plltmr_reg = 0x58,
 	.num_clks = 2,
 	.max_freq = 1000,
@@ -484,7 +502,7 @@ static const struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
 };
 
 static const struct exynos_dsi_driver_data exynos5433_dsi_driver_data = {
-	.reg_ofs = exynos5433_reg_ofs,
+	.reg_ofs = EXYNOS5433_REG_OFS,
 	.plltmr_reg = 0xa0,
 	.has_clklane_stop = 1,
 	.num_clks = 5,
@@ -495,7 +513,7 @@ static const struct exynos_dsi_driver_data exynos5433_dsi_driver_data = {
 };
 
 static const struct exynos_dsi_driver_data exynos5422_dsi_driver_data = {
-	.reg_ofs = exynos5433_reg_ofs,
+	.reg_ofs = EXYNOS5433_REG_OFS,
 	.plltmr_reg = 0xa0,
 	.has_clklane_stop = 1,
 	.num_clks = 2,
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 61+ messages in thread

* [PATCH v2 08/16] drm/exynos: add host_ops callback for platform drivers
  2020-09-11 13:53 ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Michael Tretter
                     ` (6 preceding siblings ...)
  2020-09-11 13:54   ` [PATCH v2 07/16] drm/exynos: use identifier instead of register offsets Michael Tretter
@ 2020-09-11 13:54   ` Michael Tretter
  2020-09-15 17:07     ` Andrzej Hajda
  2020-09-11 13:54   ` [PATCH v2 09/16] drm/exynos: add callback for tearing effect handler Michael Tretter
                     ` (8 subsequent siblings)
  16 siblings, 1 reply; 61+ messages in thread
From: Michael Tretter @ 2020-09-11 13:54 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: jy0922.shim, b.zolnierkie, narmstrong, sw0312.kim,
	Michael Tretter, krzk, a.hajda, Laurent.pinchart, kernel,
	sylvester.nawrocki

Platform drivers need to be aware if a mipi dsi device attaches or
detaches. Add host_ops to the driver_data for attach and detach
callbacks and move the i80 mode selection and the hotplug handling into
the callback, because these depend on the drm driver.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
v2:
- new patch
---
 drivers/gpu/drm/exynos/exynos_drm_dsi.c | 64 ++++++++++++++++++++-----
 1 file changed, 53 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 1a15ae71205d..684a2fbef08a 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -239,6 +239,12 @@ struct exynos_dsi_transfer {
 #define DSIM_STATE_CMD_LPM		BIT(2)
 #define DSIM_STATE_VIDOUT_AVAILABLE	BIT(3)
 
+struct exynos_dsi;
+struct exynos_dsi_host_ops {
+	int (*attach)(struct device *dev, struct mipi_dsi_device *device);
+	int (*detach)(struct device *dev, struct mipi_dsi_device *device);
+};
+
 enum exynos_reg_offset {
 	EXYNOS_REG_OFS,
 	EXYNOS5433_REG_OFS
@@ -254,6 +260,7 @@ struct exynos_dsi_driver_data {
 	unsigned int wait_for_reset;
 	unsigned int num_bits_resol;
 	const unsigned int *reg_values;
+	const struct exynos_dsi_host_ops *host_ops;
 };
 
 struct exynos_dsi {
@@ -467,6 +474,41 @@ static const unsigned int exynos5433_reg_values[] = {
 	[PHYTIMING_HS_TRAIL] = 0x0c,
 };
 
+static int __exynos_dsi_host_attach(struct device *dev,
+				    struct mipi_dsi_device *device)
+{
+	struct exynos_dsi *dsi = dev_get_drvdata(dev);
+	struct drm_device *drm = dsi->encoder.dev;
+	struct exynos_drm_crtc *crtc;
+
+	mutex_lock(&drm->mode_config.mutex);
+	crtc = exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD);
+	crtc->i80_mode = !(device->mode_flags & MIPI_DSI_MODE_VIDEO);
+	mutex_unlock(&drm->mode_config.mutex);
+
+	if (drm->mode_config.poll_enabled)
+		drm_kms_helper_hotplug_event(drm);
+
+	return 0;
+}
+
+static int __exynos_dsi_host_detach(struct device *dev,
+				     struct mipi_dsi_device *device)
+{
+	struct exynos_dsi *dsi = dev_get_drvdata(dev);
+	struct drm_device *drm = dsi->encoder.dev;
+
+	if (drm->mode_config.poll_enabled)
+		drm_kms_helper_hotplug_event(drm);
+
+	return 0;
+}
+
+static const struct exynos_dsi_host_ops exynos_dsi_host_ops = {
+	.attach = __exynos_dsi_host_attach,
+	.detach = __exynos_dsi_host_detach,
+};
+
 static const struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
 	.reg_ofs = EXYNOS_REG_OFS,
 	.plltmr_reg = 0x50,
@@ -477,6 +519,7 @@ static const struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
 	.wait_for_reset = 1,
 	.num_bits_resol = 11,
 	.reg_values = reg_values,
+	.host_ops = &exynos_dsi_host_ops,
 };
 
 static const struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
@@ -489,6 +532,7 @@ static const struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
 	.wait_for_reset = 1,
 	.num_bits_resol = 11,
 	.reg_values = reg_values,
+	.host_ops = &exynos_dsi_host_ops,
 };
 
 static const struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
@@ -499,6 +543,7 @@ static const struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
 	.wait_for_reset = 1,
 	.num_bits_resol = 11,
 	.reg_values = reg_values,
+	.host_ops = &exynos_dsi_host_ops,
 };
 
 static const struct exynos_dsi_driver_data exynos5433_dsi_driver_data = {
@@ -510,6 +555,7 @@ static const struct exynos_dsi_driver_data exynos5433_dsi_driver_data = {
 	.wait_for_reset = 0,
 	.num_bits_resol = 12,
 	.reg_values = exynos5433_reg_values,
+	.host_ops = &exynos_dsi_host_ops,
 };
 
 static const struct exynos_dsi_driver_data exynos5422_dsi_driver_data = {
@@ -521,6 +567,7 @@ static const struct exynos_dsi_driver_data exynos5422_dsi_driver_data = {
 	.wait_for_reset = 1,
 	.num_bits_resol = 12,
 	.reg_values = exynos5422_reg_values,
+	.host_ops = &exynos_dsi_host_ops,
 };
 
 static const struct of_device_id exynos_dsi_of_match[] = {
@@ -1551,8 +1598,8 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
 				  struct mipi_dsi_device *device)
 {
 	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_device *drm = encoder->dev;
 	struct drm_bridge *out_bridge;
 
 	out_bridge  = of_drm_find_bridge(device->dev.of_node);
@@ -1590,18 +1637,12 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
 			return ret;
 	}
 
-	mutex_lock(&drm->mode_config.mutex);
-
 	dsi->lanes = device->lanes;
 	dsi->format = device->format;
 	dsi->mode_flags = device->mode_flags;
-	exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD)->i80_mode =
-			!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO);
 
-	mutex_unlock(&drm->mode_config.mutex);
-
-	if (drm->mode_config.poll_enabled)
-		drm_kms_helper_hotplug_event(drm);
+	if (ops && ops->attach)
+		ops->attach(dsi->dsi_host.dev, device);
 
 	return 0;
 }
@@ -1610,6 +1651,7 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
 				  struct mipi_dsi_device *device)
 {
 	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) {
@@ -1625,8 +1667,8 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
 		INIT_LIST_HEAD(&dsi->bridge_chain);
 	}
 
-	if (drm->mode_config.poll_enabled)
-		drm_kms_helper_hotplug_event(drm);
+	if (ops && ops->detach)
+		ops->detach(dsi->dsi_host.dev, device);
 
 	exynos_dsi_unregister_te_irq(dsi);
 
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 61+ messages in thread

* [PATCH v2 09/16] drm/exynos: add callback for tearing effect handler
  2020-09-11 13:53 ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Michael Tretter
                     ` (7 preceding siblings ...)
  2020-09-11 13:54   ` [PATCH v2 08/16] drm/exynos: add host_ops callback for platform drivers Michael Tretter
@ 2020-09-11 13:54   ` Michael Tretter
  2020-09-11 13:54   ` [PATCH v2 10/16] drm/exynos: implement a drm bridge Michael Tretter
                     ` (7 subsequent siblings)
  16 siblings, 0 replies; 61+ messages in thread
From: Michael Tretter @ 2020-09-11 13:54 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: jy0922.shim, b.zolnierkie, narmstrong, sw0312.kim,
	Michael Tretter, krzk, a.hajda, Laurent.pinchart, kernel,
	sylvester.nawrocki

The tearing effect interrupts are not handled by the MIPI DSI bridge
driver, but the display controller driver.

Allow platforms to register a callback for the tearing effect interrupt.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
v2:
- add handler as callback in host_ops
---
 drivers/gpu/drm/exynos/exynos_drm_dsi.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 684a2fbef08a..2d75f9877dc0 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -243,6 +243,7 @@ struct exynos_dsi;
 struct exynos_dsi_host_ops {
 	int (*attach)(struct device *dev, struct mipi_dsi_device *device);
 	int (*detach)(struct device *dev, struct mipi_dsi_device *device);
+	void (*te_handler)(struct device *dev);
 };
 
 enum exynos_reg_offset {
@@ -504,9 +505,17 @@ static int __exynos_dsi_host_detach(struct device *dev,
 	return 0;
 }
 
+static void __exynos_dsi_te_handler(struct device *dev)
+{
+	struct exynos_dsi *dsi = dev_get_drvdata(dev);
+
+	exynos_drm_crtc_te_handler(dsi->encoder.crtc);
+}
+
 static const struct exynos_dsi_host_ops exynos_dsi_host_ops = {
 	.attach = __exynos_dsi_host_attach,
 	.detach = __exynos_dsi_host_detach,
+	.te_handler = __exynos_dsi_te_handler,
 };
 
 static const struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
@@ -1354,11 +1363,12 @@ static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
 
 static irqreturn_t exynos_dsi_te_irq_handler(int irq, void *dev_id)
 {
-	struct exynos_dsi *dsi = (struct exynos_dsi *)dev_id;
-	struct drm_encoder *encoder = &dsi->encoder;
+	struct exynos_dsi *dsi = dev_id;
+	const struct exynos_dsi_host_ops *ops = dsi->driver_data->host_ops;
 
-	if (dsi->state & DSIM_STATE_VIDOUT_AVAILABLE)
-		exynos_drm_crtc_te_handler(encoder->crtc);
+	if (ops && ops->te_handler &&
+	    (dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
+		ops->te_handler(dsi->dsi_host.dev);
 
 	return IRQ_HANDLED;
 }
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 61+ messages in thread

* [PATCH v2 10/16] drm/exynos: implement a drm bridge
  2020-09-11 13:53 ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Michael Tretter
                     ` (8 preceding siblings ...)
  2020-09-11 13:54   ` [PATCH v2 09/16] drm/exynos: add callback for tearing effect handler Michael Tretter
@ 2020-09-11 13:54   ` Michael Tretter
  2020-09-14  8:29     ` Marek Szyprowski
  2020-09-11 13:54   ` [PATCH v2 11/16] drm/exynos: convert encoder functions to bridge function Michael Tretter
                     ` (6 subsequent siblings)
  16 siblings, 1 reply; 61+ messages in thread
From: Michael Tretter @ 2020-09-11 13:54 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: jy0922.shim, b.zolnierkie, narmstrong, sw0312.kim,
	Michael Tretter, krzk, a.hajda, Laurent.pinchart, kernel,
	sylvester.nawrocki

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@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

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 61+ messages in thread

* [PATCH v2 11/16] drm/exynos: convert encoder functions to bridge function
  2020-09-11 13:53 ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Michael Tretter
                     ` (9 preceding siblings ...)
  2020-09-11 13:54   ` [PATCH v2 10/16] drm/exynos: implement a drm bridge Michael Tretter
@ 2020-09-11 13:54   ` Michael Tretter
  2020-09-11 13:54   ` [PATCH v2 12/16] drm/exynos: configure mode on drm bridge Michael Tretter
                     ` (5 subsequent siblings)
  16 siblings, 0 replies; 61+ messages in thread
From: Michael Tretter @ 2020-09-11 13:54 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: jy0922.shim, b.zolnierkie, narmstrong, sw0312.kim,
	Michael Tretter, krzk, a.hajda, Laurent.pinchart, kernel,
	sylvester.nawrocki

If other drivers use the exynos_dsi driver as a bridge, they might bring
their own encoder. Enable and disable the MIPI-DSI bridge using the
bridge functions instead of the encoder functions.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
v2: none
---
 drivers/gpu/drm/exynos/exynos_drm_dsi.c | 33 +++++++++++++++----------
 1 file changed, 20 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 5e7c1a99a3ee..a4f17d50d1d8 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -1455,9 +1455,8 @@ static void exynos_dsi_unregister_te_irq(struct exynos_dsi *dsi)
 	}
 }
 
-static void exynos_dsi_enable(struct drm_encoder *encoder)
+static void exynos_dsi_enable(struct exynos_dsi *dsi)
 {
-	struct exynos_dsi *dsi = encoder_to_dsi(encoder);
 	struct drm_bridge *iter;
 	int ret;
 
@@ -1505,9 +1504,8 @@ static void exynos_dsi_enable(struct drm_encoder *encoder)
 	pm_runtime_put(dsi->dev);
 }
 
-static void exynos_dsi_disable(struct drm_encoder *encoder)
+static void exynos_dsi_disable(struct exynos_dsi *dsi)
 {
-	struct exynos_dsi *dsi = encoder_to_dsi(encoder);
 	struct drm_bridge *iter;
 
 	if (!(dsi->state & DSIM_STATE_ENABLED))
@@ -1598,11 +1596,6 @@ static int exynos_dsi_create_connector(struct drm_encoder *encoder)
 	return 0;
 }
 
-static const struct drm_encoder_helper_funcs exynos_dsi_encoder_helper_funcs = {
-	.enable = exynos_dsi_enable,
-	.disable = exynos_dsi_disable,
-};
-
 static int exynos_dsi_bridge_attach(struct drm_bridge *bridge,
 				    enum drm_bridge_attach_flags flags)
 {
@@ -1640,7 +1633,7 @@ static void exynos_dsi_bridge_detach(struct drm_bridge *bridge)
 
 	if (dsi->panel) {
 		mutex_lock(&drm->mode_config.mutex);
-		exynos_dsi_disable(&dsi->encoder);
+		exynos_dsi_disable(dsi);
 		dsi->panel = NULL;
 		dsi->connector.status = connector_status_disconnected;
 		mutex_unlock(&drm->mode_config.mutex);
@@ -1652,9 +1645,25 @@ static void exynos_dsi_bridge_detach(struct drm_bridge *bridge)
 	}
 }
 
+static void exynos_dsi_bridge_enable(struct drm_bridge *bridge)
+{
+	struct exynos_dsi *dsi = bridge->driver_private;
+
+	exynos_dsi_enable(dsi);
+}
+
+static void exynos_dsi_bridge_disable(struct drm_bridge *bridge)
+{
+	struct exynos_dsi *dsi = bridge->driver_private;
+
+	exynos_dsi_disable(dsi);
+}
+
 static const struct drm_bridge_funcs exynos_dsi_bridge_funcs = {
 	.attach = exynos_dsi_bridge_attach,
 	.detach = exynos_dsi_bridge_detach,
+	.enable = exynos_dsi_bridge_enable,
+	.disable = exynos_dsi_bridge_disable,
 };
 
 MODULE_DEVICE_TABLE(of, exynos_dsi_of_match);
@@ -1800,8 +1809,6 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
 
 	drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_TMDS);
 
-	drm_encoder_helper_add(encoder, &exynos_dsi_encoder_helper_funcs);
-
 	ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
 	if (ret < 0)
 		return ret;
@@ -1831,7 +1838,7 @@ static void exynos_dsi_unbind(struct device *dev, struct device *master,
 	struct exynos_dsi *dsi = dev_get_drvdata(dev);
 	struct drm_encoder *encoder = &dsi->encoder;
 
-	exynos_dsi_disable(encoder);
+	exynos_dsi_disable(dsi);
 
 	drm_encoder_cleanup(encoder);
 }
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 61+ messages in thread

* [PATCH v2 12/16] drm/exynos: configure mode on drm bridge
  2020-09-11 13:53 ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Michael Tretter
                     ` (10 preceding siblings ...)
  2020-09-11 13:54   ` [PATCH v2 11/16] drm/exynos: convert encoder functions to bridge function Michael Tretter
@ 2020-09-11 13:54   ` Michael Tretter
  2020-09-11 13:54   ` [PATCH v2 13/16] drm/exynos: get encoder from bridge whenever possible Michael Tretter
                     ` (4 subsequent siblings)
  16 siblings, 0 replies; 61+ messages in thread
From: Michael Tretter @ 2020-09-11 13:54 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: jy0922.shim, b.zolnierkie, narmstrong, sw0312.kim,
	Michael Tretter, krzk, a.hajda, Laurent.pinchart, kernel,
	sylvester.nawrocki

The driver uses the encoder to get the mode that shall be configured.
This is not possible, if the driver is used as a bridge, because the
encoder might not be used.

Use the mode_set function to set the display mode.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
v2: none
---
 drivers/gpu/drm/exynos/exynos_drm_dsi.c | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index a4f17d50d1d8..988447812333 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -288,6 +288,8 @@ struct exynos_dsi {
 	u32 mode_flags;
 	u32 format;
 
+	struct drm_display_mode mode;
+
 	int state;
 	struct drm_property *brightness;
 	struct completion completed;
@@ -961,7 +963,7 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi)
 
 static void exynos_dsi_set_display_mode(struct exynos_dsi *dsi)
 {
-	struct drm_display_mode *m = &dsi->encoder.crtc->state->adjusted_mode;
+	struct drm_display_mode *m = &dsi->mode;
 	unsigned int num_bits_resol = dsi->driver_data->num_bits_resol;
 	u32 reg;
 
@@ -1659,11 +1661,22 @@ static void exynos_dsi_bridge_disable(struct drm_bridge *bridge)
 	exynos_dsi_disable(dsi);
 }
 
+static void exynos_dsi_bridge_mode_set(struct drm_bridge *bridge,
+				       const struct drm_display_mode *mode,
+				       const struct drm_display_mode *adjusted_mode)
+{
+	struct exynos_dsi *dsi = bridge->driver_private;
+
+	/* The mode is set when actually enabling the device. */
+	drm_mode_copy(&dsi->mode, adjusted_mode);
+}
+
 static const struct drm_bridge_funcs exynos_dsi_bridge_funcs = {
 	.attach = exynos_dsi_bridge_attach,
 	.detach = exynos_dsi_bridge_detach,
 	.enable = exynos_dsi_bridge_enable,
 	.disable = exynos_dsi_bridge_disable,
+	.mode_set = exynos_dsi_bridge_mode_set,
 };
 
 MODULE_DEVICE_TABLE(of, exynos_dsi_of_match);
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 61+ messages in thread

* [PATCH v2 13/16] drm/exynos: get encoder from bridge whenever possible
  2020-09-11 13:53 ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Michael Tretter
                     ` (11 preceding siblings ...)
  2020-09-11 13:54   ` [PATCH v2 12/16] drm/exynos: configure mode on drm bridge Michael Tretter
@ 2020-09-11 13:54   ` Michael Tretter
  2020-09-11 13:54   ` [PATCH v2 14/16] drm/exynos: add API functions for platform drivers Michael Tretter
                     ` (3 subsequent siblings)
  16 siblings, 0 replies; 61+ messages in thread
From: Michael Tretter @ 2020-09-11 13:54 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: jy0922.shim, b.zolnierkie, narmstrong, sw0312.kim,
	Michael Tretter, krzk, a.hajda, Laurent.pinchart, kernel,
	sylvester.nawrocki

The bridge will not necessarily use the encoder of struct exynos_dsi,
but might use another encoder from another drm driver. Therefore, the
driver has to use the encoder from the bridge instead of the one from
exynos_dsi.

In the future, the struct exynos_dsi will not have an encoder at all.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
v2:
- add removal of encoder_to_dsi
---
 drivers/gpu/drm/exynos/exynos_drm_dsi.c | 14 ++++----------
 1 file changed, 4 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 988447812333..b9216785b2d7 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -303,11 +303,6 @@ struct exynos_dsi {
 #define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host)
 #define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector)
 
-static inline struct exynos_dsi *encoder_to_dsi(struct drm_encoder *e)
-{
-	return container_of(e, struct exynos_dsi, encoder);
-}
-
 enum reg_idx {
 	DSIM_STATUS_REG,	/* Status register */
 	DSIM_SWRST_REG,		/* Software reset register */
@@ -1570,11 +1565,10 @@ static const struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs
 	.get_modes = exynos_dsi_get_modes,
 };
 
-static int exynos_dsi_create_connector(struct drm_encoder *encoder)
+static int exynos_dsi_create_connector(struct exynos_dsi *dsi)
 {
-	struct exynos_dsi *dsi = encoder_to_dsi(encoder);
 	struct drm_connector *connector = &dsi->connector;
-	struct drm_device *drm = encoder->dev;
+	struct drm_device *drm = dsi->bridge.dev;
 	int ret;
 
 	connector->polled = DRM_CONNECTOR_POLL_HPD;
@@ -1589,7 +1583,7 @@ static int exynos_dsi_create_connector(struct drm_encoder *encoder)
 
 	connector->status = connector_status_disconnected;
 	drm_connector_helper_add(connector, &exynos_dsi_connector_helper_funcs);
-	drm_connector_attach_encoder(connector, encoder);
+	drm_connector_attach_encoder(connector, dsi->bridge.encoder);
 	if (!drm->registered)
 		return 0;
 
@@ -1615,7 +1609,7 @@ static int exynos_dsi_bridge_attach(struct drm_bridge *bridge,
 			return ret;
 		list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain);
 	} else {
-		ret = exynos_dsi_create_connector(encoder);
+		ret = exynos_dsi_create_connector(dsi);
 		if (ret)
 			return ret;
 
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 61+ messages in thread

* [PATCH v2 14/16] drm/exynos: add API functions for platform drivers
  2020-09-11 13:53 ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Michael Tretter
                     ` (12 preceding siblings ...)
  2020-09-11 13:54   ` [PATCH v2 13/16] drm/exynos: get encoder from bridge whenever possible Michael Tretter
@ 2020-09-11 13:54   ` Michael Tretter
  2020-09-11 13:54   ` [PATCH v2 15/16] drm/exynos: split out platform specific code Michael Tretter
                     ` (2 subsequent siblings)
  16 siblings, 0 replies; 61+ messages in thread
From: Michael Tretter @ 2020-09-11 13:54 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: jy0922.shim, b.zolnierkie, narmstrong, sw0312.kim,
	Michael Tretter, krzk, a.hajda, Laurent.pinchart, kernel,
	sylvester.nawrocki

Add new functions for the probe/remove API, the bind/unbind API and
resume/suspend calls. Move everything exynos drm specific into separate
functions, which can easily be moved to other files.

Also split struct exynos_dsi into a generic and a platform specific
struct.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
v2:
- new patch
---
 drivers/gpu/drm/exynos/exynos_drm_dsi.c | 114 +++++++++++++++++-------
 1 file changed, 82 insertions(+), 32 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index b9216785b2d7..ad70f5ce81ad 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -265,7 +265,6 @@ 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;
@@ -419,6 +418,11 @@ enum reg_value_idx {
 	PHYTIMING_HS_TRAIL
 };
 
+struct exynos_dsi_pltfm {
+	struct exynos_dsi *dsi;
+	struct drm_encoder encoder;
+};
+
 static const unsigned int reg_values[] = {
 	[RESET_TYPE] = DSIM_SWRST,
 	[PLL_TIMER] = 500,
@@ -476,7 +480,7 @@ static const unsigned int exynos5433_reg_values[] = {
 static int __exynos_dsi_host_attach(struct device *dev,
 				    struct mipi_dsi_device *device)
 {
-	struct exynos_dsi *dsi = dev_get_drvdata(dev);
+	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
 	struct drm_device *drm = dsi->encoder.dev;
 	struct exynos_drm_crtc *crtc;
 
@@ -494,7 +498,7 @@ static int __exynos_dsi_host_attach(struct device *dev,
 static int __exynos_dsi_host_detach(struct device *dev,
 				     struct mipi_dsi_device *device)
 {
-	struct exynos_dsi *dsi = dev_get_drvdata(dev);
+	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
 	struct drm_device *drm = dsi->encoder.dev;
 
 	if (drm->mode_config.poll_enabled)
@@ -505,7 +509,7 @@ static int __exynos_dsi_host_detach(struct device *dev,
 
 static void __exynos_dsi_te_handler(struct device *dev)
 {
-	struct exynos_dsi *dsi = dev_get_drvdata(dev);
+	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
 
 	exynos_drm_crtc_te_handler(dsi->encoder.crtc);
 }
@@ -1804,10 +1808,12 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
 	return 0;
 }
 
-static int exynos_dsi_bind(struct device *dev, struct device *master,
-				void *data)
+static int exynos_dsi_bind(struct exynos_dsi *dsi, struct drm_encoder *encoder);
+static void exynos_dsi_unbind(struct exynos_dsi *dsi);
+
+static int exynos_dsi_pltfm_bind(struct device *dev, struct device *master, void *data)
 {
-	struct exynos_dsi *dsi = dev_get_drvdata(dev);
+	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
 	struct drm_encoder *encoder = &dsi->encoder;
 	struct drm_device *drm_dev = data;
 	struct device_node *in_bridge_node;
@@ -1828,7 +1834,7 @@ 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);
+	ret = exynos_dsi_bind(dsi->dsi, encoder);
 	if (ret)
 		goto err;
 
@@ -1839,20 +1845,20 @@ static int exynos_dsi_bind(struct device *dev, struct device *master,
 	return ret;
 }
 
-static void exynos_dsi_unbind(struct device *dev, struct device *master,
-				void *data)
+static void exynos_dsi_pltfm_unbind(struct device *dev, struct device *master,
+				    void *data)
 {
-	struct exynos_dsi *dsi = dev_get_drvdata(dev);
+	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
 	struct drm_encoder *encoder = &dsi->encoder;
 
-	exynos_dsi_disable(dsi);
+	exynos_dsi_unbind(dsi->dsi);
 
 	drm_encoder_cleanup(encoder);
 }
 
-static const struct component_ops exynos_dsi_component_ops = {
-	.bind	= exynos_dsi_bind,
-	.unbind	= exynos_dsi_unbind,
+static const struct component_ops exynos_dsi_pltfm_component_ops = {
+	.bind	= exynos_dsi_pltfm_bind,
+	.unbind	= exynos_dsi_pltfm_unbind,
 };
 
 static struct exynos_dsi *__exynos_dsi_probe(struct platform_device *pdev)
@@ -1963,20 +1969,52 @@ static void __exynos_dsi_remove(struct exynos_dsi *dsi)
 	mipi_dsi_host_unregister(&dsi->dsi_host);
 }
 
-static int exynos_dsi_probe(struct platform_device *pdev)
+/*
+ * Probe/remove API, used from platforms based on the DRM bridge API.
+ */
+static struct exynos_dsi *exynos_dsi_probe(struct platform_device *pdev)
 {
-	struct exynos_dsi *dsi;
+	return __exynos_dsi_probe(pdev);
+}
+
+static void exynos_dsi_remove(struct exynos_dsi *dsi)
+{
+	return __exynos_dsi_remove(dsi);
+}
+
+/*
+ * Bind/unbind API, used from platforms based on the component framework.
+ */
+static int exynos_dsi_bind(struct exynos_dsi *dsi, struct drm_encoder *encoder)
+{
+	struct drm_bridge *previous = drm_bridge_chain_get_first_bridge(encoder);
+
+	return drm_bridge_attach(encoder, &dsi->bridge, previous, 0);
+}
+
+static void exynos_dsi_unbind(struct exynos_dsi *dsi)
+{
+	exynos_dsi_disable(dsi);
+}
+
+static int exynos_dsi_pltfm_probe(struct platform_device *pdev)
+{
+	struct exynos_dsi_pltfm *dsi;
 	struct device *dev = &pdev->dev;
 	int ret;
 
-	dsi = __exynos_dsi_probe(pdev);
-	if (IS_ERR(dsi))
-		return PTR_ERR(dsi);
+	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
+	if (!dsi)
+		return -ENOMEM;
 	platform_set_drvdata(pdev, dsi);
 
+	dsi->dsi = exynos_dsi_probe(pdev);
+	if (IS_ERR(dsi->dsi))
+		return PTR_ERR(dsi->dsi);
+
 	pm_runtime_enable(dev);
 
-	ret = component_add(dev, &exynos_dsi_component_ops);
+	ret = component_add(dev, &exynos_dsi_pltfm_component_ops);
 	if (ret)
 		goto err_disable_runtime;
 
@@ -1988,22 +2026,21 @@ static int exynos_dsi_probe(struct platform_device *pdev)
 	return ret;
 }
 
-static int exynos_dsi_remove(struct platform_device *pdev)
+static int exynos_dsi_pltfm_remove(struct platform_device *pdev)
 {
-	struct exynos_dsi *dsi = platform_get_drvdata(pdev);
+	struct exynos_dsi_pltfm *dsi = platform_get_drvdata(pdev);
 
 	pm_runtime_disable(&pdev->dev);
 
-	__exynos_dsi_remove(dsi);
+	exynos_dsi_remove(dsi->dsi);
 
-	component_del(&pdev->dev, &exynos_dsi_component_ops);
+	component_del(&pdev->dev, &exynos_dsi_pltfm_component_ops);
 
 	return 0;
 }
 
-static int __maybe_unused exynos_dsi_suspend(struct device *dev)
+static int exynos_dsi_suspend(struct exynos_dsi *dsi)
 {
-	struct exynos_dsi *dsi = dev_get_drvdata(dev);
 	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
 	int ret, i;
 
@@ -2031,9 +2068,8 @@ static int __maybe_unused exynos_dsi_suspend(struct device *dev)
 	return 0;
 }
 
-static int __maybe_unused exynos_dsi_resume(struct device *dev)
+static int exynos_dsi_resume(struct exynos_dsi *dsi)
 {
-	struct exynos_dsi *dsi = dev_get_drvdata(dev);
 	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
 	int ret, i;
 
@@ -2065,15 +2101,29 @@ static int __maybe_unused exynos_dsi_resume(struct device *dev)
 	return ret;
 }
 
+static int __maybe_unused exynos_dsi_pltfm_suspend(struct device *dev)
+{
+	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
+
+	return exynos_dsi_suspend(dsi->dsi);
+}
+
+static int __maybe_unused exynos_dsi_pltfm_resume(struct device *dev)
+{
+	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
+
+	return exynos_dsi_resume(dsi->dsi);
+}
+
 static const struct dev_pm_ops exynos_dsi_pm_ops = {
-	SET_RUNTIME_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume, NULL)
+	SET_RUNTIME_PM_OPS(exynos_dsi_pltfm_suspend, exynos_dsi_pltfm_resume, NULL)
 	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
 				pm_runtime_force_resume)
 };
 
 struct platform_driver dsi_driver = {
-	.probe = exynos_dsi_probe,
-	.remove = exynos_dsi_remove,
+	.probe = exynos_dsi_pltfm_probe,
+	.remove = exynos_dsi_pltfm_remove,
 	.driver = {
 		   .name = "exynos-dsi",
 		   .owner = THIS_MODULE,
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 61+ messages in thread

* [PATCH v2 15/16] drm/exynos: split out platform specific code
  2020-09-11 13:53 ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Michael Tretter
                     ` (13 preceding siblings ...)
  2020-09-11 13:54   ` [PATCH v2 14/16] drm/exynos: add API functions for platform drivers Michael Tretter
@ 2020-09-11 13:54   ` Michael Tretter
  2020-09-11 13:54   ` [PATCH v2 16/16] drm/exynos: move bridge driver to bridges Michael Tretter
  2020-11-09  3:15   ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Inki Dae
  16 siblings, 0 replies; 61+ messages in thread
From: Michael Tretter @ 2020-09-11 13:54 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: jy0922.shim, b.zolnierkie, narmstrong, sw0312.kim,
	Michael Tretter, krzk, a.hajda, Laurent.pinchart, kernel,
	sylvester.nawrocki

Split the driver into the drm bridge driver and the exynos platform
specific driver.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
v2: none
---
 drivers/gpu/drm/exynos/Makefile               |   2 +-
 drivers/gpu/drm/exynos/exynos_drm_dsi.c       | 372 +-----------------
 drivers/gpu/drm/exynos/exynos_drm_dsi.h       |  64 +++
 drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c | 333 ++++++++++++++++
 4 files changed, 405 insertions(+), 366 deletions(-)
 create mode 100644 drivers/gpu/drm/exynos/exynos_drm_dsi.h
 create mode 100644 drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c

diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile
index 2fd2f3ee4fcf..add70b336935 100644
--- a/drivers/gpu/drm/exynos/Makefile
+++ b/drivers/gpu/drm/exynos/Makefile
@@ -11,7 +11,7 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD)	+= exynos_drm_fimd.o
 exynosdrm-$(CONFIG_DRM_EXYNOS5433_DECON)	+= exynos5433_drm_decon.o
 exynosdrm-$(CONFIG_DRM_EXYNOS7_DECON)	+= exynos7_drm_decon.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_DPI)	+= exynos_drm_dpi.o
-exynosdrm-$(CONFIG_DRM_EXYNOS_DSI)	+= exynos_drm_dsi.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_DSI)	+= exynos_drm_dsi.o exynos_drm_dsi_pltfm.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_DP)	+= exynos_dp.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_MIXER)	+= exynos_mixer.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI)	+= exynos_hdmi.o
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index ad70f5ce81ad..e8aea9d90c34 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -32,8 +32,7 @@
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_simple_kms_helper.h>
 
-#include "exynos_drm_crtc.h"
-#include "exynos_drm_drv.h"
+#include "exynos_drm_dsi.h"
 
 /* returns true iff both arguments logically differs */
 #define NEQV(a, b) (!(a) ^ !(b))
@@ -44,10 +43,6 @@
 #define DSIM_TX_READY_HS_CLK		(1 << 10)
 #define DSIM_PLL_STABLE			(1 << 31)
 
-/* DSIM_SWRST */
-#define DSIM_FUNCRST			(1 << 16)
-#define DSIM_SWRST			(1 << 0)
-
 /* DSIM_TIMEOUT */
 #define DSIM_LPDR_TIMEOUT(x)		((x) << 0)
 #define DSIM_BTA_TIMEOUT(x)		((x) << 16)
@@ -239,31 +234,6 @@ struct exynos_dsi_transfer {
 #define DSIM_STATE_CMD_LPM		BIT(2)
 #define DSIM_STATE_VIDOUT_AVAILABLE	BIT(3)
 
-struct exynos_dsi;
-struct exynos_dsi_host_ops {
-	int (*attach)(struct device *dev, struct mipi_dsi_device *device);
-	int (*detach)(struct device *dev, struct mipi_dsi_device *device);
-	void (*te_handler)(struct device *dev);
-};
-
-enum exynos_reg_offset {
-	EXYNOS_REG_OFS,
-	EXYNOS5433_REG_OFS
-};
-
-struct exynos_dsi_driver_data {
-	enum exynos_reg_offset reg_ofs;
-	unsigned int plltmr_reg;
-	unsigned int has_freqband:1;
-	unsigned int has_clklane_stop:1;
-	unsigned int num_clks;
-	unsigned int max_freq;
-	unsigned int wait_for_reset;
-	unsigned int num_bits_resol;
-	const unsigned int *reg_values;
-	const struct exynos_dsi_host_ops *host_ops;
-};
-
 struct exynos_dsi {
 	struct drm_bridge bridge;
 	struct mipi_dsi_host dsi_host;
@@ -400,201 +370,6 @@ static inline u32 exynos_dsi_read(struct exynos_dsi *dsi, enum reg_idx idx)
 	return readl(dsi->reg_base + reg_ofs[idx]);
 }
 
-enum reg_value_idx {
-	RESET_TYPE,
-	PLL_TIMER,
-	STOP_STATE_CNT,
-	PHYCTRL_ULPS_EXIT,
-	PHYCTRL_VREG_LP,
-	PHYCTRL_SLEW_UP,
-	PHYTIMING_LPX,
-	PHYTIMING_HS_EXIT,
-	PHYTIMING_CLK_PREPARE,
-	PHYTIMING_CLK_ZERO,
-	PHYTIMING_CLK_POST,
-	PHYTIMING_CLK_TRAIL,
-	PHYTIMING_HS_PREPARE,
-	PHYTIMING_HS_ZERO,
-	PHYTIMING_HS_TRAIL
-};
-
-struct exynos_dsi_pltfm {
-	struct exynos_dsi *dsi;
-	struct drm_encoder encoder;
-};
-
-static const unsigned int reg_values[] = {
-	[RESET_TYPE] = DSIM_SWRST,
-	[PLL_TIMER] = 500,
-	[STOP_STATE_CNT] = 0xf,
-	[PHYCTRL_ULPS_EXIT] = 0x0af,
-	[PHYCTRL_VREG_LP] = 0,
-	[PHYCTRL_SLEW_UP] = 0,
-	[PHYTIMING_LPX] = 0x06,
-	[PHYTIMING_HS_EXIT] = 0x0b,
-	[PHYTIMING_CLK_PREPARE] = 0x07,
-	[PHYTIMING_CLK_ZERO] = 0x27,
-	[PHYTIMING_CLK_POST] = 0x0d,
-	[PHYTIMING_CLK_TRAIL] = 0x08,
-	[PHYTIMING_HS_PREPARE] = 0x09,
-	[PHYTIMING_HS_ZERO] = 0x0d,
-	[PHYTIMING_HS_TRAIL] = 0x0b,
-};
-
-static const unsigned int exynos5422_reg_values[] = {
-	[RESET_TYPE] = DSIM_SWRST,
-	[PLL_TIMER] = 500,
-	[STOP_STATE_CNT] = 0xf,
-	[PHYCTRL_ULPS_EXIT] = 0xaf,
-	[PHYCTRL_VREG_LP] = 0,
-	[PHYCTRL_SLEW_UP] = 0,
-	[PHYTIMING_LPX] = 0x08,
-	[PHYTIMING_HS_EXIT] = 0x0d,
-	[PHYTIMING_CLK_PREPARE] = 0x09,
-	[PHYTIMING_CLK_ZERO] = 0x30,
-	[PHYTIMING_CLK_POST] = 0x0e,
-	[PHYTIMING_CLK_TRAIL] = 0x0a,
-	[PHYTIMING_HS_PREPARE] = 0x0c,
-	[PHYTIMING_HS_ZERO] = 0x11,
-	[PHYTIMING_HS_TRAIL] = 0x0d,
-};
-
-static const unsigned int exynos5433_reg_values[] = {
-	[RESET_TYPE] = DSIM_FUNCRST,
-	[PLL_TIMER] = 22200,
-	[STOP_STATE_CNT] = 0xa,
-	[PHYCTRL_ULPS_EXIT] = 0x190,
-	[PHYCTRL_VREG_LP] = 1,
-	[PHYCTRL_SLEW_UP] = 1,
-	[PHYTIMING_LPX] = 0x07,
-	[PHYTIMING_HS_EXIT] = 0x0c,
-	[PHYTIMING_CLK_PREPARE] = 0x09,
-	[PHYTIMING_CLK_ZERO] = 0x2d,
-	[PHYTIMING_CLK_POST] = 0x0e,
-	[PHYTIMING_CLK_TRAIL] = 0x09,
-	[PHYTIMING_HS_PREPARE] = 0x0b,
-	[PHYTIMING_HS_ZERO] = 0x10,
-	[PHYTIMING_HS_TRAIL] = 0x0c,
-};
-
-static int __exynos_dsi_host_attach(struct device *dev,
-				    struct mipi_dsi_device *device)
-{
-	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
-	struct drm_device *drm = dsi->encoder.dev;
-	struct exynos_drm_crtc *crtc;
-
-	mutex_lock(&drm->mode_config.mutex);
-	crtc = exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD);
-	crtc->i80_mode = !(device->mode_flags & MIPI_DSI_MODE_VIDEO);
-	mutex_unlock(&drm->mode_config.mutex);
-
-	if (drm->mode_config.poll_enabled)
-		drm_kms_helper_hotplug_event(drm);
-
-	return 0;
-}
-
-static int __exynos_dsi_host_detach(struct device *dev,
-				     struct mipi_dsi_device *device)
-{
-	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
-	struct drm_device *drm = dsi->encoder.dev;
-
-	if (drm->mode_config.poll_enabled)
-		drm_kms_helper_hotplug_event(drm);
-
-	return 0;
-}
-
-static void __exynos_dsi_te_handler(struct device *dev)
-{
-	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
-
-	exynos_drm_crtc_te_handler(dsi->encoder.crtc);
-}
-
-static const struct exynos_dsi_host_ops exynos_dsi_host_ops = {
-	.attach = __exynos_dsi_host_attach,
-	.detach = __exynos_dsi_host_detach,
-	.te_handler = __exynos_dsi_te_handler,
-};
-
-static const struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
-	.reg_ofs = EXYNOS_REG_OFS,
-	.plltmr_reg = 0x50,
-	.has_freqband = 1,
-	.has_clklane_stop = 1,
-	.num_clks = 2,
-	.max_freq = 1000,
-	.wait_for_reset = 1,
-	.num_bits_resol = 11,
-	.reg_values = reg_values,
-	.host_ops = &exynos_dsi_host_ops,
-};
-
-static const struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
-	.reg_ofs = EXYNOS_REG_OFS,
-	.plltmr_reg = 0x50,
-	.has_freqband = 1,
-	.has_clklane_stop = 1,
-	.num_clks = 2,
-	.max_freq = 1000,
-	.wait_for_reset = 1,
-	.num_bits_resol = 11,
-	.reg_values = reg_values,
-	.host_ops = &exynos_dsi_host_ops,
-};
-
-static const struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
-	.reg_ofs = EXYNOS_REG_OFS,
-	.plltmr_reg = 0x58,
-	.num_clks = 2,
-	.max_freq = 1000,
-	.wait_for_reset = 1,
-	.num_bits_resol = 11,
-	.reg_values = reg_values,
-	.host_ops = &exynos_dsi_host_ops,
-};
-
-static const struct exynos_dsi_driver_data exynos5433_dsi_driver_data = {
-	.reg_ofs = EXYNOS5433_REG_OFS,
-	.plltmr_reg = 0xa0,
-	.has_clklane_stop = 1,
-	.num_clks = 5,
-	.max_freq = 1500,
-	.wait_for_reset = 0,
-	.num_bits_resol = 12,
-	.reg_values = exynos5433_reg_values,
-	.host_ops = &exynos_dsi_host_ops,
-};
-
-static const struct exynos_dsi_driver_data exynos5422_dsi_driver_data = {
-	.reg_ofs = EXYNOS5433_REG_OFS,
-	.plltmr_reg = 0xa0,
-	.has_clklane_stop = 1,
-	.num_clks = 2,
-	.max_freq = 1500,
-	.wait_for_reset = 1,
-	.num_bits_resol = 12,
-	.reg_values = exynos5422_reg_values,
-	.host_ops = &exynos_dsi_host_ops,
-};
-
-static const struct of_device_id exynos_dsi_of_match[] = {
-	{ .compatible = "samsung,exynos3250-mipi-dsi",
-	  .data = &exynos3_dsi_driver_data },
-	{ .compatible = "samsung,exynos4210-mipi-dsi",
-	  .data = &exynos4_dsi_driver_data },
-	{ .compatible = "samsung,exynos5410-mipi-dsi",
-	  .data = &exynos5_dsi_driver_data },
-	{ .compatible = "samsung,exynos5422-mipi-dsi",
-	  .data = &exynos5422_dsi_driver_data },
-	{ .compatible = "samsung,exynos5433-mipi-dsi",
-	  .data = &exynos5433_dsi_driver_data },
-	{ }
-};
-
 static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi)
 {
 	if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300)))
@@ -1677,8 +1452,6 @@ static const struct drm_bridge_funcs exynos_dsi_bridge_funcs = {
 	.mode_set = exynos_dsi_bridge_mode_set,
 };
 
-MODULE_DEVICE_TABLE(of, exynos_dsi_of_match);
-
 static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
 				  struct mipi_dsi_device *device)
 {
@@ -1779,11 +1552,6 @@ static int exynos_dsi_of_read_u32(const struct device_node *np,
 	return ret;
 }
 
-enum {
-	DSI_PORT_IN,
-	DSI_PORT_OUT
-};
-
 static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
 {
 	struct device *dev = dsi->dev;
@@ -1808,59 +1576,6 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
 	return 0;
 }
 
-static int exynos_dsi_bind(struct exynos_dsi *dsi, struct drm_encoder *encoder);
-static void exynos_dsi_unbind(struct exynos_dsi *dsi);
-
-static int exynos_dsi_pltfm_bind(struct device *dev, struct device *master, void *data)
-{
-	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
-	struct drm_encoder *encoder = &dsi->encoder;
-	struct drm_device *drm_dev = data;
-	struct device_node *in_bridge_node;
-	struct drm_bridge *in_bridge;
-	int ret;
-
-	drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_TMDS);
-
-	ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
-	if (ret < 0)
-		return ret;
-
-	in_bridge_node = of_graph_get_remote_node(dev->of_node, DSI_PORT_IN, 0);
-	if (in_bridge_node) {
-		in_bridge = of_drm_find_bridge(in_bridge_node);
-		if (in_bridge)
-			drm_bridge_attach(encoder, in_bridge, NULL, 0);
-		of_node_put(in_bridge_node);
-	}
-
-	ret = exynos_dsi_bind(dsi->dsi, encoder);
-	if (ret)
-		goto err;
-
-	return 0;
-
-err:
-	drm_encoder_cleanup(encoder);
-	return ret;
-}
-
-static void exynos_dsi_pltfm_unbind(struct device *dev, struct device *master,
-				    void *data)
-{
-	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
-	struct drm_encoder *encoder = &dsi->encoder;
-
-	exynos_dsi_unbind(dsi->dsi);
-
-	drm_encoder_cleanup(encoder);
-}
-
-static const struct component_ops exynos_dsi_pltfm_component_ops = {
-	.bind	= exynos_dsi_pltfm_bind,
-	.unbind	= exynos_dsi_pltfm_unbind,
-};
-
 static struct exynos_dsi *__exynos_dsi_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -1972,12 +1687,12 @@ static void __exynos_dsi_remove(struct exynos_dsi *dsi)
 /*
  * Probe/remove API, used from platforms based on the DRM bridge API.
  */
-static struct exynos_dsi *exynos_dsi_probe(struct platform_device *pdev)
+struct exynos_dsi *exynos_dsi_probe(struct platform_device *pdev)
 {
 	return __exynos_dsi_probe(pdev);
 }
 
-static void exynos_dsi_remove(struct exynos_dsi *dsi)
+void exynos_dsi_remove(struct exynos_dsi *dsi)
 {
 	return __exynos_dsi_remove(dsi);
 }
@@ -1985,61 +1700,19 @@ static void exynos_dsi_remove(struct exynos_dsi *dsi)
 /*
  * Bind/unbind API, used from platforms based on the component framework.
  */
-static int exynos_dsi_bind(struct exynos_dsi *dsi, struct drm_encoder *encoder)
+int exynos_dsi_bind(struct exynos_dsi *dsi, struct drm_encoder *encoder)
 {
 	struct drm_bridge *previous = drm_bridge_chain_get_first_bridge(encoder);
 
 	return drm_bridge_attach(encoder, &dsi->bridge, previous, 0);
 }
 
-static void exynos_dsi_unbind(struct exynos_dsi *dsi)
+void exynos_dsi_unbind(struct exynos_dsi *dsi)
 {
 	exynos_dsi_disable(dsi);
 }
 
-static int exynos_dsi_pltfm_probe(struct platform_device *pdev)
-{
-	struct exynos_dsi_pltfm *dsi;
-	struct device *dev = &pdev->dev;
-	int ret;
-
-	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
-	if (!dsi)
-		return -ENOMEM;
-	platform_set_drvdata(pdev, dsi);
-
-	dsi->dsi = exynos_dsi_probe(pdev);
-	if (IS_ERR(dsi->dsi))
-		return PTR_ERR(dsi->dsi);
-
-	pm_runtime_enable(dev);
-
-	ret = component_add(dev, &exynos_dsi_pltfm_component_ops);
-	if (ret)
-		goto err_disable_runtime;
-
-	return 0;
-
-err_disable_runtime:
-	pm_runtime_disable(dev);
-
-	return ret;
-}
-
-static int exynos_dsi_pltfm_remove(struct platform_device *pdev)
-{
-	struct exynos_dsi_pltfm *dsi = platform_get_drvdata(pdev);
-
-	pm_runtime_disable(&pdev->dev);
-
-	exynos_dsi_remove(dsi->dsi);
-
-	component_del(&pdev->dev, &exynos_dsi_pltfm_component_ops);
-
-	return 0;
-}
-
-static int exynos_dsi_suspend(struct exynos_dsi *dsi)
+int exynos_dsi_suspend(struct exynos_dsi *dsi)
 {
 	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
 	int ret, i;
@@ -2068,7 +1741,7 @@ static int exynos_dsi_suspend(struct exynos_dsi *dsi)
 	return 0;
 }
 
-static int exynos_dsi_resume(struct exynos_dsi *dsi)
+int exynos_dsi_resume(struct exynos_dsi *dsi)
 {
 	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
 	int ret, i;
@@ -2101,37 +1774,6 @@ static int exynos_dsi_resume(struct exynos_dsi *dsi)
 	return ret;
 }
 
-static int __maybe_unused exynos_dsi_pltfm_suspend(struct device *dev)
-{
-	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
-
-	return exynos_dsi_suspend(dsi->dsi);
-}
-
-static int __maybe_unused exynos_dsi_pltfm_resume(struct device *dev)
-{
-	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
-
-	return exynos_dsi_resume(dsi->dsi);
-}
-
-static const struct dev_pm_ops exynos_dsi_pm_ops = {
-	SET_RUNTIME_PM_OPS(exynos_dsi_pltfm_suspend, exynos_dsi_pltfm_resume, NULL)
-	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
-				pm_runtime_force_resume)
-};
-
-struct platform_driver dsi_driver = {
-	.probe = exynos_dsi_pltfm_probe,
-	.remove = exynos_dsi_pltfm_remove,
-	.driver = {
-		   .name = "exynos-dsi",
-		   .owner = THIS_MODULE,
-		   .pm = &exynos_dsi_pm_ops,
-		   .of_match_table = exynos_dsi_of_match,
-	},
-};
-
 MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
 MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
 MODULE_DESCRIPTION("Samsung SoC MIPI DSI Master");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.h b/drivers/gpu/drm/exynos/exynos_drm_dsi.h
new file mode 100644
index 000000000000..8fa3276889de
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __EXYNOS_DRM_DSI__
+#define __EXYNOS_DRM_DSI__
+
+struct drm_encoder;
+struct exynos_dsi;
+struct platform_device;
+struct mipi_dsi_device;
+
+enum exynos_reg_offset {
+	EXYNOS_REG_OFS,
+	EXYNOS5433_REG_OFS
+};
+
+struct exynos_dsi *exynos_dsi_probe(struct platform_device *pdev);
+void exynos_dsi_remove(struct exynos_dsi *dsi);
+int exynos_dsi_bind(struct exynos_dsi *dsi, struct drm_encoder *encoder);
+void exynos_dsi_unbind(struct exynos_dsi *dsi);
+
+int exynos_dsi_suspend(struct exynos_dsi *dsi);
+int exynos_dsi_resume(struct exynos_dsi *dsi);
+
+enum reg_value_idx {
+	RESET_TYPE,
+	PLL_TIMER,
+	STOP_STATE_CNT,
+	PHYCTRL_ULPS_EXIT,
+	PHYCTRL_VREG_LP,
+	PHYCTRL_SLEW_UP,
+	PHYTIMING_LPX,
+	PHYTIMING_HS_EXIT,
+	PHYTIMING_CLK_PREPARE,
+	PHYTIMING_CLK_ZERO,
+	PHYTIMING_CLK_POST,
+	PHYTIMING_CLK_TRAIL,
+	PHYTIMING_HS_PREPARE,
+	PHYTIMING_HS_ZERO,
+	PHYTIMING_HS_TRAIL
+};
+
+/* DSIM_SWRST */
+#define DSIM_FUNCRST			(1 << 16)
+#define DSIM_SWRST			(1 << 0)
+
+struct exynos_dsi_host_ops {
+	int (*attach)(struct device *dev, struct mipi_dsi_device *device);
+	int (*detach)(struct device *dev, struct mipi_dsi_device *device);
+	void (*te_handler)(struct device *dev);
+};
+
+struct exynos_dsi_driver_data {
+	enum exynos_reg_offset reg_ofs;
+	unsigned int plltmr_reg;
+	unsigned int has_freqband:1;
+	unsigned int has_clklane_stop:1;
+	unsigned int num_clks;
+	unsigned int max_freq;
+	unsigned int wait_for_reset;
+	unsigned int num_bits_resol;
+	const unsigned int *reg_values;
+	const struct exynos_dsi_host_ops *host_ops;
+};
+
+#endif /* __EXYNOS_DRM_DSI__ */
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c b/drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c
new file mode 100644
index 000000000000..79d9ec6ade45
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c
@@ -0,0 +1,333 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Samsung SoC MIPI DSI Master driver.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd
+ *
+ * Contacts: Tomasz Figa <t.figa@samsung.com>
+ */
+
+#include <linux/component.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/pm_runtime.h>
+
+#include <drm/drm_bridge.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#include "exynos_drm_crtc.h"
+#include "exynos_drm_drv.h"
+#include "exynos_drm_dsi.h"
+
+enum {
+	DSI_PORT_IN,
+	DSI_PORT_OUT
+};
+
+struct exynos_dsi_pltfm {
+	struct exynos_dsi *dsi;
+	struct drm_encoder encoder;
+};
+
+static const unsigned int reg_values[] = {
+	[RESET_TYPE] = DSIM_SWRST,
+	[PLL_TIMER] = 500,
+	[STOP_STATE_CNT] = 0xf,
+	[PHYCTRL_ULPS_EXIT] = 0x0af,
+	[PHYCTRL_VREG_LP] = 0,
+	[PHYCTRL_SLEW_UP] = 0,
+	[PHYTIMING_LPX] = 0x06,
+	[PHYTIMING_HS_EXIT] = 0x0b,
+	[PHYTIMING_CLK_PREPARE] = 0x07,
+	[PHYTIMING_CLK_ZERO] = 0x27,
+	[PHYTIMING_CLK_POST] = 0x0d,
+	[PHYTIMING_CLK_TRAIL] = 0x08,
+	[PHYTIMING_HS_PREPARE] = 0x09,
+	[PHYTIMING_HS_ZERO] = 0x0d,
+	[PHYTIMING_HS_TRAIL] = 0x0b,
+};
+
+static const unsigned int exynos5422_reg_values[] = {
+	[RESET_TYPE] = DSIM_SWRST,
+	[PLL_TIMER] = 500,
+	[STOP_STATE_CNT] = 0xf,
+	[PHYCTRL_ULPS_EXIT] = 0xaf,
+	[PHYCTRL_VREG_LP] = 0,
+	[PHYCTRL_SLEW_UP] = 0,
+	[PHYTIMING_LPX] = 0x08,
+	[PHYTIMING_HS_EXIT] = 0x0d,
+	[PHYTIMING_CLK_PREPARE] = 0x09,
+	[PHYTIMING_CLK_ZERO] = 0x30,
+	[PHYTIMING_CLK_POST] = 0x0e,
+	[PHYTIMING_CLK_TRAIL] = 0x0a,
+	[PHYTIMING_HS_PREPARE] = 0x0c,
+	[PHYTIMING_HS_ZERO] = 0x11,
+	[PHYTIMING_HS_TRAIL] = 0x0d,
+};
+
+static const unsigned int exynos5433_reg_values[] = {
+	[RESET_TYPE] = DSIM_FUNCRST,
+	[PLL_TIMER] = 22200,
+	[STOP_STATE_CNT] = 0xa,
+	[PHYCTRL_ULPS_EXIT] = 0x190,
+	[PHYCTRL_VREG_LP] = 1,
+	[PHYCTRL_SLEW_UP] = 1,
+	[PHYTIMING_LPX] = 0x07,
+	[PHYTIMING_HS_EXIT] = 0x0c,
+	[PHYTIMING_CLK_PREPARE] = 0x09,
+	[PHYTIMING_CLK_ZERO] = 0x2d,
+	[PHYTIMING_CLK_POST] = 0x0e,
+	[PHYTIMING_CLK_TRAIL] = 0x09,
+	[PHYTIMING_HS_PREPARE] = 0x0b,
+	[PHYTIMING_HS_ZERO] = 0x10,
+	[PHYTIMING_HS_TRAIL] = 0x0c,
+};
+
+static int __exynos_dsi_host_attach(struct device *dev,
+				    struct mipi_dsi_device *device)
+{
+	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
+	struct drm_device *drm = dsi->encoder.dev;
+	struct exynos_drm_crtc *crtc;
+
+	mutex_lock(&drm->mode_config.mutex);
+	crtc = exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD);
+	crtc->i80_mode = !(device->mode_flags & MIPI_DSI_MODE_VIDEO);
+	mutex_unlock(&drm->mode_config.mutex);
+
+	if (drm->mode_config.poll_enabled)
+		drm_kms_helper_hotplug_event(drm);
+
+	return 0;
+}
+
+static int __exynos_dsi_host_detach(struct device *dev,
+				    struct mipi_dsi_device *device)
+{
+	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
+	struct drm_device *drm = dsi->encoder.dev;
+
+	if (drm->mode_config.poll_enabled)
+		drm_kms_helper_hotplug_event(drm);
+
+	return 0;
+}
+
+static void __exynos_dsi_te_handler(struct device *dev)
+{
+	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
+
+	exynos_drm_crtc_te_handler(dsi->encoder.crtc);
+}
+
+static const struct exynos_dsi_host_ops exynos_dsi_host_ops = {
+	.attach = __exynos_dsi_host_attach,
+	.detach = __exynos_dsi_host_detach,
+	.te_handler = __exynos_dsi_te_handler,
+};
+
+static const struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
+	.reg_ofs = EXYNOS_REG_OFS,
+	.plltmr_reg = 0x50,
+	.has_freqband = 1,
+	.has_clklane_stop = 1,
+	.num_clks = 2,
+	.max_freq = 1000,
+	.wait_for_reset = 1,
+	.num_bits_resol = 11,
+	.reg_values = reg_values,
+	.host_ops = &exynos_dsi_host_ops,
+};
+
+static const struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
+	.reg_ofs = EXYNOS_REG_OFS,
+	.plltmr_reg = 0x50,
+	.has_freqband = 1,
+	.has_clklane_stop = 1,
+	.num_clks = 2,
+	.max_freq = 1000,
+	.wait_for_reset = 1,
+	.num_bits_resol = 11,
+	.reg_values = reg_values,
+	.host_ops = &exynos_dsi_host_ops,
+};
+
+static const struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
+	.reg_ofs = EXYNOS_REG_OFS,
+	.plltmr_reg = 0x58,
+	.num_clks = 2,
+	.max_freq = 1000,
+	.wait_for_reset = 1,
+	.num_bits_resol = 11,
+	.reg_values = reg_values,
+	.host_ops = &exynos_dsi_host_ops,
+};
+
+static const struct exynos_dsi_driver_data exynos5433_dsi_driver_data = {
+	.reg_ofs = EXYNOS5433_REG_OFS,
+	.plltmr_reg = 0xa0,
+	.has_clklane_stop = 1,
+	.num_clks = 5,
+	.max_freq = 1500,
+	.wait_for_reset = 0,
+	.num_bits_resol = 12,
+	.reg_values = exynos5433_reg_values,
+	.host_ops = &exynos_dsi_host_ops,
+};
+
+static const struct exynos_dsi_driver_data exynos5422_dsi_driver_data = {
+	.reg_ofs = EXYNOS5433_REG_OFS,
+	.plltmr_reg = 0xa0,
+	.has_clklane_stop = 1,
+	.num_clks = 2,
+	.max_freq = 1500,
+	.wait_for_reset = 1,
+	.num_bits_resol = 12,
+	.reg_values = exynos5422_reg_values,
+	.host_ops = &exynos_dsi_host_ops,
+};
+
+static const struct of_device_id exynos_dsi_of_match[] = {
+	{ .compatible = "samsung,exynos3250-mipi-dsi",
+	  .data = &exynos3_dsi_driver_data },
+	{ .compatible = "samsung,exynos4210-mipi-dsi",
+	  .data = &exynos4_dsi_driver_data },
+	{ .compatible = "samsung,exynos5410-mipi-dsi",
+	  .data = &exynos5_dsi_driver_data },
+	{ .compatible = "samsung,exynos5422-mipi-dsi",
+	  .data = &exynos5422_dsi_driver_data },
+	{ .compatible = "samsung,exynos5433-mipi-dsi",
+	  .data = &exynos5433_dsi_driver_data },
+	{ }
+};
+
+static int exynos_dsi_pltfm_bind(struct device *dev, struct device *master, void *data)
+{
+	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
+	struct drm_encoder *encoder = &dsi->encoder;
+	struct drm_device *drm_dev = data;
+	struct device_node *in_bridge_node;
+	struct drm_bridge *in_bridge;
+	int ret;
+
+	drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_TMDS);
+
+	ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
+	if (ret < 0)
+		return ret;
+
+	in_bridge_node = of_graph_get_remote_node(dev->of_node, DSI_PORT_IN, 0);
+	if (in_bridge_node) {
+		in_bridge = of_drm_find_bridge(in_bridge_node);
+		if (in_bridge)
+			drm_bridge_attach(encoder, in_bridge, NULL, 0);
+		of_node_put(in_bridge_node);
+	}
+
+	ret = exynos_dsi_bind(dsi->dsi, encoder);
+	if (ret)
+		goto err;
+
+	return 0;
+
+err:
+	drm_encoder_cleanup(encoder);
+	return ret;
+}
+
+static void exynos_dsi_pltfm_unbind(struct device *dev, struct device *master,
+				    void *data)
+{
+	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
+	struct drm_encoder *encoder = &dsi->encoder;
+
+	exynos_dsi_unbind(dsi->dsi);
+
+	drm_encoder_cleanup(encoder);
+}
+
+static const struct component_ops exynos_dsi_pltfm_component_ops = {
+	.bind	= exynos_dsi_pltfm_bind,
+	.unbind	= exynos_dsi_pltfm_unbind,
+};
+
+static int exynos_dsi_pltfm_probe(struct platform_device *pdev)
+{
+	struct exynos_dsi_pltfm *dsi;
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
+	if (!dsi)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, dsi);
+
+	dsi->dsi = exynos_dsi_probe(pdev);
+	if (IS_ERR(dsi->dsi))
+		return PTR_ERR(dsi->dsi);
+
+	pm_runtime_enable(dev);
+
+	ret = component_add(dev, &exynos_dsi_pltfm_component_ops);
+	if (ret)
+		goto err_disable_runtime;
+
+	return 0;
+
+err_disable_runtime:
+	pm_runtime_disable(dev);
+
+	return ret;
+}
+
+static int exynos_dsi_pltfm_remove(struct platform_device *pdev)
+{
+	struct exynos_dsi_pltfm *dsi = platform_get_drvdata(pdev);
+
+	pm_runtime_disable(&pdev->dev);
+
+	exynos_dsi_remove(dsi->dsi);
+
+	component_del(&pdev->dev, &exynos_dsi_pltfm_component_ops);
+
+	return 0;
+}
+
+static int __maybe_unused exynos_dsi_pltfm_suspend(struct device *dev)
+{
+	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
+
+	return exynos_dsi_suspend(dsi->dsi);
+}
+
+static int __maybe_unused exynos_dsi_pltfm_resume(struct device *dev)
+{
+	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
+
+	return exynos_dsi_resume(dsi->dsi);
+}
+
+static const struct dev_pm_ops exynos_dsi_pm_ops = {
+	SET_RUNTIME_PM_OPS(exynos_dsi_pltfm_suspend, exynos_dsi_pltfm_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				pm_runtime_force_resume)
+};
+
+struct platform_driver dsi_driver = {
+	.probe = exynos_dsi_pltfm_probe,
+	.remove = exynos_dsi_pltfm_remove,
+	.driver = {
+		   .name = "exynos-dsi",
+		   .owner = THIS_MODULE,
+		   .pm = &exynos_dsi_pm_ops,
+		   .of_match_table = exynos_dsi_of_match,
+	},
+};
+
+MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
+MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
+MODULE_DESCRIPTION("Samsung SoC MIPI DSI Master");
+MODULE_LICENSE("GPL v2");
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 61+ messages in thread

* [PATCH v2 16/16] drm/exynos: move bridge driver to bridges
  2020-09-11 13:53 ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Michael Tretter
                     ` (14 preceding siblings ...)
  2020-09-11 13:54   ` [PATCH v2 15/16] drm/exynos: split out platform specific code Michael Tretter
@ 2020-09-11 13:54   ` Michael Tretter
  2020-09-16  7:58     ` Daniel Vetter
  2020-11-09  3:15   ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Inki Dae
  16 siblings, 1 reply; 61+ messages in thread
From: Michael Tretter @ 2020-09-11 13:54 UTC (permalink / raw)
  To: dri-devel, linux-samsung-soc
  Cc: jy0922.shim, b.zolnierkie, narmstrong, sw0312.kim,
	Michael Tretter, krzk, a.hajda, Laurent.pinchart, kernel,
	sylvester.nawrocki

As the driver is not platform dependent anymore, move it to the drm
bridge driver directory.

Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
---
v2:
- select DRM_SAMSUNG_DSIM from DRM_EXYNOS_DSI
- add removal of depends on !FB_S3C
---
 drivers/gpu/drm/bridge/Kconfig                |    9 +
 drivers/gpu/drm/bridge/Makefile               |    1 +
 drivers/gpu/drm/bridge/samsung-dsim.c         | 1790 ++++++++++++++++
 drivers/gpu/drm/exynos/Kconfig                |    5 +-
 drivers/gpu/drm/exynos/Makefile               |    2 +-
 drivers/gpu/drm/exynos/exynos_drm_dsi.c       | 1896 ++---------------
 drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c |  333 ---
 .../drm/bridge/samsung-dsim.h                 |   20 +-
 8 files changed, 2037 insertions(+), 2019 deletions(-)
 create mode 100644 drivers/gpu/drm/bridge/samsung-dsim.c
 delete mode 100644 drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c
 rename drivers/gpu/drm/exynos/exynos_drm_dsi.h => include/drm/bridge/samsung-dsim.h (69%)

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 3e11af4e9f63..55ab5030c6cf 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -125,6 +125,15 @@ config DRM_PARADE_PS8640
 	  The PS8640 is a high-performance and low-power
 	  MIPI DSI to eDP converter
 
+config DRM_SAMSUNG_DSIM
+	tristate "Samsung MIPI DSI bridge"
+	depends on OF
+	select DRM_KMS_HELPER
+	select DRM_MIPI_DSI
+	select DRM_PANEL
+	help
+	  Samsung MIPI DSI bridge driver.
+
 config DRM_SIL_SII8620
 	tristate "Silicon Image SII8620 HDMI/MHL bridge"
 	depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index c589a6a7cbe1..5ac7a5c413dc 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v
 obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
 obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
 obj-$(CONFIG_DRM_PARADE_PS8640) += parade-ps8640.o
+obj-$(CONFIG_DRM_SAMSUNG_DSIM) += samsung-dsim.o
 obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o
 obj-$(CONFIG_DRM_SII902X) += sii902x.o
 obj-$(CONFIG_DRM_SII9234) += sii9234.o
diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
new file mode 100644
index 000000000000..6d2d8dc027de
--- /dev/null
+++ b/drivers/gpu/drm/bridge/samsung-dsim.c
@@ -0,0 +1,1790 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Samsung SoC MIPI DSI Master driver.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd
+ *
+ * Contacts: Tomasz Figa <t.figa@samsung.com>
+*/
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/component.h>
+#include <linux/gpio/consumer.h>
+#include <linux/irq.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_graph.h>
+#include <linux/phy/phy.h>
+#include <linux/regulator/consumer.h>
+
+#include <asm/unaligned.h>
+
+#include <video/mipi_display.h>
+#include <video/videomode.h>
+
+#include <drm/bridge/samsung-dsim.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+
+/* returns true iff both arguments logically differs */
+#define NEQV(a, b) (!(a) ^ !(b))
+
+/* DSIM_STATUS */
+#define DSIM_STOP_STATE_DAT(x)		(((x) & 0xf) << 0)
+#define DSIM_STOP_STATE_CLK		(1 << 8)
+#define DSIM_TX_READY_HS_CLK		(1 << 10)
+#define DSIM_PLL_STABLE			(1 << 31)
+
+/* DSIM_TIMEOUT */
+#define DSIM_LPDR_TIMEOUT(x)		((x) << 0)
+#define DSIM_BTA_TIMEOUT(x)		((x) << 16)
+
+/* DSIM_CLKCTRL */
+#define DSIM_ESC_PRESCALER(x)		(((x) & 0xffff) << 0)
+#define DSIM_ESC_PRESCALER_MASK		(0xffff << 0)
+#define DSIM_LANE_ESC_CLK_EN_CLK	(1 << 19)
+#define DSIM_LANE_ESC_CLK_EN_DATA(x)	(((x) & 0xf) << 20)
+#define DSIM_LANE_ESC_CLK_EN_DATA_MASK	(0xf << 20)
+#define DSIM_BYTE_CLKEN			(1 << 24)
+#define DSIM_BYTE_CLK_SRC(x)		(((x) & 0x3) << 25)
+#define DSIM_BYTE_CLK_SRC_MASK		(0x3 << 25)
+#define DSIM_PLL_BYPASS			(1 << 27)
+#define DSIM_ESC_CLKEN			(1 << 28)
+#define DSIM_TX_REQUEST_HSCLK		(1 << 31)
+
+/* DSIM_CONFIG */
+#define DSIM_LANE_EN_CLK		(1 << 0)
+#define DSIM_LANE_EN(x)			(((x) & 0xf) << 1)
+#define DSIM_NUM_OF_DATA_LANE(x)	(((x) & 0x3) << 5)
+#define DSIM_SUB_PIX_FORMAT(x)		(((x) & 0x7) << 8)
+#define DSIM_MAIN_PIX_FORMAT_MASK	(0x7 << 12)
+#define DSIM_MAIN_PIX_FORMAT_RGB888	(0x7 << 12)
+#define DSIM_MAIN_PIX_FORMAT_RGB666	(0x6 << 12)
+#define DSIM_MAIN_PIX_FORMAT_RGB666_P	(0x5 << 12)
+#define DSIM_MAIN_PIX_FORMAT_RGB565	(0x4 << 12)
+#define DSIM_SUB_VC			(((x) & 0x3) << 16)
+#define DSIM_MAIN_VC			(((x) & 0x3) << 18)
+#define DSIM_HSA_MODE			(1 << 20)
+#define DSIM_HBP_MODE			(1 << 21)
+#define DSIM_HFP_MODE			(1 << 22)
+#define DSIM_HSE_MODE			(1 << 23)
+#define DSIM_AUTO_MODE			(1 << 24)
+#define DSIM_VIDEO_MODE			(1 << 25)
+#define DSIM_BURST_MODE			(1 << 26)
+#define DSIM_SYNC_INFORM		(1 << 27)
+#define DSIM_EOT_DISABLE		(1 << 28)
+#define DSIM_MFLUSH_VS			(1 << 29)
+/* This flag is valid only for exynos3250/3472/5260/5430 */
+#define DSIM_CLKLANE_STOP		(1 << 30)
+
+/* DSIM_ESCMODE */
+#define DSIM_TX_TRIGGER_RST		(1 << 4)
+#define DSIM_TX_LPDT_LP			(1 << 6)
+#define DSIM_CMD_LPDT_LP		(1 << 7)
+#define DSIM_FORCE_BTA			(1 << 16)
+#define DSIM_FORCE_STOP_STATE		(1 << 20)
+#define DSIM_STOP_STATE_CNT(x)		(((x) & 0x7ff) << 21)
+#define DSIM_STOP_STATE_CNT_MASK	(0x7ff << 21)
+
+/* DSIM_MDRESOL */
+#define DSIM_MAIN_STAND_BY		(1 << 31)
+#define DSIM_MAIN_VRESOL(x, num_bits)	(((x) & ((1 << (num_bits)) - 1)) << 16)
+#define DSIM_MAIN_HRESOL(x, num_bits)	(((x) & ((1 << (num_bits)) - 1)) << 0)
+
+/* DSIM_MVPORCH */
+#define DSIM_CMD_ALLOW(x)		((x) << 28)
+#define DSIM_STABLE_VFP(x)		((x) << 16)
+#define DSIM_MAIN_VBP(x)		((x) << 0)
+#define DSIM_CMD_ALLOW_MASK		(0xf << 28)
+#define DSIM_STABLE_VFP_MASK		(0x7ff << 16)
+#define DSIM_MAIN_VBP_MASK		(0x7ff << 0)
+
+/* DSIM_MHPORCH */
+#define DSIM_MAIN_HFP(x)		((x) << 16)
+#define DSIM_MAIN_HBP(x)		((x) << 0)
+#define DSIM_MAIN_HFP_MASK		((0xffff) << 16)
+#define DSIM_MAIN_HBP_MASK		((0xffff) << 0)
+
+/* DSIM_MSYNC */
+#define DSIM_MAIN_VSA(x)		((x) << 22)
+#define DSIM_MAIN_HSA(x)		((x) << 0)
+#define DSIM_MAIN_VSA_MASK		((0x3ff) << 22)
+#define DSIM_MAIN_HSA_MASK		((0xffff) << 0)
+
+/* DSIM_SDRESOL */
+#define DSIM_SUB_STANDY(x)		((x) << 31)
+#define DSIM_SUB_VRESOL(x)		((x) << 16)
+#define DSIM_SUB_HRESOL(x)		((x) << 0)
+#define DSIM_SUB_STANDY_MASK		((0x1) << 31)
+#define DSIM_SUB_VRESOL_MASK		((0x7ff) << 16)
+#define DSIM_SUB_HRESOL_MASK		((0x7ff) << 0)
+
+/* DSIM_INTSRC */
+#define DSIM_INT_PLL_STABLE		(1 << 31)
+#define DSIM_INT_SW_RST_RELEASE		(1 << 30)
+#define DSIM_INT_SFR_FIFO_EMPTY		(1 << 29)
+#define DSIM_INT_SFR_HDR_FIFO_EMPTY	(1 << 28)
+#define DSIM_INT_BTA			(1 << 25)
+#define DSIM_INT_FRAME_DONE		(1 << 24)
+#define DSIM_INT_RX_TIMEOUT		(1 << 21)
+#define DSIM_INT_BTA_TIMEOUT		(1 << 20)
+#define DSIM_INT_RX_DONE		(1 << 18)
+#define DSIM_INT_RX_TE			(1 << 17)
+#define DSIM_INT_RX_ACK			(1 << 16)
+#define DSIM_INT_RX_ECC_ERR		(1 << 15)
+#define DSIM_INT_RX_CRC_ERR		(1 << 14)
+
+/* DSIM_FIFOCTRL */
+#define DSIM_RX_DATA_FULL		(1 << 25)
+#define DSIM_RX_DATA_EMPTY		(1 << 24)
+#define DSIM_SFR_HEADER_FULL		(1 << 23)
+#define DSIM_SFR_HEADER_EMPTY		(1 << 22)
+#define DSIM_SFR_PAYLOAD_FULL		(1 << 21)
+#define DSIM_SFR_PAYLOAD_EMPTY		(1 << 20)
+#define DSIM_I80_HEADER_FULL		(1 << 19)
+#define DSIM_I80_HEADER_EMPTY		(1 << 18)
+#define DSIM_I80_PAYLOAD_FULL		(1 << 17)
+#define DSIM_I80_PAYLOAD_EMPTY		(1 << 16)
+#define DSIM_SD_HEADER_FULL		(1 << 15)
+#define DSIM_SD_HEADER_EMPTY		(1 << 14)
+#define DSIM_SD_PAYLOAD_FULL		(1 << 13)
+#define DSIM_SD_PAYLOAD_EMPTY		(1 << 12)
+#define DSIM_MD_HEADER_FULL		(1 << 11)
+#define DSIM_MD_HEADER_EMPTY		(1 << 10)
+#define DSIM_MD_PAYLOAD_FULL		(1 << 9)
+#define DSIM_MD_PAYLOAD_EMPTY		(1 << 8)
+#define DSIM_RX_FIFO			(1 << 4)
+#define DSIM_SFR_FIFO			(1 << 3)
+#define DSIM_I80_FIFO			(1 << 2)
+#define DSIM_SD_FIFO			(1 << 1)
+#define DSIM_MD_FIFO			(1 << 0)
+
+/* DSIM_PHYACCHR */
+#define DSIM_AFC_EN			(1 << 14)
+#define DSIM_AFC_CTL(x)			(((x) & 0x7) << 5)
+
+/* DSIM_PLLCTRL */
+#define DSIM_FREQ_BAND(x)		((x) << 24)
+#define DSIM_PLL_EN			(1 << 23)
+#define DSIM_PLL_P(x)			((x) << 13)
+#define DSIM_PLL_M(x)			((x) << 4)
+#define DSIM_PLL_S(x)			((x) << 1)
+
+/* DSIM_PHYCTRL */
+#define DSIM_PHYCTRL_ULPS_EXIT(x)	(((x) & 0x1ff) << 0)
+#define DSIM_PHYCTRL_B_DPHYCTL_VREG_LP	(1 << 30)
+#define DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP	(1 << 14)
+
+/* DSIM_PHYTIMING */
+#define DSIM_PHYTIMING_LPX(x)		((x) << 8)
+#define DSIM_PHYTIMING_HS_EXIT(x)	((x) << 0)
+
+/* DSIM_PHYTIMING1 */
+#define DSIM_PHYTIMING1_CLK_PREPARE(x)	((x) << 24)
+#define DSIM_PHYTIMING1_CLK_ZERO(x)	((x) << 16)
+#define DSIM_PHYTIMING1_CLK_POST(x)	((x) << 8)
+#define DSIM_PHYTIMING1_CLK_TRAIL(x)	((x) << 0)
+
+/* DSIM_PHYTIMING2 */
+#define DSIM_PHYTIMING2_HS_PREPARE(x)	((x) << 16)
+#define DSIM_PHYTIMING2_HS_ZERO(x)	((x) << 8)
+#define DSIM_PHYTIMING2_HS_TRAIL(x)	((x) << 0)
+
+#define DSI_MAX_BUS_WIDTH		4
+#define DSI_NUM_VIRTUAL_CHANNELS	4
+#define DSI_TX_FIFO_SIZE		2048
+#define DSI_RX_FIFO_SIZE		256
+#define DSI_XFER_TIMEOUT_MS		100
+#define DSI_RX_FIFO_EMPTY		0x30800002
+
+#define OLD_SCLK_MIPI_CLK_NAME "pll_clk"
+
+static const char *const clk_names[5] = { "bus_clk", "sclk_mipi",
+	"phyclk_mipidphy0_bitclkdiv8", "phyclk_mipidphy0_rxclkesc0",
+	"sclk_rgb_vclk_to_dsim0" };
+
+enum samsung_dsim_transfer_type {
+	EXYNOS_DSI_TX,
+	EXYNOS_DSI_RX,
+};
+
+struct samsung_dsim_transfer {
+	struct list_head list;
+	struct completion completed;
+	int result;
+	struct mipi_dsi_packet packet;
+	u16 flags;
+	u16 tx_done;
+
+	u8 *rx_payload;
+	u16 rx_len;
+	u16 rx_done;
+};
+
+#define DSIM_STATE_ENABLED		BIT(0)
+#define DSIM_STATE_INITIALIZED		BIT(1)
+#define DSIM_STATE_CMD_LPM		BIT(2)
+#define DSIM_STATE_VIDOUT_AVAILABLE	BIT(3)
+
+struct samsung_dsim {
+	struct drm_bridge bridge;
+	struct mipi_dsi_host dsi_host;
+	struct drm_connector connector;
+	struct drm_panel *panel;
+	struct list_head bridge_chain;
+	struct drm_bridge *out_bridge;
+	struct device *dev;
+
+	void __iomem *reg_base;
+	struct phy *phy;
+	struct clk **clks;
+	struct regulator_bulk_data supplies[2];
+	int irq;
+	int te_gpio;
+
+	u32 pll_clk_rate;
+	u32 burst_clk_rate;
+	u32 esc_clk_rate;
+	u32 lanes;
+	u32 mode_flags;
+	u32 format;
+
+	struct drm_display_mode mode;
+
+	int state;
+	struct drm_property *brightness;
+	struct completion completed;
+
+	spinlock_t transfer_lock; /* protects transfer_list */
+	struct list_head transfer_list;
+
+	const struct samsung_dsim_driver_data *driver_data;
+};
+
+#define host_to_dsi(host) container_of(host, struct samsung_dsim, dsi_host)
+#define connector_to_dsi(c) container_of(c, struct samsung_dsim, connector)
+
+enum reg_idx {
+	DSIM_STATUS_REG,	/* Status register */
+	DSIM_SWRST_REG,		/* Software reset register */
+	DSIM_CLKCTRL_REG,	/* Clock control register */
+	DSIM_TIMEOUT_REG,	/* Time out register */
+	DSIM_CONFIG_REG,	/* Configuration register */
+	DSIM_ESCMODE_REG,	/* Escape mode register */
+	DSIM_MDRESOL_REG,
+	DSIM_MVPORCH_REG,	/* Main display Vporch register */
+	DSIM_MHPORCH_REG,	/* Main display Hporch register */
+	DSIM_MSYNC_REG,		/* Main display sync area register */
+	DSIM_INTSRC_REG,	/* Interrupt source register */
+	DSIM_INTMSK_REG,	/* Interrupt mask register */
+	DSIM_PKTHDR_REG,	/* Packet Header FIFO register */
+	DSIM_PAYLOAD_REG,	/* Payload FIFO register */
+	DSIM_RXFIFO_REG,	/* Read FIFO register */
+	DSIM_FIFOCTRL_REG,	/* FIFO status and control register */
+	DSIM_PLLCTRL_REG,	/* PLL control register */
+	DSIM_PHYCTRL_REG,
+	DSIM_PHYTIMING_REG,
+	DSIM_PHYTIMING1_REG,
+	DSIM_PHYTIMING2_REG,
+	NUM_REGS
+};
+
+static const unsigned int exynos_reg_ofs[] = {
+	[DSIM_STATUS_REG] =  0x00,
+	[DSIM_SWRST_REG] =  0x04,
+	[DSIM_CLKCTRL_REG] =  0x08,
+	[DSIM_TIMEOUT_REG] =  0x0c,
+	[DSIM_CONFIG_REG] =  0x10,
+	[DSIM_ESCMODE_REG] =  0x14,
+	[DSIM_MDRESOL_REG] =  0x18,
+	[DSIM_MVPORCH_REG] =  0x1c,
+	[DSIM_MHPORCH_REG] =  0x20,
+	[DSIM_MSYNC_REG] =  0x24,
+	[DSIM_INTSRC_REG] =  0x2c,
+	[DSIM_INTMSK_REG] =  0x30,
+	[DSIM_PKTHDR_REG] =  0x34,
+	[DSIM_PAYLOAD_REG] =  0x38,
+	[DSIM_RXFIFO_REG] =  0x3c,
+	[DSIM_FIFOCTRL_REG] =  0x44,
+	[DSIM_PLLCTRL_REG] =  0x4c,
+	[DSIM_PHYCTRL_REG] =  0x5c,
+	[DSIM_PHYTIMING_REG] =  0x64,
+	[DSIM_PHYTIMING1_REG] =  0x68,
+	[DSIM_PHYTIMING2_REG] =  0x6c,
+};
+
+static const unsigned int exynos5433_reg_ofs[] = {
+	[DSIM_STATUS_REG] = 0x04,
+	[DSIM_SWRST_REG] = 0x0C,
+	[DSIM_CLKCTRL_REG] = 0x10,
+	[DSIM_TIMEOUT_REG] = 0x14,
+	[DSIM_CONFIG_REG] = 0x18,
+	[DSIM_ESCMODE_REG] = 0x1C,
+	[DSIM_MDRESOL_REG] = 0x20,
+	[DSIM_MVPORCH_REG] = 0x24,
+	[DSIM_MHPORCH_REG] = 0x28,
+	[DSIM_MSYNC_REG] = 0x2C,
+	[DSIM_INTSRC_REG] = 0x34,
+	[DSIM_INTMSK_REG] = 0x38,
+	[DSIM_PKTHDR_REG] = 0x3C,
+	[DSIM_PAYLOAD_REG] = 0x40,
+	[DSIM_RXFIFO_REG] = 0x44,
+	[DSIM_FIFOCTRL_REG] = 0x4C,
+	[DSIM_PLLCTRL_REG] = 0x94,
+	[DSIM_PHYCTRL_REG] = 0xA4,
+	[DSIM_PHYTIMING_REG] = 0xB4,
+	[DSIM_PHYTIMING1_REG] = 0xB8,
+	[DSIM_PHYTIMING2_REG] = 0xBC,
+};
+
+static inline void samsung_dsim_write(struct samsung_dsim *dsi,
+				      enum reg_idx idx, u32 val)
+{
+	const unsigned int *reg_ofs;
+
+	if (dsi->driver_data->reg_ofs == EXYNOS5433_REG_OFS)
+		reg_ofs = exynos5433_reg_ofs;
+	else
+		reg_ofs = exynos_reg_ofs;
+
+	writel(val, dsi->reg_base + reg_ofs[idx]);
+}
+
+static inline u32 samsung_dsim_read(struct samsung_dsim *dsi, enum reg_idx idx)
+{
+	const unsigned int *reg_ofs;
+
+	if (dsi->driver_data->reg_ofs == EXYNOS5433_REG_OFS)
+		reg_ofs = exynos5433_reg_ofs;
+	else
+		reg_ofs = exynos_reg_ofs;
+
+	return readl(dsi->reg_base + reg_ofs[idx]);
+}
+
+static void samsung_dsim_wait_for_reset(struct samsung_dsim *dsi)
+{
+	if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300)))
+		return;
+
+	dev_err(dsi->dev, "timeout waiting for reset\n");
+}
+
+static void samsung_dsim_reset(struct samsung_dsim *dsi)
+{
+	u32 reset_val = dsi->driver_data->reg_values[RESET_TYPE];
+
+	reinit_completion(&dsi->completed);
+	samsung_dsim_write(dsi, DSIM_SWRST_REG, reset_val);
+}
+
+#ifndef MHZ
+#define MHZ	(1000*1000)
+#endif
+
+static unsigned long samsung_dsim_pll_find_pms(struct samsung_dsim *dsi,
+					       unsigned long fin,
+					       unsigned long fout,
+					       u8 *p, u16 *m, u8 *s)
+{
+	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
+	unsigned long best_freq = 0;
+	u32 min_delta = 0xffffffff;
+	u8 p_min, p_max;
+	u8 _p, best_p;
+	u16 _m, best_m;
+	u8 _s, best_s;
+
+	p_min = DIV_ROUND_UP(fin, (12 * MHZ));
+	p_max = fin / (6 * MHZ);
+
+	for (_p = p_min; _p <= p_max; ++_p) {
+		for (_s = 0; _s <= 5; ++_s) {
+			u64 tmp;
+			u32 delta;
+
+			tmp = (u64)fout * (_p << _s);
+			do_div(tmp, fin);
+			_m = tmp;
+			if (_m < 41 || _m > 125)
+				continue;
+
+			tmp = (u64)_m * fin;
+			do_div(tmp, _p);
+			if (tmp < 500 * MHZ ||
+					tmp > driver_data->max_freq * MHZ)
+				continue;
+
+			tmp = (u64)_m * fin;
+			do_div(tmp, _p << _s);
+
+			delta = abs(fout - tmp);
+			if (delta < min_delta) {
+				best_p = _p;
+				best_m = _m;
+				best_s = _s;
+				min_delta = delta;
+				best_freq = tmp;
+			}
+		}
+	}
+
+	if (best_freq) {
+		*p = best_p;
+		*m = best_m;
+		*s = best_s;
+	}
+
+	return best_freq;
+}
+
+static unsigned long samsung_dsim_set_pll(struct samsung_dsim *dsi,
+					  unsigned long freq)
+{
+	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
+	unsigned long fin, fout;
+	int timeout;
+	u8 p, s;
+	u16 m;
+	u32 reg;
+
+	fin = dsi->pll_clk_rate;
+	fout = samsung_dsim_pll_find_pms(dsi, fin, freq, &p, &m, &s);
+	if (!fout) {
+		dev_err(dsi->dev,
+			"failed to find PLL PMS for requested frequency\n");
+		return 0;
+	}
+	dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s);
+
+	writel(driver_data->reg_values[PLL_TIMER],
+			dsi->reg_base + driver_data->plltmr_reg);
+
+	reg = DSIM_PLL_EN | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s);
+
+	if (driver_data->has_freqband) {
+		static const unsigned long freq_bands[] = {
+			100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ,
+			270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ,
+			510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ,
+			770 * MHZ, 870 * MHZ, 950 * MHZ,
+		};
+		int band;
+
+		for (band = 0; band < ARRAY_SIZE(freq_bands); ++band)
+			if (fout < freq_bands[band])
+				break;
+
+		dev_dbg(dsi->dev, "band %d\n", band);
+
+		reg |= DSIM_FREQ_BAND(band);
+	}
+
+	samsung_dsim_write(dsi, DSIM_PLLCTRL_REG, reg);
+
+	timeout = 1000;
+	do {
+		if (timeout-- == 0) {
+			dev_err(dsi->dev, "PLL failed to stabilize\n");
+			return 0;
+		}
+		reg = samsung_dsim_read(dsi, DSIM_STATUS_REG);
+	} while ((reg & DSIM_PLL_STABLE) == 0);
+
+	return fout;
+}
+
+static int samsung_dsim_enable_clock(struct samsung_dsim *dsi)
+{
+	unsigned long hs_clk, byte_clk, esc_clk;
+	unsigned long esc_div;
+	u32 reg;
+
+	hs_clk = samsung_dsim_set_pll(dsi, dsi->burst_clk_rate);
+	if (!hs_clk) {
+		dev_err(dsi->dev, "failed to configure DSI PLL\n");
+		return -EFAULT;
+	}
+
+	byte_clk = hs_clk / 8;
+	esc_div = DIV_ROUND_UP(byte_clk, dsi->esc_clk_rate);
+	esc_clk = byte_clk / esc_div;
+
+	if (esc_clk > 20 * MHZ) {
+		++esc_div;
+		esc_clk = byte_clk / esc_div;
+	}
+
+	dev_dbg(dsi->dev, "hs_clk = %lu, byte_clk = %lu, esc_clk = %lu\n",
+		hs_clk, byte_clk, esc_clk);
+
+	reg = samsung_dsim_read(dsi, DSIM_CLKCTRL_REG);
+	reg &= ~(DSIM_ESC_PRESCALER_MASK | DSIM_LANE_ESC_CLK_EN_CLK
+			| DSIM_LANE_ESC_CLK_EN_DATA_MASK | DSIM_PLL_BYPASS
+			| DSIM_BYTE_CLK_SRC_MASK);
+	reg |= DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN
+			| DSIM_ESC_PRESCALER(esc_div)
+			| DSIM_LANE_ESC_CLK_EN_CLK
+			| DSIM_LANE_ESC_CLK_EN_DATA(BIT(dsi->lanes) - 1)
+			| DSIM_BYTE_CLK_SRC(0)
+			| DSIM_TX_REQUEST_HSCLK;
+	samsung_dsim_write(dsi, DSIM_CLKCTRL_REG, reg);
+
+	return 0;
+}
+
+static void samsung_dsim_set_phy_ctrl(struct samsung_dsim *dsi)
+{
+	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
+	const unsigned int *reg_values = driver_data->reg_values;
+	u32 reg;
+
+	if (driver_data->has_freqband)
+		return;
+
+	/* B D-PHY: D-PHY Master & Slave Analog Block control */
+	reg = DSIM_PHYCTRL_ULPS_EXIT(reg_values[PHYCTRL_ULPS_EXIT]);
+	if (reg_values[PHYCTRL_VREG_LP])
+		reg |= DSIM_PHYCTRL_B_DPHYCTL_VREG_LP;
+	if (reg_values[PHYCTRL_SLEW_UP])
+		reg |= DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP;
+	samsung_dsim_write(dsi, DSIM_PHYCTRL_REG, reg);
+
+	/*
+	 * T LPX: Transmitted length of any Low-Power state period
+	 * T HS-EXIT: Time that the transmitter drives LP-11 following a HS
+	 *	burst
+	 */
+	reg = DSIM_PHYTIMING_LPX(reg_values[PHYTIMING_LPX]) |
+		DSIM_PHYTIMING_HS_EXIT(reg_values[PHYTIMING_HS_EXIT]);
+	samsung_dsim_write(dsi, DSIM_PHYTIMING_REG, reg);
+
+	/*
+	 * T CLK-PREPARE: Time that the transmitter drives the Clock Lane LP-00
+	 *	Line state immediately before the HS-0 Line state starting the
+	 *	HS transmission
+	 * T CLK-ZERO: Time that the transmitter drives the HS-0 state prior to
+	 *	transmitting the Clock.
+	 * T CLK_POST: Time that the transmitter continues to send HS clock
+	 *	after the last associated Data Lane has transitioned to LP Mode
+	 *	Interval is defined as the period from the end of T HS-TRAIL to
+	 *	the beginning of T CLK-TRAIL
+	 * T CLK-TRAIL: Time that the transmitter drives the HS-0 state after
+	 *	the last payload clock bit of a HS transmission burst
+	 */
+	reg = DSIM_PHYTIMING1_CLK_PREPARE(reg_values[PHYTIMING_CLK_PREPARE]) |
+		DSIM_PHYTIMING1_CLK_ZERO(reg_values[PHYTIMING_CLK_ZERO]) |
+		DSIM_PHYTIMING1_CLK_POST(reg_values[PHYTIMING_CLK_POST]) |
+		DSIM_PHYTIMING1_CLK_TRAIL(reg_values[PHYTIMING_CLK_TRAIL]);
+	samsung_dsim_write(dsi, DSIM_PHYTIMING1_REG, reg);
+
+	/*
+	 * T HS-PREPARE: Time that the transmitter drives the Data Lane LP-00
+	 *	Line state immediately before the HS-0 Line state starting the
+	 *	HS transmission
+	 * T HS-ZERO: Time that the transmitter drives the HS-0 state prior to
+	 *	transmitting the Sync sequence.
+	 * T HS-TRAIL: Time that the transmitter drives the flipped differential
+	 *	state after last payload data bit of a HS transmission burst
+	 */
+	reg = DSIM_PHYTIMING2_HS_PREPARE(reg_values[PHYTIMING_HS_PREPARE]) |
+		DSIM_PHYTIMING2_HS_ZERO(reg_values[PHYTIMING_HS_ZERO]) |
+		DSIM_PHYTIMING2_HS_TRAIL(reg_values[PHYTIMING_HS_TRAIL]);
+	samsung_dsim_write(dsi, DSIM_PHYTIMING2_REG, reg);
+}
+
+static void samsung_dsim_disable_clock(struct samsung_dsim *dsi)
+{
+	u32 reg;
+
+	reg = samsung_dsim_read(dsi, DSIM_CLKCTRL_REG);
+	reg &= ~(DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK
+			| DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN);
+	samsung_dsim_write(dsi, DSIM_CLKCTRL_REG, reg);
+
+	reg = samsung_dsim_read(dsi, DSIM_PLLCTRL_REG);
+	reg &= ~DSIM_PLL_EN;
+	samsung_dsim_write(dsi, DSIM_PLLCTRL_REG, reg);
+}
+
+static void samsung_dsim_enable_lane(struct samsung_dsim *dsi, u32 lane)
+{
+	u32 reg = samsung_dsim_read(dsi, DSIM_CONFIG_REG);
+	reg |= (DSIM_NUM_OF_DATA_LANE(dsi->lanes - 1) | DSIM_LANE_EN_CLK |
+			DSIM_LANE_EN(lane));
+	samsung_dsim_write(dsi, DSIM_CONFIG_REG, reg);
+}
+
+static int samsung_dsim_init_link(struct samsung_dsim *dsi)
+{
+	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
+	int timeout;
+	u32 reg;
+	u32 lanes_mask;
+
+	/* Initialize FIFO pointers */
+	reg = samsung_dsim_read(dsi, DSIM_FIFOCTRL_REG);
+	reg &= ~0x1f;
+	samsung_dsim_write(dsi, DSIM_FIFOCTRL_REG, reg);
+
+	usleep_range(9000, 11000);
+
+	reg |= 0x1f;
+	samsung_dsim_write(dsi, DSIM_FIFOCTRL_REG, reg);
+	usleep_range(9000, 11000);
+
+	/* DSI configuration */
+	reg = 0;
+
+	/*
+	 * The first bit of mode_flags specifies display configuration.
+	 * If this bit is set[= MIPI_DSI_MODE_VIDEO], dsi will support video
+	 * mode, otherwise it will support command mode.
+	 */
+	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
+		reg |= DSIM_VIDEO_MODE;
+
+		/*
+		 * The user manual describes that following bits are ignored in
+		 * command mode.
+		 */
+		if (!(dsi->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH))
+			reg |= DSIM_MFLUSH_VS;
+		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+			reg |= DSIM_SYNC_INFORM;
+		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+			reg |= DSIM_BURST_MODE;
+		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT)
+			reg |= DSIM_AUTO_MODE;
+		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSE)
+			reg |= DSIM_HSE_MODE;
+		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HFP))
+			reg |= DSIM_HFP_MODE;
+		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HBP))
+			reg |= DSIM_HBP_MODE;
+		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSA))
+			reg |= DSIM_HSA_MODE;
+	}
+
+	if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET))
+		reg |= DSIM_EOT_DISABLE;
+
+	switch (dsi->format) {
+	case MIPI_DSI_FMT_RGB888:
+		reg |= DSIM_MAIN_PIX_FORMAT_RGB888;
+		break;
+	case MIPI_DSI_FMT_RGB666:
+		reg |= DSIM_MAIN_PIX_FORMAT_RGB666;
+		break;
+	case MIPI_DSI_FMT_RGB666_PACKED:
+		reg |= DSIM_MAIN_PIX_FORMAT_RGB666_P;
+		break;
+	case MIPI_DSI_FMT_RGB565:
+		reg |= DSIM_MAIN_PIX_FORMAT_RGB565;
+		break;
+	default:
+		dev_err(dsi->dev, "invalid pixel format\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Use non-continuous clock mode if the periparal wants and
+	 * host controller supports
+	 *
+	 * In non-continous clock mode, host controller will turn off
+	 * the HS clock between high-speed transmissions to reduce
+	 * power consumption.
+	 */
+	if (driver_data->has_clklane_stop &&
+			dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
+		reg |= DSIM_CLKLANE_STOP;
+	}
+	samsung_dsim_write(dsi, DSIM_CONFIG_REG, reg);
+
+	lanes_mask = BIT(dsi->lanes) - 1;
+	samsung_dsim_enable_lane(dsi, lanes_mask);
+
+	/* Check clock and data lane state are stop state */
+	timeout = 100;
+	do {
+		if (timeout-- == 0) {
+			dev_err(dsi->dev, "waiting for bus lanes timed out\n");
+			return -EFAULT;
+		}
+
+		reg = samsung_dsim_read(dsi, DSIM_STATUS_REG);
+		if ((reg & DSIM_STOP_STATE_DAT(lanes_mask))
+		    != DSIM_STOP_STATE_DAT(lanes_mask))
+			continue;
+	} while (!(reg & (DSIM_STOP_STATE_CLK | DSIM_TX_READY_HS_CLK)));
+
+	reg = samsung_dsim_read(dsi, DSIM_ESCMODE_REG);
+	reg &= ~DSIM_STOP_STATE_CNT_MASK;
+	reg |= DSIM_STOP_STATE_CNT(driver_data->reg_values[STOP_STATE_CNT]);
+	samsung_dsim_write(dsi, DSIM_ESCMODE_REG, reg);
+
+	reg = DSIM_BTA_TIMEOUT(0xff) | DSIM_LPDR_TIMEOUT(0xffff);
+	samsung_dsim_write(dsi, DSIM_TIMEOUT_REG, reg);
+
+	return 0;
+}
+
+static void samsung_dsim_set_display_mode(struct samsung_dsim *dsi)
+{
+	struct drm_display_mode *m = &dsi->mode;
+	unsigned int num_bits_resol = dsi->driver_data->num_bits_resol;
+	u32 reg;
+
+	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
+		reg = DSIM_CMD_ALLOW(0xf)
+			| DSIM_STABLE_VFP(m->vsync_start - m->vdisplay)
+			| DSIM_MAIN_VBP(m->vtotal - m->vsync_end);
+		samsung_dsim_write(dsi, DSIM_MVPORCH_REG, reg);
+
+		reg = DSIM_MAIN_HFP(m->hsync_start - m->hdisplay)
+			| DSIM_MAIN_HBP(m->htotal - m->hsync_end);
+		samsung_dsim_write(dsi, DSIM_MHPORCH_REG, reg);
+
+		reg = DSIM_MAIN_VSA(m->vsync_end - m->vsync_start)
+			| DSIM_MAIN_HSA(m->hsync_end - m->hsync_start);
+		samsung_dsim_write(dsi, DSIM_MSYNC_REG, reg);
+	}
+	reg =  DSIM_MAIN_HRESOL(m->hdisplay, num_bits_resol) |
+		DSIM_MAIN_VRESOL(m->vdisplay, num_bits_resol);
+
+	samsung_dsim_write(dsi, DSIM_MDRESOL_REG, reg);
+
+	dev_dbg(dsi->dev, "LCD size = %dx%d\n", m->hdisplay, m->vdisplay);
+}
+
+static void samsung_dsim_set_display_enable(struct samsung_dsim *dsi,
+					    bool enable)
+{
+	u32 reg;
+
+	reg = samsung_dsim_read(dsi, DSIM_MDRESOL_REG);
+	if (enable)
+		reg |= DSIM_MAIN_STAND_BY;
+	else
+		reg &= ~DSIM_MAIN_STAND_BY;
+	samsung_dsim_write(dsi, DSIM_MDRESOL_REG, reg);
+}
+
+static int samsung_dsim_wait_for_hdr_fifo(struct samsung_dsim *dsi)
+{
+	int timeout = 2000;
+
+	do {
+		u32 reg = samsung_dsim_read(dsi, DSIM_FIFOCTRL_REG);
+
+		if (!(reg & DSIM_SFR_HEADER_FULL))
+			return 0;
+
+		if (!cond_resched())
+			usleep_range(950, 1050);
+	} while (--timeout);
+
+	return -ETIMEDOUT;
+}
+
+static void samsung_dsim_set_cmd_lpm(struct samsung_dsim *dsi, bool lpm)
+{
+	u32 v = samsung_dsim_read(dsi, DSIM_ESCMODE_REG);
+
+	if (lpm)
+		v |= DSIM_CMD_LPDT_LP;
+	else
+		v &= ~DSIM_CMD_LPDT_LP;
+
+	samsung_dsim_write(dsi, DSIM_ESCMODE_REG, v);
+}
+
+static void samsung_dsim_force_bta(struct samsung_dsim *dsi)
+{
+	u32 v = samsung_dsim_read(dsi, DSIM_ESCMODE_REG);
+	v |= DSIM_FORCE_BTA;
+	samsung_dsim_write(dsi, DSIM_ESCMODE_REG, v);
+}
+
+static void samsung_dsim_send_to_fifo(struct samsung_dsim *dsi,
+				      struct samsung_dsim_transfer *xfer)
+{
+	struct device *dev = dsi->dev;
+	struct mipi_dsi_packet *pkt = &xfer->packet;
+	const u8 *payload = pkt->payload + xfer->tx_done;
+	u16 length = pkt->payload_length - xfer->tx_done;
+	bool first = !xfer->tx_done;
+	u32 reg;
+
+	dev_dbg(dev, "< xfer %pK: tx len %u, done %u, rx len %u, done %u\n",
+		xfer, length, xfer->tx_done, xfer->rx_len, xfer->rx_done);
+
+	if (length > DSI_TX_FIFO_SIZE)
+		length = DSI_TX_FIFO_SIZE;
+
+	xfer->tx_done += length;
+
+	/* Send payload */
+	while (length >= 4) {
+		reg = get_unaligned_le32(payload);
+		samsung_dsim_write(dsi, DSIM_PAYLOAD_REG, reg);
+		payload += 4;
+		length -= 4;
+	}
+
+	reg = 0;
+	switch (length) {
+	case 3:
+		reg |= payload[2] << 16;
+		fallthrough;
+	case 2:
+		reg |= payload[1] << 8;
+		fallthrough;
+	case 1:
+		reg |= payload[0];
+		samsung_dsim_write(dsi, DSIM_PAYLOAD_REG, reg);
+		break;
+	}
+
+	/* Send packet header */
+	if (!first)
+		return;
+
+	reg = get_unaligned_le32(pkt->header);
+	if (samsung_dsim_wait_for_hdr_fifo(dsi)) {
+		dev_err(dev, "waiting for header FIFO timed out\n");
+		return;
+	}
+
+	if (NEQV(xfer->flags & MIPI_DSI_MSG_USE_LPM,
+		 dsi->state & DSIM_STATE_CMD_LPM)) {
+		samsung_dsim_set_cmd_lpm(dsi, xfer->flags & MIPI_DSI_MSG_USE_LPM);
+		dsi->state ^= DSIM_STATE_CMD_LPM;
+	}
+
+	samsung_dsim_write(dsi, DSIM_PKTHDR_REG, reg);
+
+	if (xfer->flags & MIPI_DSI_MSG_REQ_ACK)
+		samsung_dsim_force_bta(dsi);
+}
+
+static void samsung_dsim_read_from_fifo(struct samsung_dsim *dsi,
+					struct samsung_dsim_transfer *xfer)
+{
+	u8 *payload = xfer->rx_payload + xfer->rx_done;
+	bool first = !xfer->rx_done;
+	struct device *dev = dsi->dev;
+	u16 length;
+	u32 reg;
+
+	if (first) {
+		reg = samsung_dsim_read(dsi, DSIM_RXFIFO_REG);
+
+		switch (reg & 0x3f) {
+		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
+		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
+			if (xfer->rx_len >= 2) {
+				payload[1] = reg >> 16;
+				++xfer->rx_done;
+			}
+			fallthrough;
+		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
+		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
+			payload[0] = reg >> 8;
+			++xfer->rx_done;
+			xfer->rx_len = xfer->rx_done;
+			xfer->result = 0;
+			goto clear_fifo;
+		case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
+			dev_err(dev, "DSI Error Report: 0x%04x\n",
+				(reg >> 8) & 0xffff);
+			xfer->result = 0;
+			goto clear_fifo;
+		}
+
+		length = (reg >> 8) & 0xffff;
+		if (length > xfer->rx_len) {
+			dev_err(dev,
+				"response too long (%u > %u bytes), stripping\n",
+				xfer->rx_len, length);
+			length = xfer->rx_len;
+		} else if (length < xfer->rx_len)
+			xfer->rx_len = length;
+	}
+
+	length = xfer->rx_len - xfer->rx_done;
+	xfer->rx_done += length;
+
+	/* Receive payload */
+	while (length >= 4) {
+		reg = samsung_dsim_read(dsi, DSIM_RXFIFO_REG);
+		payload[0] = (reg >>  0) & 0xff;
+		payload[1] = (reg >>  8) & 0xff;
+		payload[2] = (reg >> 16) & 0xff;
+		payload[3] = (reg >> 24) & 0xff;
+		payload += 4;
+		length -= 4;
+	}
+
+	if (length) {
+		reg = samsung_dsim_read(dsi, DSIM_RXFIFO_REG);
+		switch (length) {
+		case 3:
+			payload[2] = (reg >> 16) & 0xff;
+			fallthrough;
+		case 2:
+			payload[1] = (reg >> 8) & 0xff;
+			fallthrough;
+		case 1:
+			payload[0] = reg & 0xff;
+		}
+	}
+
+	if (xfer->rx_done == xfer->rx_len)
+		xfer->result = 0;
+
+clear_fifo:
+	length = DSI_RX_FIFO_SIZE / 4;
+	do {
+		reg = samsung_dsim_read(dsi, DSIM_RXFIFO_REG);
+		if (reg == DSI_RX_FIFO_EMPTY)
+			break;
+	} while (--length);
+}
+
+static void samsung_dsim_transfer_start(struct samsung_dsim *dsi)
+{
+	unsigned long flags;
+	struct samsung_dsim_transfer *xfer;
+	bool start = false;
+
+again:
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	if (list_empty(&dsi->transfer_list)) {
+		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+		return;
+	}
+
+	xfer = list_first_entry(&dsi->transfer_list,
+					struct samsung_dsim_transfer, list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+	if (xfer->packet.payload_length &&
+	    xfer->tx_done == xfer->packet.payload_length)
+		/* waiting for RX */
+		return;
+
+	samsung_dsim_send_to_fifo(dsi, xfer);
+
+	if (xfer->packet.payload_length || xfer->rx_len)
+		return;
+
+	xfer->result = 0;
+	complete(&xfer->completed);
+
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	list_del_init(&xfer->list);
+	start = !list_empty(&dsi->transfer_list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+	if (start)
+		goto again;
+}
+
+static bool samsung_dsim_transfer_finish(struct samsung_dsim *dsi)
+{
+	struct samsung_dsim_transfer *xfer;
+	unsigned long flags;
+	bool start = true;
+
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	if (list_empty(&dsi->transfer_list)) {
+		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+		return false;
+	}
+
+	xfer = list_first_entry(&dsi->transfer_list,
+					struct samsung_dsim_transfer, list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+	dev_dbg(dsi->dev,
+		"> xfer %pK, tx_len %zu, tx_done %u, rx_len %u, rx_done %u\n",
+		xfer, xfer->packet.payload_length, xfer->tx_done, xfer->rx_len,
+		xfer->rx_done);
+
+	if (xfer->tx_done != xfer->packet.payload_length)
+		return true;
+
+	if (xfer->rx_done != xfer->rx_len)
+		samsung_dsim_read_from_fifo(dsi, xfer);
+
+	if (xfer->rx_done != xfer->rx_len)
+		return true;
+
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	list_del_init(&xfer->list);
+	start = !list_empty(&dsi->transfer_list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+	if (!xfer->rx_len)
+		xfer->result = 0;
+	complete(&xfer->completed);
+
+	return start;
+}
+
+static void samsung_dsim_remove_transfer(struct samsung_dsim *dsi,
+					 struct samsung_dsim_transfer *xfer)
+{
+	unsigned long flags;
+	bool start;
+
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	if (!list_empty(&dsi->transfer_list) &&
+	    xfer == list_first_entry(&dsi->transfer_list,
+				     struct samsung_dsim_transfer, list)) {
+		list_del_init(&xfer->list);
+		start = !list_empty(&dsi->transfer_list);
+		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+		if (start)
+			samsung_dsim_transfer_start(dsi);
+		return;
+	}
+
+	list_del_init(&xfer->list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+}
+
+static int samsung_dsim_transfer(struct samsung_dsim *dsi,
+				 struct samsung_dsim_transfer *xfer)
+{
+	unsigned long flags;
+	bool stopped;
+
+	xfer->tx_done = 0;
+	xfer->rx_done = 0;
+	xfer->result = -ETIMEDOUT;
+	init_completion(&xfer->completed);
+
+	spin_lock_irqsave(&dsi->transfer_lock, flags);
+
+	stopped = list_empty(&dsi->transfer_list);
+	list_add_tail(&xfer->list, &dsi->transfer_list);
+
+	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
+
+	if (stopped)
+		samsung_dsim_transfer_start(dsi);
+
+	wait_for_completion_timeout(&xfer->completed,
+				    msecs_to_jiffies(DSI_XFER_TIMEOUT_MS));
+	if (xfer->result == -ETIMEDOUT) {
+		struct mipi_dsi_packet *pkt = &xfer->packet;
+		samsung_dsim_remove_transfer(dsi, xfer);
+		dev_err(dsi->dev, "xfer timed out: %*ph %*ph\n", 4, pkt->header,
+			(int)pkt->payload_length, pkt->payload);
+		return -ETIMEDOUT;
+	}
+
+	/* Also covers hardware timeout condition */
+	return xfer->result;
+}
+
+static irqreturn_t samsung_dsim_irq(int irq, void *dev_id)
+{
+	struct samsung_dsim *dsi = dev_id;
+	u32 status;
+
+	status = samsung_dsim_read(dsi, DSIM_INTSRC_REG);
+	if (!status) {
+		static unsigned long int j;
+		if (printk_timed_ratelimit(&j, 500))
+			dev_warn(dsi->dev, "spurious interrupt\n");
+		return IRQ_HANDLED;
+	}
+	samsung_dsim_write(dsi, DSIM_INTSRC_REG, status);
+
+	if (status & DSIM_INT_SW_RST_RELEASE) {
+		u32 mask = ~(DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY |
+			DSIM_INT_SFR_HDR_FIFO_EMPTY | DSIM_INT_RX_ECC_ERR |
+			DSIM_INT_SW_RST_RELEASE);
+		samsung_dsim_write(dsi, DSIM_INTMSK_REG, mask);
+		complete(&dsi->completed);
+		return IRQ_HANDLED;
+	}
+
+	if (!(status & (DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY |
+			DSIM_INT_PLL_STABLE)))
+		return IRQ_HANDLED;
+
+	if (samsung_dsim_transfer_finish(dsi))
+		samsung_dsim_transfer_start(dsi);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t samsung_dsim_te_irq_handler(int irq, void *dev_id)
+{
+	struct samsung_dsim *dsi = dev_id;
+	const struct samsung_dsim_host_ops *ops = dsi->driver_data->host_ops;
+
+	if (ops && ops->te_handler &&
+	    (dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
+		ops->te_handler(dsi->dsi_host.dev);
+
+	return IRQ_HANDLED;
+}
+
+static void samsung_dsim_enable_irq(struct samsung_dsim *dsi)
+{
+	enable_irq(dsi->irq);
+
+	if (gpio_is_valid(dsi->te_gpio))
+		enable_irq(gpio_to_irq(dsi->te_gpio));
+}
+
+static void samsung_dsim_disable_irq(struct samsung_dsim *dsi)
+{
+	if (gpio_is_valid(dsi->te_gpio))
+		disable_irq(gpio_to_irq(dsi->te_gpio));
+
+	disable_irq(dsi->irq);
+}
+
+static int samsung_dsim_init(struct samsung_dsim *dsi)
+{
+	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
+
+	samsung_dsim_reset(dsi);
+	samsung_dsim_enable_irq(dsi);
+
+	if (driver_data->reg_values[RESET_TYPE] == DSIM_FUNCRST)
+		samsung_dsim_enable_lane(dsi, BIT(dsi->lanes) - 1);
+
+	samsung_dsim_enable_clock(dsi);
+	if (driver_data->wait_for_reset)
+		samsung_dsim_wait_for_reset(dsi);
+	samsung_dsim_set_phy_ctrl(dsi);
+	samsung_dsim_init_link(dsi);
+
+	return 0;
+}
+
+static int samsung_dsim_register_te_irq(struct samsung_dsim *dsi,
+					struct device *panel)
+{
+	int ret;
+	int te_gpio_irq;
+
+	dsi->te_gpio = of_get_named_gpio(panel->of_node, "te-gpios", 0);
+	if (dsi->te_gpio == -ENOENT)
+		return 0;
+
+	if (!gpio_is_valid(dsi->te_gpio)) {
+		ret = dsi->te_gpio;
+		dev_err(dsi->dev, "cannot get te-gpios, %d\n", ret);
+		goto out;
+	}
+
+	ret = gpio_request(dsi->te_gpio, "te_gpio");
+	if (ret) {
+		dev_err(dsi->dev, "gpio request failed with %d\n", ret);
+		goto out;
+	}
+
+	te_gpio_irq = gpio_to_irq(dsi->te_gpio);
+	irq_set_status_flags(te_gpio_irq, IRQ_NOAUTOEN);
+
+	ret = request_threaded_irq(te_gpio_irq, samsung_dsim_te_irq_handler,
+				   NULL, IRQF_TRIGGER_RISING, "TE", dsi);
+	if (ret) {
+		dev_err(dsi->dev, "request interrupt failed with %d\n", ret);
+		gpio_free(dsi->te_gpio);
+		goto out;
+	}
+
+out:
+	return ret;
+}
+
+static void samsung_dsim_unregister_te_irq(struct samsung_dsim *dsi)
+{
+	if (gpio_is_valid(dsi->te_gpio)) {
+		free_irq(gpio_to_irq(dsi->te_gpio), dsi);
+		gpio_free(dsi->te_gpio);
+		dsi->te_gpio = -ENOENT;
+	}
+}
+
+static void samsung_dsim_enable(struct samsung_dsim *dsi)
+{
+	struct drm_bridge *iter;
+	int ret;
+
+	if (dsi->state & DSIM_STATE_ENABLED)
+		return;
+
+	pm_runtime_get_sync(dsi->dev);
+	dsi->state |= DSIM_STATE_ENABLED;
+
+	if (dsi->panel) {
+		ret = drm_panel_prepare(dsi->panel);
+		if (ret < 0)
+			goto err_put_sync;
+	} else {
+		list_for_each_entry_reverse(iter, &dsi->bridge_chain,
+					    chain_node) {
+			if (iter->funcs->pre_enable)
+				iter->funcs->pre_enable(iter);
+		}
+	}
+
+	samsung_dsim_set_display_mode(dsi);
+	samsung_dsim_set_display_enable(dsi, true);
+
+	if (dsi->panel) {
+		ret = drm_panel_enable(dsi->panel);
+		if (ret < 0)
+			goto err_display_disable;
+	} else {
+		list_for_each_entry(iter, &dsi->bridge_chain, chain_node) {
+			if (iter->funcs->enable)
+				iter->funcs->enable(iter);
+		}
+	}
+
+	dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
+	return;
+
+err_display_disable:
+	samsung_dsim_set_display_enable(dsi, false);
+	drm_panel_unprepare(dsi->panel);
+
+err_put_sync:
+	dsi->state &= ~DSIM_STATE_ENABLED;
+	pm_runtime_put(dsi->dev);
+}
+
+static void samsung_dsim_disable(struct samsung_dsim *dsi)
+{
+	struct drm_bridge *iter;
+
+	if (!(dsi->state & DSIM_STATE_ENABLED))
+		return;
+
+	dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
+
+	drm_panel_disable(dsi->panel);
+
+	list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) {
+		if (iter->funcs->disable)
+			iter->funcs->disable(iter);
+	}
+
+	samsung_dsim_set_display_enable(dsi, false);
+	drm_panel_unprepare(dsi->panel);
+
+	list_for_each_entry(iter, &dsi->bridge_chain, chain_node) {
+		if (iter->funcs->post_disable)
+			iter->funcs->post_disable(iter);
+	}
+
+	dsi->state &= ~DSIM_STATE_ENABLED;
+	pm_runtime_put_sync(dsi->dev);
+}
+
+static enum drm_connector_status
+samsung_dsim_detect(struct drm_connector *connector, bool force)
+{
+	return connector->status;
+}
+
+static void samsung_dsim_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+	connector->dev = NULL;
+}
+
+static const struct drm_connector_funcs samsung_dsim_connector_funcs = {
+	.detect = samsung_dsim_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = samsung_dsim_connector_destroy,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int samsung_dsim_get_modes(struct drm_connector *connector)
+{
+	struct samsung_dsim *dsi = connector_to_dsi(connector);
+
+	if (dsi->panel)
+		return drm_panel_get_modes(dsi->panel, connector);
+
+	return 0;
+}
+
+static const struct drm_connector_helper_funcs samsung_dsim_connector_helper_funcs = {
+	.get_modes = samsung_dsim_get_modes,
+};
+
+static int samsung_dsim_create_connector(struct samsung_dsim *dsi)
+{
+	struct drm_connector *connector = &dsi->connector;
+	struct drm_device *drm = dsi->bridge.dev;
+	int ret;
+
+	connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+	ret = drm_connector_init(drm, connector, &samsung_dsim_connector_funcs,
+				 DRM_MODE_CONNECTOR_DSI);
+	if (ret) {
+		DRM_DEV_ERROR(dsi->dev,
+			      "Failed to initialize connector with drm\n");
+		return ret;
+	}
+
+	connector->status = connector_status_disconnected;
+	drm_connector_helper_add(connector, &samsung_dsim_connector_helper_funcs);
+	drm_connector_attach_encoder(connector, dsi->bridge.encoder);
+	if (!drm->registered)
+		return 0;
+
+	connector->funcs->reset(connector);
+	drm_connector_register(connector);
+	return 0;
+}
+
+static int samsung_dsim_bridge_attach(struct drm_bridge *bridge,
+				      enum drm_bridge_attach_flags flags)
+{
+	struct samsung_dsim *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 = samsung_dsim_create_connector(dsi);
+		if (ret)
+			return ret;
+
+		if (dsi->panel) {
+			dsi->connector.status = connector_status_connected;
+		}
+	}
+
+	return 0;
+}
+
+static void samsung_dsim_bridge_detach(struct drm_bridge *bridge)
+{
+	struct samsung_dsim *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);
+		samsung_dsim_disable(dsi);
+		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 void samsung_dsim_bridge_enable(struct drm_bridge *bridge)
+{
+	struct samsung_dsim *dsi = bridge->driver_private;
+
+	samsung_dsim_enable(dsi);
+}
+
+static void samsung_dsim_bridge_disable(struct drm_bridge *bridge)
+{
+	struct samsung_dsim *dsi = bridge->driver_private;
+
+	samsung_dsim_disable(dsi);
+}
+
+static void samsung_dsim_bridge_mode_set(struct drm_bridge *bridge,
+					 const struct drm_display_mode *mode,
+					 const struct drm_display_mode *adjusted_mode)
+{
+	struct samsung_dsim *dsi = bridge->driver_private;
+
+	/* The mode is set when actually enabling the device. */
+	drm_mode_copy(&dsi->mode, adjusted_mode);
+}
+
+static const struct drm_bridge_funcs samsung_dsim_bridge_funcs = {
+	.attach = samsung_dsim_bridge_attach,
+	.detach = samsung_dsim_bridge_detach,
+	.enable = samsung_dsim_bridge_enable,
+	.disable = samsung_dsim_bridge_disable,
+	.mode_set = samsung_dsim_bridge_mode_set,
+};
+
+static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
+				    struct mipi_dsi_device *device)
+{
+	struct samsung_dsim *dsi = host_to_dsi(host);
+	const struct samsung_dsim_host_ops *ops = dsi->driver_data->host_ops;
+	struct drm_bridge *out_bridge;
+
+	out_bridge = of_drm_find_bridge(device->dev.of_node);
+	if (out_bridge) {
+		dsi->out_bridge = out_bridge;
+	} else {
+		dsi->panel = of_drm_find_panel(device->dev.of_node);
+		if (IS_ERR(dsi->panel))
+			dsi->panel = NULL;
+		else
+			dsi->connector.status = connector_status_connected;
+	}
+
+	/*
+	 * This is a temporary solution and should be made by more generic way.
+	 *
+	 * If attached panel device is for command mode one, dsi should register
+	 * TE interrupt handler.
+	 */
+	if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO)) {
+		int ret = samsung_dsim_register_te_irq(dsi, &device->dev);
+		if (ret)
+			return ret;
+	}
+
+	dsi->lanes = device->lanes;
+	dsi->format = device->format;
+	dsi->mode_flags = device->mode_flags;
+
+	if (ops && ops->attach)
+		ops->attach(dsi->dsi_host.dev, device);
+
+	return 0;
+}
+
+static int samsung_dsim_host_detach(struct mipi_dsi_host *host,
+				    struct mipi_dsi_device *device)
+{
+	struct samsung_dsim *dsi = host_to_dsi(host);
+	const struct samsung_dsim_host_ops *ops = dsi->driver_data->host_ops;
+
+	samsung_dsim_unregister_te_irq(dsi);
+
+	if (ops && ops->detach)
+		ops->detach(dsi->dsi_host.dev, device);
+
+	samsung_dsim_unregister_te_irq(dsi);
+
+	return 0;
+}
+
+static ssize_t samsung_dsim_host_transfer(struct mipi_dsi_host *host,
+					  const struct mipi_dsi_msg *msg)
+{
+	struct samsung_dsim *dsi = host_to_dsi(host);
+	struct samsung_dsim_transfer xfer;
+	int ret;
+
+	if (!(dsi->state & DSIM_STATE_ENABLED))
+		return -EINVAL;
+
+	if (!(dsi->state & DSIM_STATE_INITIALIZED)) {
+		ret = samsung_dsim_init(dsi);
+		if (ret)
+			return ret;
+		dsi->state |= DSIM_STATE_INITIALIZED;
+	}
+
+	ret = mipi_dsi_create_packet(&xfer.packet, msg);
+	if (ret < 0)
+		return ret;
+
+	xfer.rx_len = msg->rx_len;
+	xfer.rx_payload = msg->rx_buf;
+	xfer.flags = msg->flags;
+
+	ret = samsung_dsim_transfer(dsi, &xfer);
+	return (ret < 0) ? ret : xfer.rx_done;
+}
+
+static const struct mipi_dsi_host_ops samsung_dsim_ops = {
+	.attach = samsung_dsim_host_attach,
+	.detach = samsung_dsim_host_detach,
+	.transfer = samsung_dsim_host_transfer,
+};
+
+static int samsung_dsim_of_read_u32(const struct device_node *np,
+				    const char *propname, u32 *out_value)
+{
+	int ret = of_property_read_u32(np, propname, out_value);
+
+	if (ret < 0)
+		pr_err("%pOF: failed to get '%s' property\n", np, propname);
+
+	return ret;
+}
+
+static int samsung_dsim_parse_dt(struct samsung_dsim *dsi)
+{
+	struct device *dev = dsi->dev;
+	struct device_node *node = dev->of_node;
+	int ret;
+
+	ret = samsung_dsim_of_read_u32(node, "samsung,pll-clock-frequency",
+				       &dsi->pll_clk_rate);
+	if (ret < 0)
+		return ret;
+
+	ret = samsung_dsim_of_read_u32(node, "samsung,burst-clock-frequency",
+				       &dsi->burst_clk_rate);
+	if (ret < 0)
+		return ret;
+
+	ret = samsung_dsim_of_read_u32(node, "samsung,esc-clock-frequency",
+				       &dsi->esc_clk_rate);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct samsung_dsim *__samsung_dsim_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct drm_bridge *bridge;
+	struct resource *res;
+	struct samsung_dsim *dsi;
+	int ret, i;
+
+	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
+	if (!dsi)
+		return ERR_PTR(-ENOMEM);
+
+	/* To be checked as invalid one */
+	dsi->te_gpio = -ENOENT;
+
+	init_completion(&dsi->completed);
+	spin_lock_init(&dsi->transfer_lock);
+	INIT_LIST_HEAD(&dsi->transfer_list);
+	INIT_LIST_HEAD(&dsi->bridge_chain);
+
+	dsi->dsi_host.ops = &samsung_dsim_ops;
+	dsi->dsi_host.dev = dev;
+
+	dsi->dev = dev;
+	dsi->driver_data = of_device_get_match_data(dev);
+
+	dsi->supplies[0].supply = "vddcore";
+	dsi->supplies[1].supply = "vddio";
+	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(dsi->supplies),
+				      dsi->supplies);
+	if (ret) {
+		if (ret != -EPROBE_DEFER)
+			dev_info(dev, "failed to get regulators: %d\n", ret);
+		return ERR_PTR(ret);
+	}
+
+	dsi->clks = devm_kcalloc(dev,
+			dsi->driver_data->num_clks, sizeof(*dsi->clks),
+			GFP_KERNEL);
+	if (!dsi->clks)
+		return ERR_PTR(-ENOMEM);
+
+	for (i = 0; i < dsi->driver_data->num_clks; i++) {
+		dsi->clks[i] = devm_clk_get(dev, clk_names[i]);
+		if (IS_ERR(dsi->clks[i])) {
+			if (strcmp(clk_names[i], "sclk_mipi") == 0) {
+				dsi->clks[i] = devm_clk_get(dev,
+							OLD_SCLK_MIPI_CLK_NAME);
+				if (!IS_ERR(dsi->clks[i]))
+					continue;
+			}
+
+			dev_info(dev, "failed to get the clock: %s\n",
+					clk_names[i]);
+			return ERR_PTR(PTR_ERR(dsi->clks[i]));
+		}
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	dsi->reg_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(dsi->reg_base)) {
+		dev_err(dev, "failed to remap io region\n");
+		return dsi->reg_base;
+	}
+
+	dsi->phy = devm_phy_get(dev, "dsim");
+	if (IS_ERR(dsi->phy)) {
+		dev_info(dev, "failed to get dsim phy\n");
+		return ERR_PTR(PTR_ERR(dsi->phy));
+	}
+
+	dsi->irq = platform_get_irq(pdev, 0);
+	if (dsi->irq < 0)
+		return ERR_PTR(dsi->irq);
+
+	irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
+	ret = devm_request_threaded_irq(dev, dsi->irq, NULL,
+					samsung_dsim_irq, IRQF_ONESHOT,
+					dev_name(dev), dsi);
+	if (ret) {
+		dev_err(dev, "failed to request dsi irq\n");
+		return ERR_PTR(ret);
+	}
+
+	ret = samsung_dsim_parse_dt(dsi);
+	if (ret)
+		return ERR_PTR(ret);
+
+	ret = mipi_dsi_host_register(&dsi->dsi_host);
+	if (ret)
+		return ERR_PTR(ret);
+
+	bridge = &dsi->bridge;
+	bridge->driver_private = dsi;
+	bridge->funcs = &samsung_dsim_bridge_funcs;
+	bridge->of_node = dev->of_node;
+	drm_bridge_add(bridge);
+
+	return dsi;
+}
+
+static void __samsung_dsim_remove(struct samsung_dsim *dsi)
+{
+	drm_bridge_remove(&dsi->bridge);
+
+	mipi_dsi_host_unregister(&dsi->dsi_host);
+}
+
+/*
+ * Probe/remove API, used from platforms based on the DRM bridge API.
+ */
+struct samsung_dsim *samsung_dsim_probe(struct platform_device *pdev)
+{
+	return __samsung_dsim_probe(pdev);
+}
+EXPORT_SYMBOL_GPL(samsung_dsim_probe);
+
+void samsung_dsim_remove(struct samsung_dsim *dsi)
+{
+	return __samsung_dsim_remove(dsi);
+}
+EXPORT_SYMBOL_GPL(samsung_dsim_remove);
+
+/*
+ * Bind/unbind API, used from platforms based on the component framework.
+ */
+int samsung_dsim_bind(struct samsung_dsim *dsi, struct drm_encoder *encoder)
+{
+	struct drm_bridge *previous = drm_bridge_chain_get_first_bridge(encoder);
+
+	return drm_bridge_attach(encoder, &dsi->bridge, previous, 0);
+}
+EXPORT_SYMBOL_GPL(samsung_dsim_bind);
+
+void samsung_dsim_unbind(struct samsung_dsim *dsi)
+{
+	samsung_dsim_disable(dsi);
+}
+EXPORT_SYMBOL_GPL(samsung_dsim_unbind);
+
+int samsung_dsim_suspend(struct samsung_dsim *dsi)
+{
+	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
+	int ret, i;
+
+	usleep_range(10000, 20000);
+
+	if (dsi->state & DSIM_STATE_INITIALIZED) {
+		dsi->state &= ~DSIM_STATE_INITIALIZED;
+
+		samsung_dsim_disable_clock(dsi);
+
+		samsung_dsim_disable_irq(dsi);
+	}
+
+	dsi->state &= ~DSIM_STATE_CMD_LPM;
+
+	phy_power_off(dsi->phy);
+
+	for (i = driver_data->num_clks - 1; i > -1; i--)
+		clk_disable_unprepare(dsi->clks[i]);
+
+	ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
+	if (ret < 0)
+		dev_err(dsi->dev, "cannot disable regulators %d\n", ret);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(samsung_dsim_suspend);
+
+int samsung_dsim_resume(struct samsung_dsim *dsi)
+{
+	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
+	int ret, i;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
+	if (ret < 0) {
+		dev_err(dsi->dev, "cannot enable regulators %d\n", ret);
+		return ret;
+	}
+
+	for (i = 0; i < driver_data->num_clks; i++) {
+		ret = clk_prepare_enable(dsi->clks[i]);
+		if (ret < 0)
+			goto err_clk;
+	}
+
+	ret = phy_power_on(dsi->phy);
+	if (ret < 0) {
+		dev_err(dsi->dev, "cannot enable phy %d\n", ret);
+		goto err_clk;
+	}
+
+	return 0;
+
+err_clk:
+	while (--i > -1)
+		clk_disable_unprepare(dsi->clks[i]);
+	regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(samsung_dsim_resume);
+
+MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
+MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
+MODULE_DESCRIPTION("Samsung SoC MIPI DSI Master");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index 6417f374b923..3bc321ab5bc8 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -16,7 +16,6 @@ comment "CRTCs"
 
 config DRM_EXYNOS_FIMD
 	bool "FIMD"
-	depends on !FB_S3C
 	select MFD_SYSCON
 	help
 	  Choose this option if you want to use Exynos FIMD for DRM.
@@ -28,7 +27,6 @@ config DRM_EXYNOS5433_DECON
 
 config DRM_EXYNOS7_DECON
 	bool "DECON on Exynos7"
-	depends on !FB_S3C
 	help
 	  Choose this option if you want to use Exynos DECON for DRM.
 
@@ -55,8 +53,7 @@ config DRM_EXYNOS_DPI
 config DRM_EXYNOS_DSI
 	bool "MIPI-DSI host"
 	depends on DRM_EXYNOS_FIMD || DRM_EXYNOS5433_DECON || DRM_EXYNOS7_DECON
-	select DRM_MIPI_DSI
-	select DRM_PANEL
+	select DRM_SAMSUNG_DSIM
 	default n
 	help
 	  This enables support for Exynos MIPI-DSI device.
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile
index add70b336935..2fd2f3ee4fcf 100644
--- a/drivers/gpu/drm/exynos/Makefile
+++ b/drivers/gpu/drm/exynos/Makefile
@@ -11,7 +11,7 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD)	+= exynos_drm_fimd.o
 exynosdrm-$(CONFIG_DRM_EXYNOS5433_DECON)	+= exynos5433_drm_decon.o
 exynosdrm-$(CONFIG_DRM_EXYNOS7_DECON)	+= exynos7_drm_decon.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_DPI)	+= exynos_drm_dpi.o
-exynosdrm-$(CONFIG_DRM_EXYNOS_DSI)	+= exynos_drm_dsi.o exynos_drm_dsi_pltfm.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_DSI)	+= exynos_drm_dsi.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_DP)	+= exynos_dp.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_MIXER)	+= exynos_mixer.o
 exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI)	+= exynos_hdmi.o
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index e8aea9d90c34..17f37fa74718 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -5,1774 +5,328 @@
  * Copyright (c) 2014 Samsung Electronics Co., Ltd
  *
  * Contacts: Tomasz Figa <t.figa@samsung.com>
-*/
+ */
 
-#include <linux/clk.h>
-#include <linux/delay.h>
 #include <linux/component.h>
-#include <linux/gpio/consumer.h>
-#include <linux/irq.h>
 #include <linux/of_device.h>
-#include <linux/of_gpio.h>
 #include <linux/of_graph.h>
-#include <linux/phy/phy.h>
-#include <linux/regulator/consumer.h>
-
-#include <asm/unaligned.h>
+#include <linux/pm_runtime.h>
 
-#include <video/mipi_display.h>
-#include <video/videomode.h>
-
-#include <drm/drm_atomic_helper.h>
+#include <drm/bridge/samsung-dsim.h>
 #include <drm/drm_bridge.h>
-#include <drm/drm_fb_helper.h>
+#include <drm/drm_encoder.h>
 #include <drm/drm_mipi_dsi.h>
-#include <drm/drm_panel.h>
-#include <drm/drm_print.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_simple_kms_helper.h>
 
-#include "exynos_drm_dsi.h"
-
-/* returns true iff both arguments logically differs */
-#define NEQV(a, b) (!(a) ^ !(b))
-
-/* DSIM_STATUS */
-#define DSIM_STOP_STATE_DAT(x)		(((x) & 0xf) << 0)
-#define DSIM_STOP_STATE_CLK		(1 << 8)
-#define DSIM_TX_READY_HS_CLK		(1 << 10)
-#define DSIM_PLL_STABLE			(1 << 31)
-
-/* DSIM_TIMEOUT */
-#define DSIM_LPDR_TIMEOUT(x)		((x) << 0)
-#define DSIM_BTA_TIMEOUT(x)		((x) << 16)
-
-/* DSIM_CLKCTRL */
-#define DSIM_ESC_PRESCALER(x)		(((x) & 0xffff) << 0)
-#define DSIM_ESC_PRESCALER_MASK		(0xffff << 0)
-#define DSIM_LANE_ESC_CLK_EN_CLK	(1 << 19)
-#define DSIM_LANE_ESC_CLK_EN_DATA(x)	(((x) & 0xf) << 20)
-#define DSIM_LANE_ESC_CLK_EN_DATA_MASK	(0xf << 20)
-#define DSIM_BYTE_CLKEN			(1 << 24)
-#define DSIM_BYTE_CLK_SRC(x)		(((x) & 0x3) << 25)
-#define DSIM_BYTE_CLK_SRC_MASK		(0x3 << 25)
-#define DSIM_PLL_BYPASS			(1 << 27)
-#define DSIM_ESC_CLKEN			(1 << 28)
-#define DSIM_TX_REQUEST_HSCLK		(1 << 31)
-
-/* DSIM_CONFIG */
-#define DSIM_LANE_EN_CLK		(1 << 0)
-#define DSIM_LANE_EN(x)			(((x) & 0xf) << 1)
-#define DSIM_NUM_OF_DATA_LANE(x)	(((x) & 0x3) << 5)
-#define DSIM_SUB_PIX_FORMAT(x)		(((x) & 0x7) << 8)
-#define DSIM_MAIN_PIX_FORMAT_MASK	(0x7 << 12)
-#define DSIM_MAIN_PIX_FORMAT_RGB888	(0x7 << 12)
-#define DSIM_MAIN_PIX_FORMAT_RGB666	(0x6 << 12)
-#define DSIM_MAIN_PIX_FORMAT_RGB666_P	(0x5 << 12)
-#define DSIM_MAIN_PIX_FORMAT_RGB565	(0x4 << 12)
-#define DSIM_SUB_VC			(((x) & 0x3) << 16)
-#define DSIM_MAIN_VC			(((x) & 0x3) << 18)
-#define DSIM_HSA_MODE			(1 << 20)
-#define DSIM_HBP_MODE			(1 << 21)
-#define DSIM_HFP_MODE			(1 << 22)
-#define DSIM_HSE_MODE			(1 << 23)
-#define DSIM_AUTO_MODE			(1 << 24)
-#define DSIM_VIDEO_MODE			(1 << 25)
-#define DSIM_BURST_MODE			(1 << 26)
-#define DSIM_SYNC_INFORM		(1 << 27)
-#define DSIM_EOT_DISABLE		(1 << 28)
-#define DSIM_MFLUSH_VS			(1 << 29)
-/* This flag is valid only for exynos3250/3472/5260/5430 */
-#define DSIM_CLKLANE_STOP		(1 << 30)
-
-/* DSIM_ESCMODE */
-#define DSIM_TX_TRIGGER_RST		(1 << 4)
-#define DSIM_TX_LPDT_LP			(1 << 6)
-#define DSIM_CMD_LPDT_LP		(1 << 7)
-#define DSIM_FORCE_BTA			(1 << 16)
-#define DSIM_FORCE_STOP_STATE		(1 << 20)
-#define DSIM_STOP_STATE_CNT(x)		(((x) & 0x7ff) << 21)
-#define DSIM_STOP_STATE_CNT_MASK	(0x7ff << 21)
-
-/* DSIM_MDRESOL */
-#define DSIM_MAIN_STAND_BY		(1 << 31)
-#define DSIM_MAIN_VRESOL(x, num_bits)	(((x) & ((1 << (num_bits)) - 1)) << 16)
-#define DSIM_MAIN_HRESOL(x, num_bits)	(((x) & ((1 << (num_bits)) - 1)) << 0)
-
-/* DSIM_MVPORCH */
-#define DSIM_CMD_ALLOW(x)		((x) << 28)
-#define DSIM_STABLE_VFP(x)		((x) << 16)
-#define DSIM_MAIN_VBP(x)		((x) << 0)
-#define DSIM_CMD_ALLOW_MASK		(0xf << 28)
-#define DSIM_STABLE_VFP_MASK		(0x7ff << 16)
-#define DSIM_MAIN_VBP_MASK		(0x7ff << 0)
-
-/* DSIM_MHPORCH */
-#define DSIM_MAIN_HFP(x)		((x) << 16)
-#define DSIM_MAIN_HBP(x)		((x) << 0)
-#define DSIM_MAIN_HFP_MASK		((0xffff) << 16)
-#define DSIM_MAIN_HBP_MASK		((0xffff) << 0)
-
-/* DSIM_MSYNC */
-#define DSIM_MAIN_VSA(x)		((x) << 22)
-#define DSIM_MAIN_HSA(x)		((x) << 0)
-#define DSIM_MAIN_VSA_MASK		((0x3ff) << 22)
-#define DSIM_MAIN_HSA_MASK		((0xffff) << 0)
-
-/* DSIM_SDRESOL */
-#define DSIM_SUB_STANDY(x)		((x) << 31)
-#define DSIM_SUB_VRESOL(x)		((x) << 16)
-#define DSIM_SUB_HRESOL(x)		((x) << 0)
-#define DSIM_SUB_STANDY_MASK		((0x1) << 31)
-#define DSIM_SUB_VRESOL_MASK		((0x7ff) << 16)
-#define DSIM_SUB_HRESOL_MASK		((0x7ff) << 0)
-
-/* DSIM_INTSRC */
-#define DSIM_INT_PLL_STABLE		(1 << 31)
-#define DSIM_INT_SW_RST_RELEASE		(1 << 30)
-#define DSIM_INT_SFR_FIFO_EMPTY		(1 << 29)
-#define DSIM_INT_SFR_HDR_FIFO_EMPTY	(1 << 28)
-#define DSIM_INT_BTA			(1 << 25)
-#define DSIM_INT_FRAME_DONE		(1 << 24)
-#define DSIM_INT_RX_TIMEOUT		(1 << 21)
-#define DSIM_INT_BTA_TIMEOUT		(1 << 20)
-#define DSIM_INT_RX_DONE		(1 << 18)
-#define DSIM_INT_RX_TE			(1 << 17)
-#define DSIM_INT_RX_ACK			(1 << 16)
-#define DSIM_INT_RX_ECC_ERR		(1 << 15)
-#define DSIM_INT_RX_CRC_ERR		(1 << 14)
+#include "exynos_drm_crtc.h"
+#include "exynos_drm_drv.h"
 
-/* DSIM_FIFOCTRL */
-#define DSIM_RX_DATA_FULL		(1 << 25)
-#define DSIM_RX_DATA_EMPTY		(1 << 24)
-#define DSIM_SFR_HEADER_FULL		(1 << 23)
-#define DSIM_SFR_HEADER_EMPTY		(1 << 22)
-#define DSIM_SFR_PAYLOAD_FULL		(1 << 21)
-#define DSIM_SFR_PAYLOAD_EMPTY		(1 << 20)
-#define DSIM_I80_HEADER_FULL		(1 << 19)
-#define DSIM_I80_HEADER_EMPTY		(1 << 18)
-#define DSIM_I80_PAYLOAD_FULL		(1 << 17)
-#define DSIM_I80_PAYLOAD_EMPTY		(1 << 16)
-#define DSIM_SD_HEADER_FULL		(1 << 15)
-#define DSIM_SD_HEADER_EMPTY		(1 << 14)
-#define DSIM_SD_PAYLOAD_FULL		(1 << 13)
-#define DSIM_SD_PAYLOAD_EMPTY		(1 << 12)
-#define DSIM_MD_HEADER_FULL		(1 << 11)
-#define DSIM_MD_HEADER_EMPTY		(1 << 10)
-#define DSIM_MD_PAYLOAD_FULL		(1 << 9)
-#define DSIM_MD_PAYLOAD_EMPTY		(1 << 8)
-#define DSIM_RX_FIFO			(1 << 4)
-#define DSIM_SFR_FIFO			(1 << 3)
-#define DSIM_I80_FIFO			(1 << 2)
-#define DSIM_SD_FIFO			(1 << 1)
-#define DSIM_MD_FIFO			(1 << 0)
-
-/* DSIM_PHYACCHR */
-#define DSIM_AFC_EN			(1 << 14)
-#define DSIM_AFC_CTL(x)			(((x) & 0x7) << 5)
-
-/* DSIM_PLLCTRL */
-#define DSIM_FREQ_BAND(x)		((x) << 24)
-#define DSIM_PLL_EN			(1 << 23)
-#define DSIM_PLL_P(x)			((x) << 13)
-#define DSIM_PLL_M(x)			((x) << 4)
-#define DSIM_PLL_S(x)			((x) << 1)
-
-/* DSIM_PHYCTRL */
-#define DSIM_PHYCTRL_ULPS_EXIT(x)	(((x) & 0x1ff) << 0)
-#define DSIM_PHYCTRL_B_DPHYCTL_VREG_LP	(1 << 30)
-#define DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP	(1 << 14)
-
-/* DSIM_PHYTIMING */
-#define DSIM_PHYTIMING_LPX(x)		((x) << 8)
-#define DSIM_PHYTIMING_HS_EXIT(x)	((x) << 0)
-
-/* DSIM_PHYTIMING1 */
-#define DSIM_PHYTIMING1_CLK_PREPARE(x)	((x) << 24)
-#define DSIM_PHYTIMING1_CLK_ZERO(x)	((x) << 16)
-#define DSIM_PHYTIMING1_CLK_POST(x)	((x) << 8)
-#define DSIM_PHYTIMING1_CLK_TRAIL(x)	((x) << 0)
-
-/* DSIM_PHYTIMING2 */
-#define DSIM_PHYTIMING2_HS_PREPARE(x)	((x) << 16)
-#define DSIM_PHYTIMING2_HS_ZERO(x)	((x) << 8)
-#define DSIM_PHYTIMING2_HS_TRAIL(x)	((x) << 0)
-
-#define DSI_MAX_BUS_WIDTH		4
-#define DSI_NUM_VIRTUAL_CHANNELS	4
-#define DSI_TX_FIFO_SIZE		2048
-#define DSI_RX_FIFO_SIZE		256
-#define DSI_XFER_TIMEOUT_MS		100
-#define DSI_RX_FIFO_EMPTY		0x30800002
-
-#define OLD_SCLK_MIPI_CLK_NAME "pll_clk"
-
-static const char *const clk_names[5] = { "bus_clk", "sclk_mipi",
-	"phyclk_mipidphy0_bitclkdiv8", "phyclk_mipidphy0_rxclkesc0",
-	"sclk_rgb_vclk_to_dsim0" };
-
-enum exynos_dsi_transfer_type {
-	EXYNOS_DSI_TX,
-	EXYNOS_DSI_RX,
+enum {
+	DSI_PORT_IN,
+	DSI_PORT_OUT
 };
 
-struct exynos_dsi_transfer {
-	struct list_head list;
-	struct completion completed;
-	int result;
-	struct mipi_dsi_packet packet;
-	u16 flags;
-	u16 tx_done;
-
-	u8 *rx_payload;
-	u16 rx_len;
-	u16 rx_done;
-};
-
-#define DSIM_STATE_ENABLED		BIT(0)
-#define DSIM_STATE_INITIALIZED		BIT(1)
-#define DSIM_STATE_CMD_LPM		BIT(2)
-#define DSIM_STATE_VIDOUT_AVAILABLE	BIT(3)
-
 struct exynos_dsi {
-	struct drm_bridge bridge;
-	struct mipi_dsi_host dsi_host;
-	struct drm_connector connector;
-	struct drm_panel *panel;
-	struct list_head bridge_chain;
-	struct drm_bridge *out_bridge;
-	struct device *dev;
-
-	void __iomem *reg_base;
-	struct phy *phy;
-	struct clk **clks;
-	struct regulator_bulk_data supplies[2];
-	int irq;
-	int te_gpio;
-
-	u32 pll_clk_rate;
-	u32 burst_clk_rate;
-	u32 esc_clk_rate;
-	u32 lanes;
-	u32 mode_flags;
-	u32 format;
-
-	struct drm_display_mode mode;
-
-	int state;
-	struct drm_property *brightness;
-	struct completion completed;
-
-	spinlock_t transfer_lock; /* protects transfer_list */
-	struct list_head transfer_list;
-
-	const struct exynos_dsi_driver_data *driver_data;
+	struct samsung_dsim *dsi;
+	struct drm_encoder encoder;
 };
 
-#define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host)
-#define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector)
-
-enum reg_idx {
-	DSIM_STATUS_REG,	/* Status register */
-	DSIM_SWRST_REG,		/* Software reset register */
-	DSIM_CLKCTRL_REG,	/* Clock control register */
-	DSIM_TIMEOUT_REG,	/* Time out register */
-	DSIM_CONFIG_REG,	/* Configuration register */
-	DSIM_ESCMODE_REG,	/* Escape mode register */
-	DSIM_MDRESOL_REG,
-	DSIM_MVPORCH_REG,	/* Main display Vporch register */
-	DSIM_MHPORCH_REG,	/* Main display Hporch register */
-	DSIM_MSYNC_REG,		/* Main display sync area register */
-	DSIM_INTSRC_REG,	/* Interrupt source register */
-	DSIM_INTMSK_REG,	/* Interrupt mask register */
-	DSIM_PKTHDR_REG,	/* Packet Header FIFO register */
-	DSIM_PAYLOAD_REG,	/* Payload FIFO register */
-	DSIM_RXFIFO_REG,	/* Read FIFO register */
-	DSIM_FIFOCTRL_REG,	/* FIFO status and control register */
-	DSIM_PLLCTRL_REG,	/* PLL control register */
-	DSIM_PHYCTRL_REG,
-	DSIM_PHYTIMING_REG,
-	DSIM_PHYTIMING1_REG,
-	DSIM_PHYTIMING2_REG,
-	NUM_REGS
+static const unsigned int reg_values[] = {
+	[RESET_TYPE] = DSIM_SWRST,
+	[PLL_TIMER] = 500,
+	[STOP_STATE_CNT] = 0xf,
+	[PHYCTRL_ULPS_EXIT] = 0x0af,
+	[PHYCTRL_VREG_LP] = 0,
+	[PHYCTRL_SLEW_UP] = 0,
+	[PHYTIMING_LPX] = 0x06,
+	[PHYTIMING_HS_EXIT] = 0x0b,
+	[PHYTIMING_CLK_PREPARE] = 0x07,
+	[PHYTIMING_CLK_ZERO] = 0x27,
+	[PHYTIMING_CLK_POST] = 0x0d,
+	[PHYTIMING_CLK_TRAIL] = 0x08,
+	[PHYTIMING_HS_PREPARE] = 0x09,
+	[PHYTIMING_HS_ZERO] = 0x0d,
+	[PHYTIMING_HS_TRAIL] = 0x0b,
 };
 
-static const unsigned int exynos_reg_ofs[] = {
-	[DSIM_STATUS_REG] =  0x00,
-	[DSIM_SWRST_REG] =  0x04,
-	[DSIM_CLKCTRL_REG] =  0x08,
-	[DSIM_TIMEOUT_REG] =  0x0c,
-	[DSIM_CONFIG_REG] =  0x10,
-	[DSIM_ESCMODE_REG] =  0x14,
-	[DSIM_MDRESOL_REG] =  0x18,
-	[DSIM_MVPORCH_REG] =  0x1c,
-	[DSIM_MHPORCH_REG] =  0x20,
-	[DSIM_MSYNC_REG] =  0x24,
-	[DSIM_INTSRC_REG] =  0x2c,
-	[DSIM_INTMSK_REG] =  0x30,
-	[DSIM_PKTHDR_REG] =  0x34,
-	[DSIM_PAYLOAD_REG] =  0x38,
-	[DSIM_RXFIFO_REG] =  0x3c,
-	[DSIM_FIFOCTRL_REG] =  0x44,
-	[DSIM_PLLCTRL_REG] =  0x4c,
-	[DSIM_PHYCTRL_REG] =  0x5c,
-	[DSIM_PHYTIMING_REG] =  0x64,
-	[DSIM_PHYTIMING1_REG] =  0x68,
-	[DSIM_PHYTIMING2_REG] =  0x6c,
+static const unsigned int exynos5422_reg_values[] = {
+	[RESET_TYPE] = DSIM_SWRST,
+	[PLL_TIMER] = 500,
+	[STOP_STATE_CNT] = 0xf,
+	[PHYCTRL_ULPS_EXIT] = 0xaf,
+	[PHYCTRL_VREG_LP] = 0,
+	[PHYCTRL_SLEW_UP] = 0,
+	[PHYTIMING_LPX] = 0x08,
+	[PHYTIMING_HS_EXIT] = 0x0d,
+	[PHYTIMING_CLK_PREPARE] = 0x09,
+	[PHYTIMING_CLK_ZERO] = 0x30,
+	[PHYTIMING_CLK_POST] = 0x0e,
+	[PHYTIMING_CLK_TRAIL] = 0x0a,
+	[PHYTIMING_HS_PREPARE] = 0x0c,
+	[PHYTIMING_HS_ZERO] = 0x11,
+	[PHYTIMING_HS_TRAIL] = 0x0d,
 };
 
-static const unsigned int exynos5433_reg_ofs[] = {
-	[DSIM_STATUS_REG] = 0x04,
-	[DSIM_SWRST_REG] = 0x0C,
-	[DSIM_CLKCTRL_REG] = 0x10,
-	[DSIM_TIMEOUT_REG] = 0x14,
-	[DSIM_CONFIG_REG] = 0x18,
-	[DSIM_ESCMODE_REG] = 0x1C,
-	[DSIM_MDRESOL_REG] = 0x20,
-	[DSIM_MVPORCH_REG] = 0x24,
-	[DSIM_MHPORCH_REG] = 0x28,
-	[DSIM_MSYNC_REG] = 0x2C,
-	[DSIM_INTSRC_REG] = 0x34,
-	[DSIM_INTMSK_REG] = 0x38,
-	[DSIM_PKTHDR_REG] = 0x3C,
-	[DSIM_PAYLOAD_REG] = 0x40,
-	[DSIM_RXFIFO_REG] = 0x44,
-	[DSIM_FIFOCTRL_REG] = 0x4C,
-	[DSIM_PLLCTRL_REG] = 0x94,
-	[DSIM_PHYCTRL_REG] = 0xA4,
-	[DSIM_PHYTIMING_REG] = 0xB4,
-	[DSIM_PHYTIMING1_REG] = 0xB8,
-	[DSIM_PHYTIMING2_REG] = 0xBC,
+static const unsigned int exynos5433_reg_values[] = {
+	[RESET_TYPE] = DSIM_FUNCRST,
+	[PLL_TIMER] = 22200,
+	[STOP_STATE_CNT] = 0xa,
+	[PHYCTRL_ULPS_EXIT] = 0x190,
+	[PHYCTRL_VREG_LP] = 1,
+	[PHYCTRL_SLEW_UP] = 1,
+	[PHYTIMING_LPX] = 0x07,
+	[PHYTIMING_HS_EXIT] = 0x0c,
+	[PHYTIMING_CLK_PREPARE] = 0x09,
+	[PHYTIMING_CLK_ZERO] = 0x2d,
+	[PHYTIMING_CLK_POST] = 0x0e,
+	[PHYTIMING_CLK_TRAIL] = 0x09,
+	[PHYTIMING_HS_PREPARE] = 0x0b,
+	[PHYTIMING_HS_ZERO] = 0x10,
+	[PHYTIMING_HS_TRAIL] = 0x0c,
 };
 
-static inline void exynos_dsi_write(struct exynos_dsi *dsi, enum reg_idx idx,
-				    u32 val)
-{
-	const unsigned int *reg_ofs;
-
-	if (dsi->driver_data->reg_ofs == EXYNOS5433_REG_OFS)
-		reg_ofs = exynos5433_reg_ofs;
-	else
-		reg_ofs = exynos_reg_ofs;
-
-	writel(val, dsi->reg_base + reg_ofs[idx]);
-}
-
-static inline u32 exynos_dsi_read(struct exynos_dsi *dsi, enum reg_idx idx)
-{
-	const unsigned int *reg_ofs;
-
-	if (dsi->driver_data->reg_ofs == EXYNOS5433_REG_OFS)
-		reg_ofs = exynos5433_reg_ofs;
-	else
-		reg_ofs = exynos_reg_ofs;
-
-	return readl(dsi->reg_base + reg_ofs[idx]);
-}
-
-static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi)
-{
-	if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300)))
-		return;
-
-	dev_err(dsi->dev, "timeout waiting for reset\n");
-}
-
-static void exynos_dsi_reset(struct exynos_dsi *dsi)
-{
-	u32 reset_val = dsi->driver_data->reg_values[RESET_TYPE];
-
-	reinit_completion(&dsi->completed);
-	exynos_dsi_write(dsi, DSIM_SWRST_REG, reset_val);
-}
-
-#ifndef MHZ
-#define MHZ	(1000*1000)
-#endif
-
-static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi,
-		unsigned long fin, unsigned long fout, u8 *p, u16 *m, u8 *s)
-{
-	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
-	unsigned long best_freq = 0;
-	u32 min_delta = 0xffffffff;
-	u8 p_min, p_max;
-	u8 _p, best_p;
-	u16 _m, best_m;
-	u8 _s, best_s;
-
-	p_min = DIV_ROUND_UP(fin, (12 * MHZ));
-	p_max = fin / (6 * MHZ);
-
-	for (_p = p_min; _p <= p_max; ++_p) {
-		for (_s = 0; _s <= 5; ++_s) {
-			u64 tmp;
-			u32 delta;
-
-			tmp = (u64)fout * (_p << _s);
-			do_div(tmp, fin);
-			_m = tmp;
-			if (_m < 41 || _m > 125)
-				continue;
-
-			tmp = (u64)_m * fin;
-			do_div(tmp, _p);
-			if (tmp < 500 * MHZ ||
-					tmp > driver_data->max_freq * MHZ)
-				continue;
-
-			tmp = (u64)_m * fin;
-			do_div(tmp, _p << _s);
-
-			delta = abs(fout - tmp);
-			if (delta < min_delta) {
-				best_p = _p;
-				best_m = _m;
-				best_s = _s;
-				min_delta = delta;
-				best_freq = tmp;
-			}
-		}
-	}
-
-	if (best_freq) {
-		*p = best_p;
-		*m = best_m;
-		*s = best_s;
-	}
-
-	return best_freq;
-}
-
-static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi,
-					unsigned long freq)
-{
-	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
-	unsigned long fin, fout;
-	int timeout;
-	u8 p, s;
-	u16 m;
-	u32 reg;
-
-	fin = dsi->pll_clk_rate;
-	fout = exynos_dsi_pll_find_pms(dsi, fin, freq, &p, &m, &s);
-	if (!fout) {
-		dev_err(dsi->dev,
-			"failed to find PLL PMS for requested frequency\n");
-		return 0;
-	}
-	dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s);
-
-	writel(driver_data->reg_values[PLL_TIMER],
-			dsi->reg_base + driver_data->plltmr_reg);
-
-	reg = DSIM_PLL_EN | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s);
-
-	if (driver_data->has_freqband) {
-		static const unsigned long freq_bands[] = {
-			100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ,
-			270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ,
-			510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ,
-			770 * MHZ, 870 * MHZ, 950 * MHZ,
-		};
-		int band;
-
-		for (band = 0; band < ARRAY_SIZE(freq_bands); ++band)
-			if (fout < freq_bands[band])
-				break;
-
-		dev_dbg(dsi->dev, "band %d\n", band);
-
-		reg |= DSIM_FREQ_BAND(band);
-	}
-
-	exynos_dsi_write(dsi, DSIM_PLLCTRL_REG, reg);
-
-	timeout = 1000;
-	do {
-		if (timeout-- == 0) {
-			dev_err(dsi->dev, "PLL failed to stabilize\n");
-			return 0;
-		}
-		reg = exynos_dsi_read(dsi, DSIM_STATUS_REG);
-	} while ((reg & DSIM_PLL_STABLE) == 0);
-
-	return fout;
-}
-
-static int exynos_dsi_enable_clock(struct exynos_dsi *dsi)
-{
-	unsigned long hs_clk, byte_clk, esc_clk;
-	unsigned long esc_div;
-	u32 reg;
-
-	hs_clk = exynos_dsi_set_pll(dsi, dsi->burst_clk_rate);
-	if (!hs_clk) {
-		dev_err(dsi->dev, "failed to configure DSI PLL\n");
-		return -EFAULT;
-	}
-
-	byte_clk = hs_clk / 8;
-	esc_div = DIV_ROUND_UP(byte_clk, dsi->esc_clk_rate);
-	esc_clk = byte_clk / esc_div;
-
-	if (esc_clk > 20 * MHZ) {
-		++esc_div;
-		esc_clk = byte_clk / esc_div;
-	}
-
-	dev_dbg(dsi->dev, "hs_clk = %lu, byte_clk = %lu, esc_clk = %lu\n",
-		hs_clk, byte_clk, esc_clk);
-
-	reg = exynos_dsi_read(dsi, DSIM_CLKCTRL_REG);
-	reg &= ~(DSIM_ESC_PRESCALER_MASK | DSIM_LANE_ESC_CLK_EN_CLK
-			| DSIM_LANE_ESC_CLK_EN_DATA_MASK | DSIM_PLL_BYPASS
-			| DSIM_BYTE_CLK_SRC_MASK);
-	reg |= DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN
-			| DSIM_ESC_PRESCALER(esc_div)
-			| DSIM_LANE_ESC_CLK_EN_CLK
-			| DSIM_LANE_ESC_CLK_EN_DATA(BIT(dsi->lanes) - 1)
-			| DSIM_BYTE_CLK_SRC(0)
-			| DSIM_TX_REQUEST_HSCLK;
-	exynos_dsi_write(dsi, DSIM_CLKCTRL_REG, reg);
-
-	return 0;
-}
-
-static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi)
-{
-	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
-	const unsigned int *reg_values = driver_data->reg_values;
-	u32 reg;
-
-	if (driver_data->has_freqband)
-		return;
-
-	/* B D-PHY: D-PHY Master & Slave Analog Block control */
-	reg = DSIM_PHYCTRL_ULPS_EXIT(reg_values[PHYCTRL_ULPS_EXIT]);
-	if (reg_values[PHYCTRL_VREG_LP])
-		reg |= DSIM_PHYCTRL_B_DPHYCTL_VREG_LP;
-	if (reg_values[PHYCTRL_SLEW_UP])
-		reg |= DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP;
-	exynos_dsi_write(dsi, DSIM_PHYCTRL_REG, reg);
-
-	/*
-	 * T LPX: Transmitted length of any Low-Power state period
-	 * T HS-EXIT: Time that the transmitter drives LP-11 following a HS
-	 *	burst
-	 */
-	reg = DSIM_PHYTIMING_LPX(reg_values[PHYTIMING_LPX]) |
-		DSIM_PHYTIMING_HS_EXIT(reg_values[PHYTIMING_HS_EXIT]);
-	exynos_dsi_write(dsi, DSIM_PHYTIMING_REG, reg);
-
-	/*
-	 * T CLK-PREPARE: Time that the transmitter drives the Clock Lane LP-00
-	 *	Line state immediately before the HS-0 Line state starting the
-	 *	HS transmission
-	 * T CLK-ZERO: Time that the transmitter drives the HS-0 state prior to
-	 *	transmitting the Clock.
-	 * T CLK_POST: Time that the transmitter continues to send HS clock
-	 *	after the last associated Data Lane has transitioned to LP Mode
-	 *	Interval is defined as the period from the end of T HS-TRAIL to
-	 *	the beginning of T CLK-TRAIL
-	 * T CLK-TRAIL: Time that the transmitter drives the HS-0 state after
-	 *	the last payload clock bit of a HS transmission burst
-	 */
-	reg = DSIM_PHYTIMING1_CLK_PREPARE(reg_values[PHYTIMING_CLK_PREPARE]) |
-		DSIM_PHYTIMING1_CLK_ZERO(reg_values[PHYTIMING_CLK_ZERO]) |
-		DSIM_PHYTIMING1_CLK_POST(reg_values[PHYTIMING_CLK_POST]) |
-		DSIM_PHYTIMING1_CLK_TRAIL(reg_values[PHYTIMING_CLK_TRAIL]);
-	exynos_dsi_write(dsi, DSIM_PHYTIMING1_REG, reg);
-
-	/*
-	 * T HS-PREPARE: Time that the transmitter drives the Data Lane LP-00
-	 *	Line state immediately before the HS-0 Line state starting the
-	 *	HS transmission
-	 * T HS-ZERO: Time that the transmitter drives the HS-0 state prior to
-	 *	transmitting the Sync sequence.
-	 * T HS-TRAIL: Time that the transmitter drives the flipped differential
-	 *	state after last payload data bit of a HS transmission burst
-	 */
-	reg = DSIM_PHYTIMING2_HS_PREPARE(reg_values[PHYTIMING_HS_PREPARE]) |
-		DSIM_PHYTIMING2_HS_ZERO(reg_values[PHYTIMING_HS_ZERO]) |
-		DSIM_PHYTIMING2_HS_TRAIL(reg_values[PHYTIMING_HS_TRAIL]);
-	exynos_dsi_write(dsi, DSIM_PHYTIMING2_REG, reg);
-}
-
-static void exynos_dsi_disable_clock(struct exynos_dsi *dsi)
-{
-	u32 reg;
-
-	reg = exynos_dsi_read(dsi, DSIM_CLKCTRL_REG);
-	reg &= ~(DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK
-			| DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN);
-	exynos_dsi_write(dsi, DSIM_CLKCTRL_REG, reg);
-
-	reg = exynos_dsi_read(dsi, DSIM_PLLCTRL_REG);
-	reg &= ~DSIM_PLL_EN;
-	exynos_dsi_write(dsi, DSIM_PLLCTRL_REG, reg);
-}
-
-static void exynos_dsi_enable_lane(struct exynos_dsi *dsi, u32 lane)
-{
-	u32 reg = exynos_dsi_read(dsi, DSIM_CONFIG_REG);
-	reg |= (DSIM_NUM_OF_DATA_LANE(dsi->lanes - 1) | DSIM_LANE_EN_CLK |
-			DSIM_LANE_EN(lane));
-	exynos_dsi_write(dsi, DSIM_CONFIG_REG, reg);
-}
-
-static int exynos_dsi_init_link(struct exynos_dsi *dsi)
+static int exynos_dsi_host_attach(struct device *dev,
+				  struct mipi_dsi_device *device)
 {
-	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
-	int timeout;
-	u32 reg;
-	u32 lanes_mask;
-
-	/* Initialize FIFO pointers */
-	reg = exynos_dsi_read(dsi, DSIM_FIFOCTRL_REG);
-	reg &= ~0x1f;
-	exynos_dsi_write(dsi, DSIM_FIFOCTRL_REG, reg);
-
-	usleep_range(9000, 11000);
-
-	reg |= 0x1f;
-	exynos_dsi_write(dsi, DSIM_FIFOCTRL_REG, reg);
-	usleep_range(9000, 11000);
+	struct exynos_dsi *dsi = dev_get_drvdata(dev);
+	struct drm_device *drm = dsi->encoder.dev;
+	struct exynos_drm_crtc *crtc;
 
-	/* DSI configuration */
-	reg = 0;
+	mutex_lock(&drm->mode_config.mutex);
+	crtc = exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD);
+	crtc->i80_mode = !(device->mode_flags & MIPI_DSI_MODE_VIDEO);
+	mutex_unlock(&drm->mode_config.mutex);
 
-	/*
-	 * The first bit of mode_flags specifies display configuration.
-	 * If this bit is set[= MIPI_DSI_MODE_VIDEO], dsi will support video
-	 * mode, otherwise it will support command mode.
-	 */
-	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
-		reg |= DSIM_VIDEO_MODE;
-
-		/*
-		 * The user manual describes that following bits are ignored in
-		 * command mode.
-		 */
-		if (!(dsi->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH))
-			reg |= DSIM_MFLUSH_VS;
-		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
-			reg |= DSIM_SYNC_INFORM;
-		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
-			reg |= DSIM_BURST_MODE;
-		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT)
-			reg |= DSIM_AUTO_MODE;
-		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSE)
-			reg |= DSIM_HSE_MODE;
-		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HFP))
-			reg |= DSIM_HFP_MODE;
-		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HBP))
-			reg |= DSIM_HBP_MODE;
-		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSA))
-			reg |= DSIM_HSA_MODE;
-	}
-
-	if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET))
-		reg |= DSIM_EOT_DISABLE;
-
-	switch (dsi->format) {
-	case MIPI_DSI_FMT_RGB888:
-		reg |= DSIM_MAIN_PIX_FORMAT_RGB888;
-		break;
-	case MIPI_DSI_FMT_RGB666:
-		reg |= DSIM_MAIN_PIX_FORMAT_RGB666;
-		break;
-	case MIPI_DSI_FMT_RGB666_PACKED:
-		reg |= DSIM_MAIN_PIX_FORMAT_RGB666_P;
-		break;
-	case MIPI_DSI_FMT_RGB565:
-		reg |= DSIM_MAIN_PIX_FORMAT_RGB565;
-		break;
-	default:
-		dev_err(dsi->dev, "invalid pixel format\n");
-		return -EINVAL;
-	}
-
-	/*
-	 * Use non-continuous clock mode if the periparal wants and
-	 * host controller supports
-	 *
-	 * In non-continous clock mode, host controller will turn off
-	 * the HS clock between high-speed transmissions to reduce
-	 * power consumption.
-	 */
-	if (driver_data->has_clklane_stop &&
-			dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
-		reg |= DSIM_CLKLANE_STOP;
-	}
-	exynos_dsi_write(dsi, DSIM_CONFIG_REG, reg);
-
-	lanes_mask = BIT(dsi->lanes) - 1;
-	exynos_dsi_enable_lane(dsi, lanes_mask);
-
-	/* Check clock and data lane state are stop state */
-	timeout = 100;
-	do {
-		if (timeout-- == 0) {
-			dev_err(dsi->dev, "waiting for bus lanes timed out\n");
-			return -EFAULT;
-		}
-
-		reg = exynos_dsi_read(dsi, DSIM_STATUS_REG);
-		if ((reg & DSIM_STOP_STATE_DAT(lanes_mask))
-		    != DSIM_STOP_STATE_DAT(lanes_mask))
-			continue;
-	} while (!(reg & (DSIM_STOP_STATE_CLK | DSIM_TX_READY_HS_CLK)));
-
-	reg = exynos_dsi_read(dsi, DSIM_ESCMODE_REG);
-	reg &= ~DSIM_STOP_STATE_CNT_MASK;
-	reg |= DSIM_STOP_STATE_CNT(driver_data->reg_values[STOP_STATE_CNT]);
-	exynos_dsi_write(dsi, DSIM_ESCMODE_REG, reg);
-
-	reg = DSIM_BTA_TIMEOUT(0xff) | DSIM_LPDR_TIMEOUT(0xffff);
-	exynos_dsi_write(dsi, DSIM_TIMEOUT_REG, reg);
+	if (drm->mode_config.poll_enabled)
+		drm_kms_helper_hotplug_event(drm);
 
 	return 0;
 }
 
-static void exynos_dsi_set_display_mode(struct exynos_dsi *dsi)
-{
-	struct drm_display_mode *m = &dsi->mode;
-	unsigned int num_bits_resol = dsi->driver_data->num_bits_resol;
-	u32 reg;
-
-	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
-		reg = DSIM_CMD_ALLOW(0xf)
-			| DSIM_STABLE_VFP(m->vsync_start - m->vdisplay)
-			| DSIM_MAIN_VBP(m->vtotal - m->vsync_end);
-		exynos_dsi_write(dsi, DSIM_MVPORCH_REG, reg);
-
-		reg = DSIM_MAIN_HFP(m->hsync_start - m->hdisplay)
-			| DSIM_MAIN_HBP(m->htotal - m->hsync_end);
-		exynos_dsi_write(dsi, DSIM_MHPORCH_REG, reg);
-
-		reg = DSIM_MAIN_VSA(m->vsync_end - m->vsync_start)
-			| DSIM_MAIN_HSA(m->hsync_end - m->hsync_start);
-		exynos_dsi_write(dsi, DSIM_MSYNC_REG, reg);
-	}
-	reg =  DSIM_MAIN_HRESOL(m->hdisplay, num_bits_resol) |
-		DSIM_MAIN_VRESOL(m->vdisplay, num_bits_resol);
-
-	exynos_dsi_write(dsi, DSIM_MDRESOL_REG, reg);
-
-	dev_dbg(dsi->dev, "LCD size = %dx%d\n", m->hdisplay, m->vdisplay);
-}
-
-static void exynos_dsi_set_display_enable(struct exynos_dsi *dsi, bool enable)
-{
-	u32 reg;
-
-	reg = exynos_dsi_read(dsi, DSIM_MDRESOL_REG);
-	if (enable)
-		reg |= DSIM_MAIN_STAND_BY;
-	else
-		reg &= ~DSIM_MAIN_STAND_BY;
-	exynos_dsi_write(dsi, DSIM_MDRESOL_REG, reg);
-}
-
-static int exynos_dsi_wait_for_hdr_fifo(struct exynos_dsi *dsi)
-{
-	int timeout = 2000;
-
-	do {
-		u32 reg = exynos_dsi_read(dsi, DSIM_FIFOCTRL_REG);
-
-		if (!(reg & DSIM_SFR_HEADER_FULL))
-			return 0;
-
-		if (!cond_resched())
-			usleep_range(950, 1050);
-	} while (--timeout);
-
-	return -ETIMEDOUT;
-}
-
-static void exynos_dsi_set_cmd_lpm(struct exynos_dsi *dsi, bool lpm)
-{
-	u32 v = exynos_dsi_read(dsi, DSIM_ESCMODE_REG);
-
-	if (lpm)
-		v |= DSIM_CMD_LPDT_LP;
-	else
-		v &= ~DSIM_CMD_LPDT_LP;
-
-	exynos_dsi_write(dsi, DSIM_ESCMODE_REG, v);
-}
-
-static void exynos_dsi_force_bta(struct exynos_dsi *dsi)
-{
-	u32 v = exynos_dsi_read(dsi, DSIM_ESCMODE_REG);
-	v |= DSIM_FORCE_BTA;
-	exynos_dsi_write(dsi, DSIM_ESCMODE_REG, v);
-}
-
-static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi,
-					struct exynos_dsi_transfer *xfer)
-{
-	struct device *dev = dsi->dev;
-	struct mipi_dsi_packet *pkt = &xfer->packet;
-	const u8 *payload = pkt->payload + xfer->tx_done;
-	u16 length = pkt->payload_length - xfer->tx_done;
-	bool first = !xfer->tx_done;
-	u32 reg;
-
-	dev_dbg(dev, "< xfer %pK: tx len %u, done %u, rx len %u, done %u\n",
-		xfer, length, xfer->tx_done, xfer->rx_len, xfer->rx_done);
-
-	if (length > DSI_TX_FIFO_SIZE)
-		length = DSI_TX_FIFO_SIZE;
-
-	xfer->tx_done += length;
-
-	/* Send payload */
-	while (length >= 4) {
-		reg = get_unaligned_le32(payload);
-		exynos_dsi_write(dsi, DSIM_PAYLOAD_REG, reg);
-		payload += 4;
-		length -= 4;
-	}
-
-	reg = 0;
-	switch (length) {
-	case 3:
-		reg |= payload[2] << 16;
-		fallthrough;
-	case 2:
-		reg |= payload[1] << 8;
-		fallthrough;
-	case 1:
-		reg |= payload[0];
-		exynos_dsi_write(dsi, DSIM_PAYLOAD_REG, reg);
-		break;
-	}
-
-	/* Send packet header */
-	if (!first)
-		return;
-
-	reg = get_unaligned_le32(pkt->header);
-	if (exynos_dsi_wait_for_hdr_fifo(dsi)) {
-		dev_err(dev, "waiting for header FIFO timed out\n");
-		return;
-	}
-
-	if (NEQV(xfer->flags & MIPI_DSI_MSG_USE_LPM,
-		 dsi->state & DSIM_STATE_CMD_LPM)) {
-		exynos_dsi_set_cmd_lpm(dsi, xfer->flags & MIPI_DSI_MSG_USE_LPM);
-		dsi->state ^= DSIM_STATE_CMD_LPM;
-	}
-
-	exynos_dsi_write(dsi, DSIM_PKTHDR_REG, reg);
-
-	if (xfer->flags & MIPI_DSI_MSG_REQ_ACK)
-		exynos_dsi_force_bta(dsi);
-}
-
-static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi,
-					struct exynos_dsi_transfer *xfer)
-{
-	u8 *payload = xfer->rx_payload + xfer->rx_done;
-	bool first = !xfer->rx_done;
-	struct device *dev = dsi->dev;
-	u16 length;
-	u32 reg;
-
-	if (first) {
-		reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG);
-
-		switch (reg & 0x3f) {
-		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
-		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
-			if (xfer->rx_len >= 2) {
-				payload[1] = reg >> 16;
-				++xfer->rx_done;
-			}
-			fallthrough;
-		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
-		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
-			payload[0] = reg >> 8;
-			++xfer->rx_done;
-			xfer->rx_len = xfer->rx_done;
-			xfer->result = 0;
-			goto clear_fifo;
-		case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
-			dev_err(dev, "DSI Error Report: 0x%04x\n",
-				(reg >> 8) & 0xffff);
-			xfer->result = 0;
-			goto clear_fifo;
-		}
-
-		length = (reg >> 8) & 0xffff;
-		if (length > xfer->rx_len) {
-			dev_err(dev,
-				"response too long (%u > %u bytes), stripping\n",
-				xfer->rx_len, length);
-			length = xfer->rx_len;
-		} else if (length < xfer->rx_len)
-			xfer->rx_len = length;
-	}
-
-	length = xfer->rx_len - xfer->rx_done;
-	xfer->rx_done += length;
-
-	/* Receive payload */
-	while (length >= 4) {
-		reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG);
-		payload[0] = (reg >>  0) & 0xff;
-		payload[1] = (reg >>  8) & 0xff;
-		payload[2] = (reg >> 16) & 0xff;
-		payload[3] = (reg >> 24) & 0xff;
-		payload += 4;
-		length -= 4;
-	}
-
-	if (length) {
-		reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG);
-		switch (length) {
-		case 3:
-			payload[2] = (reg >> 16) & 0xff;
-			fallthrough;
-		case 2:
-			payload[1] = (reg >> 8) & 0xff;
-			fallthrough;
-		case 1:
-			payload[0] = reg & 0xff;
-		}
-	}
-
-	if (xfer->rx_done == xfer->rx_len)
-		xfer->result = 0;
-
-clear_fifo:
-	length = DSI_RX_FIFO_SIZE / 4;
-	do {
-		reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG);
-		if (reg == DSI_RX_FIFO_EMPTY)
-			break;
-	} while (--length);
-}
-
-static void exynos_dsi_transfer_start(struct exynos_dsi *dsi)
-{
-	unsigned long flags;
-	struct exynos_dsi_transfer *xfer;
-	bool start = false;
-
-again:
-	spin_lock_irqsave(&dsi->transfer_lock, flags);
-
-	if (list_empty(&dsi->transfer_list)) {
-		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
-		return;
-	}
-
-	xfer = list_first_entry(&dsi->transfer_list,
-					struct exynos_dsi_transfer, list);
-
-	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
-
-	if (xfer->packet.payload_length &&
-	    xfer->tx_done == xfer->packet.payload_length)
-		/* waiting for RX */
-		return;
-
-	exynos_dsi_send_to_fifo(dsi, xfer);
-
-	if (xfer->packet.payload_length || xfer->rx_len)
-		return;
-
-	xfer->result = 0;
-	complete(&xfer->completed);
-
-	spin_lock_irqsave(&dsi->transfer_lock, flags);
-
-	list_del_init(&xfer->list);
-	start = !list_empty(&dsi->transfer_list);
-
-	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
-
-	if (start)
-		goto again;
-}
-
-static bool exynos_dsi_transfer_finish(struct exynos_dsi *dsi)
-{
-	struct exynos_dsi_transfer *xfer;
-	unsigned long flags;
-	bool start = true;
-
-	spin_lock_irqsave(&dsi->transfer_lock, flags);
-
-	if (list_empty(&dsi->transfer_list)) {
-		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
-		return false;
-	}
-
-	xfer = list_first_entry(&dsi->transfer_list,
-					struct exynos_dsi_transfer, list);
-
-	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
-
-	dev_dbg(dsi->dev,
-		"> xfer %pK, tx_len %zu, tx_done %u, rx_len %u, rx_done %u\n",
-		xfer, xfer->packet.payload_length, xfer->tx_done, xfer->rx_len,
-		xfer->rx_done);
-
-	if (xfer->tx_done != xfer->packet.payload_length)
-		return true;
-
-	if (xfer->rx_done != xfer->rx_len)
-		exynos_dsi_read_from_fifo(dsi, xfer);
-
-	if (xfer->rx_done != xfer->rx_len)
-		return true;
-
-	spin_lock_irqsave(&dsi->transfer_lock, flags);
-
-	list_del_init(&xfer->list);
-	start = !list_empty(&dsi->transfer_list);
-
-	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
-
-	if (!xfer->rx_len)
-		xfer->result = 0;
-	complete(&xfer->completed);
-
-	return start;
-}
-
-static void exynos_dsi_remove_transfer(struct exynos_dsi *dsi,
-					struct exynos_dsi_transfer *xfer)
-{
-	unsigned long flags;
-	bool start;
-
-	spin_lock_irqsave(&dsi->transfer_lock, flags);
-
-	if (!list_empty(&dsi->transfer_list) &&
-	    xfer == list_first_entry(&dsi->transfer_list,
-				     struct exynos_dsi_transfer, list)) {
-		list_del_init(&xfer->list);
-		start = !list_empty(&dsi->transfer_list);
-		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
-		if (start)
-			exynos_dsi_transfer_start(dsi);
-		return;
-	}
-
-	list_del_init(&xfer->list);
-
-	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
-}
-
-static int exynos_dsi_transfer(struct exynos_dsi *dsi,
-					struct exynos_dsi_transfer *xfer)
-{
-	unsigned long flags;
-	bool stopped;
-
-	xfer->tx_done = 0;
-	xfer->rx_done = 0;
-	xfer->result = -ETIMEDOUT;
-	init_completion(&xfer->completed);
-
-	spin_lock_irqsave(&dsi->transfer_lock, flags);
-
-	stopped = list_empty(&dsi->transfer_list);
-	list_add_tail(&xfer->list, &dsi->transfer_list);
-
-	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
-
-	if (stopped)
-		exynos_dsi_transfer_start(dsi);
-
-	wait_for_completion_timeout(&xfer->completed,
-				    msecs_to_jiffies(DSI_XFER_TIMEOUT_MS));
-	if (xfer->result == -ETIMEDOUT) {
-		struct mipi_dsi_packet *pkt = &xfer->packet;
-		exynos_dsi_remove_transfer(dsi, xfer);
-		dev_err(dsi->dev, "xfer timed out: %*ph %*ph\n", 4, pkt->header,
-			(int)pkt->payload_length, pkt->payload);
-		return -ETIMEDOUT;
-	}
-
-	/* Also covers hardware timeout condition */
-	return xfer->result;
-}
-
-static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
-{
-	struct exynos_dsi *dsi = dev_id;
-	u32 status;
-
-	status = exynos_dsi_read(dsi, DSIM_INTSRC_REG);
-	if (!status) {
-		static unsigned long int j;
-		if (printk_timed_ratelimit(&j, 500))
-			dev_warn(dsi->dev, "spurious interrupt\n");
-		return IRQ_HANDLED;
-	}
-	exynos_dsi_write(dsi, DSIM_INTSRC_REG, status);
-
-	if (status & DSIM_INT_SW_RST_RELEASE) {
-		u32 mask = ~(DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY |
-			DSIM_INT_SFR_HDR_FIFO_EMPTY | DSIM_INT_RX_ECC_ERR |
-			DSIM_INT_SW_RST_RELEASE);
-		exynos_dsi_write(dsi, DSIM_INTMSK_REG, mask);
-		complete(&dsi->completed);
-		return IRQ_HANDLED;
-	}
-
-	if (!(status & (DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY |
-			DSIM_INT_PLL_STABLE)))
-		return IRQ_HANDLED;
-
-	if (exynos_dsi_transfer_finish(dsi))
-		exynos_dsi_transfer_start(dsi);
-
-	return IRQ_HANDLED;
-}
-
-static irqreturn_t exynos_dsi_te_irq_handler(int irq, void *dev_id)
-{
-	struct exynos_dsi *dsi = dev_id;
-	const struct exynos_dsi_host_ops *ops = dsi->driver_data->host_ops;
-
-	if (ops && ops->te_handler &&
-	    (dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
-		ops->te_handler(dsi->dsi_host.dev);
-
-	return IRQ_HANDLED;
-}
-
-static void exynos_dsi_enable_irq(struct exynos_dsi *dsi)
-{
-	enable_irq(dsi->irq);
-
-	if (gpio_is_valid(dsi->te_gpio))
-		enable_irq(gpio_to_irq(dsi->te_gpio));
-}
-
-static void exynos_dsi_disable_irq(struct exynos_dsi *dsi)
-{
-	if (gpio_is_valid(dsi->te_gpio))
-		disable_irq(gpio_to_irq(dsi->te_gpio));
-
-	disable_irq(dsi->irq);
-}
-
-static int exynos_dsi_init(struct exynos_dsi *dsi)
+static int exynos_dsi_host_detach(struct device *dev,
+				  struct mipi_dsi_device *device)
 {
-	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
-
-	exynos_dsi_reset(dsi);
-	exynos_dsi_enable_irq(dsi);
+	struct exynos_dsi *dsi = dev_get_drvdata(dev);
+	struct drm_device *drm = dsi->encoder.dev;
 
-	if (driver_data->reg_values[RESET_TYPE] == DSIM_FUNCRST)
-		exynos_dsi_enable_lane(dsi, BIT(dsi->lanes) - 1);
-
-	exynos_dsi_enable_clock(dsi);
-	if (driver_data->wait_for_reset)
-		exynos_dsi_wait_for_reset(dsi);
-	exynos_dsi_set_phy_ctrl(dsi);
-	exynos_dsi_init_link(dsi);
+	if (drm->mode_config.poll_enabled)
+		drm_kms_helper_hotplug_event(drm);
 
 	return 0;
 }
 
-static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi,
-				      struct device *panel)
+static void exynos_dsi_te_handler(struct device *dev)
 {
-	int ret;
-	int te_gpio_irq;
+	struct exynos_dsi *dsi = dev_get_drvdata(dev);
 
-	dsi->te_gpio = of_get_named_gpio(panel->of_node, "te-gpios", 0);
-	if (dsi->te_gpio == -ENOENT)
-		return 0;
-
-	if (!gpio_is_valid(dsi->te_gpio)) {
-		ret = dsi->te_gpio;
-		dev_err(dsi->dev, "cannot get te-gpios, %d\n", ret);
-		goto out;
-	}
-
-	ret = gpio_request(dsi->te_gpio, "te_gpio");
-	if (ret) {
-		dev_err(dsi->dev, "gpio request failed with %d\n", ret);
-		goto out;
-	}
-
-	te_gpio_irq = gpio_to_irq(dsi->te_gpio);
-	irq_set_status_flags(te_gpio_irq, IRQ_NOAUTOEN);
-
-	ret = request_threaded_irq(te_gpio_irq, exynos_dsi_te_irq_handler, NULL,
-					IRQF_TRIGGER_RISING, "TE", dsi);
-	if (ret) {
-		dev_err(dsi->dev, "request interrupt failed with %d\n", ret);
-		gpio_free(dsi->te_gpio);
-		goto out;
-	}
-
-out:
-	return ret;
+	exynos_drm_crtc_te_handler(dsi->encoder.crtc);
 }
 
-static void exynos_dsi_unregister_te_irq(struct exynos_dsi *dsi)
-{
-	if (gpio_is_valid(dsi->te_gpio)) {
-		free_irq(gpio_to_irq(dsi->te_gpio), dsi);
-		gpio_free(dsi->te_gpio);
-		dsi->te_gpio = -ENOENT;
-	}
-}
-
-static void exynos_dsi_enable(struct exynos_dsi *dsi)
-{
-	struct drm_bridge *iter;
-	int ret;
-
-	if (dsi->state & DSIM_STATE_ENABLED)
-		return;
-
-	pm_runtime_get_sync(dsi->dev);
-	dsi->state |= DSIM_STATE_ENABLED;
-
-	if (dsi->panel) {
-		ret = drm_panel_prepare(dsi->panel);
-		if (ret < 0)
-			goto err_put_sync;
-	} else {
-		list_for_each_entry_reverse(iter, &dsi->bridge_chain,
-					    chain_node) {
-			if (iter->funcs->pre_enable)
-				iter->funcs->pre_enable(iter);
-		}
-	}
-
-	exynos_dsi_set_display_mode(dsi);
-	exynos_dsi_set_display_enable(dsi, true);
-
-	if (dsi->panel) {
-		ret = drm_panel_enable(dsi->panel);
-		if (ret < 0)
-			goto err_display_disable;
-	} else {
-		list_for_each_entry(iter, &dsi->bridge_chain, chain_node) {
-			if (iter->funcs->enable)
-				iter->funcs->enable(iter);
-		}
-	}
-
-	dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
-	return;
-
-err_display_disable:
-	exynos_dsi_set_display_enable(dsi, false);
-	drm_panel_unprepare(dsi->panel);
-
-err_put_sync:
-	dsi->state &= ~DSIM_STATE_ENABLED;
-	pm_runtime_put(dsi->dev);
-}
-
-static void exynos_dsi_disable(struct exynos_dsi *dsi)
-{
-	struct drm_bridge *iter;
-
-	if (!(dsi->state & DSIM_STATE_ENABLED))
-		return;
-
-	dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
-
-	drm_panel_disable(dsi->panel);
-
-	list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) {
-		if (iter->funcs->disable)
-			iter->funcs->disable(iter);
-	}
-
-	exynos_dsi_set_display_enable(dsi, false);
-	drm_panel_unprepare(dsi->panel);
-
-	list_for_each_entry(iter, &dsi->bridge_chain, chain_node) {
-		if (iter->funcs->post_disable)
-			iter->funcs->post_disable(iter);
-	}
-
-	dsi->state &= ~DSIM_STATE_ENABLED;
-	pm_runtime_put_sync(dsi->dev);
-}
-
-static enum drm_connector_status
-exynos_dsi_detect(struct drm_connector *connector, bool force)
-{
-	return connector->status;
-}
+static const struct samsung_dsim_host_ops exynos_dsi_host_ops = {
+	.attach = exynos_dsi_host_attach,
+	.detach = exynos_dsi_host_detach,
+	.te_handler = exynos_dsi_te_handler,
+};
 
-static void exynos_dsi_connector_destroy(struct drm_connector *connector)
-{
-	drm_connector_unregister(connector);
-	drm_connector_cleanup(connector);
-	connector->dev = NULL;
-}
+static const struct samsung_dsim_driver_data exynos3_dsi_driver_data = {
+	.reg_ofs = EXYNOS_REG_OFS,
+	.plltmr_reg = 0x50,
+	.has_freqband = 1,
+	.has_clklane_stop = 1,
+	.num_clks = 2,
+	.max_freq = 1000,
+	.wait_for_reset = 1,
+	.num_bits_resol = 11,
+	.reg_values = reg_values,
+	.host_ops = &exynos_dsi_host_ops,
+};
 
-static const struct drm_connector_funcs exynos_dsi_connector_funcs = {
-	.detect = exynos_dsi_detect,
-	.fill_modes = drm_helper_probe_single_connector_modes,
-	.destroy = exynos_dsi_connector_destroy,
-	.reset = drm_atomic_helper_connector_reset,
-	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
-	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+static const struct samsung_dsim_driver_data exynos4_dsi_driver_data = {
+	.reg_ofs = EXYNOS_REG_OFS,
+	.plltmr_reg = 0x50,
+	.has_freqband = 1,
+	.has_clklane_stop = 1,
+	.num_clks = 2,
+	.max_freq = 1000,
+	.wait_for_reset = 1,
+	.num_bits_resol = 11,
+	.reg_values = reg_values,
+	.host_ops = &exynos_dsi_host_ops,
 };
 
-static int exynos_dsi_get_modes(struct drm_connector *connector)
-{
-	struct exynos_dsi *dsi = connector_to_dsi(connector);
+static const struct samsung_dsim_driver_data exynos5_dsi_driver_data = {
+	.reg_ofs = EXYNOS_REG_OFS,
+	.plltmr_reg = 0x58,
+	.num_clks = 2,
+	.max_freq = 1000,
+	.wait_for_reset = 1,
+	.num_bits_resol = 11,
+	.reg_values = reg_values,
+	.host_ops = &exynos_dsi_host_ops,
+};
 
-	if (dsi->panel)
-		return drm_panel_get_modes(dsi->panel, connector);
+static const struct samsung_dsim_driver_data exynos5433_dsi_driver_data = {
+	.reg_ofs = EXYNOS5433_REG_OFS,
+	.plltmr_reg = 0xa0,
+	.has_clklane_stop = 1,
+	.num_clks = 5,
+	.max_freq = 1500,
+	.wait_for_reset = 0,
+	.num_bits_resol = 12,
+	.reg_values = exynos5433_reg_values,
+	.host_ops = &exynos_dsi_host_ops,
+};
 
-	return 0;
-}
+static const struct samsung_dsim_driver_data exynos5422_dsi_driver_data = {
+	.reg_ofs = EXYNOS5433_REG_OFS,
+	.plltmr_reg = 0xa0,
+	.has_clklane_stop = 1,
+	.num_clks = 2,
+	.max_freq = 1500,
+	.wait_for_reset = 1,
+	.num_bits_resol = 12,
+	.reg_values = exynos5422_reg_values,
+	.host_ops = &exynos_dsi_host_ops,
+};
 
-static const struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = {
-	.get_modes = exynos_dsi_get_modes,
+static const struct of_device_id exynos_dsi_of_match[] = {
+	{ .compatible = "samsung,exynos3250-mipi-dsi",
+	  .data = &exynos3_dsi_driver_data },
+	{ .compatible = "samsung,exynos4210-mipi-dsi",
+	  .data = &exynos4_dsi_driver_data },
+	{ .compatible = "samsung,exynos5410-mipi-dsi",
+	  .data = &exynos5_dsi_driver_data },
+	{ .compatible = "samsung,exynos5422-mipi-dsi",
+	  .data = &exynos5422_dsi_driver_data },
+	{ .compatible = "samsung,exynos5433-mipi-dsi",
+	  .data = &exynos5433_dsi_driver_data },
+	{ }
 };
 
-static int exynos_dsi_create_connector(struct exynos_dsi *dsi)
+static int exynos_dsi_bind(struct device *dev,
+			   struct device *master, void *data)
 {
-	struct drm_connector *connector = &dsi->connector;
-	struct drm_device *drm = dsi->bridge.dev;
+	struct exynos_dsi *dsi = dev_get_drvdata(dev);
+	struct drm_encoder *encoder = &dsi->encoder;
+	struct drm_device *drm_dev = data;
+	struct device_node *in_bridge_node;
+	struct drm_bridge *in_bridge;
 	int ret;
 
-	connector->polled = DRM_CONNECTOR_POLL_HPD;
+	drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_TMDS);
 
-	ret = drm_connector_init(drm, connector, &exynos_dsi_connector_funcs,
-				 DRM_MODE_CONNECTOR_DSI);
-	if (ret) {
-		DRM_DEV_ERROR(dsi->dev,
-			      "Failed to initialize connector with drm\n");
+	ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
+	if (ret < 0)
 		return ret;
-	}
-
-	connector->status = connector_status_disconnected;
-	drm_connector_helper_add(connector, &exynos_dsi_connector_helper_funcs);
-	drm_connector_attach_encoder(connector, dsi->bridge.encoder);
-	if (!drm->registered)
-		return 0;
-
-	connector->funcs->reset(connector);
-	drm_connector_register(connector);
-	return 0;
-}
-
-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(dsi);
-		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);
-		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);
+	in_bridge_node = of_graph_get_remote_node(dev->of_node, DSI_PORT_IN, 0);
+	if (in_bridge_node) {
+		in_bridge = of_drm_find_bridge(in_bridge_node);
+		if (in_bridge)
+			drm_bridge_attach(encoder, in_bridge, NULL, 0);
+		of_node_put(in_bridge_node);
 	}
-}
-
-static void exynos_dsi_bridge_enable(struct drm_bridge *bridge)
-{
-	struct exynos_dsi *dsi = bridge->driver_private;
-
-	exynos_dsi_enable(dsi);
-}
-
-static void exynos_dsi_bridge_disable(struct drm_bridge *bridge)
-{
-	struct exynos_dsi *dsi = bridge->driver_private;
-
-	exynos_dsi_disable(dsi);
-}
 
-static void exynos_dsi_bridge_mode_set(struct drm_bridge *bridge,
-				       const struct drm_display_mode *mode,
-				       const struct drm_display_mode *adjusted_mode)
-{
-	struct exynos_dsi *dsi = bridge->driver_private;
-
-	/* The mode is set when actually enabling the device. */
-	drm_mode_copy(&dsi->mode, adjusted_mode);
-}
-
-static const struct drm_bridge_funcs exynos_dsi_bridge_funcs = {
-	.attach = exynos_dsi_bridge_attach,
-	.detach = exynos_dsi_bridge_detach,
-	.enable = exynos_dsi_bridge_enable,
-	.disable = exynos_dsi_bridge_disable,
-	.mode_set = exynos_dsi_bridge_mode_set,
-};
-
-static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
-				  struct mipi_dsi_device *device)
-{
-	struct exynos_dsi *dsi = host_to_dsi(host);
-	const struct exynos_dsi_host_ops *ops = dsi->driver_data->host_ops;
-	struct drm_bridge *out_bridge;
-
-	out_bridge = of_drm_find_bridge(device->dev.of_node);
-	if (out_bridge) {
-		dsi->out_bridge = out_bridge;
-	} else {
-		dsi->panel = of_drm_find_panel(device->dev.of_node);
-		if (IS_ERR(dsi->panel))
-			dsi->panel = NULL;
-		else
-			dsi->connector.status = connector_status_connected;
-	}
-
-	/*
-	 * This is a temporary solution and should be made by more generic way.
-	 *
-	 * If attached panel device is for command mode one, dsi should register
-	 * TE interrupt handler.
-	 */
-	if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO)) {
-		int ret = exynos_dsi_register_te_irq(dsi, &device->dev);
-		if (ret)
-			return ret;
-	}
-
-	dsi->lanes = device->lanes;
-	dsi->format = device->format;
-	dsi->mode_flags = device->mode_flags;
-
-	if (ops && ops->attach)
-		ops->attach(dsi->dsi_host.dev, device);
+	ret = samsung_dsim_bind(dsi->dsi, encoder);
+	if (ret)
+		goto err;
 
 	return 0;
-}
-
-static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
-				  struct mipi_dsi_device *device)
-{
-	struct exynos_dsi *dsi = host_to_dsi(host);
-	const struct exynos_dsi_host_ops *ops = dsi->driver_data->host_ops;
 
-	if (ops && ops->detach)
-		ops->detach(dsi->dsi_host.dev, device);
-
-	exynos_dsi_unregister_te_irq(dsi);
-
-	return 0;
+err:
+	drm_encoder_cleanup(encoder);
+	return ret;
 }
 
-static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host,
-					 const struct mipi_dsi_msg *msg)
+static void exynos_dsi_unbind(struct device *dev,
+			      struct device *master, void *data)
 {
-	struct exynos_dsi *dsi = host_to_dsi(host);
-	struct exynos_dsi_transfer xfer;
-	int ret;
+	struct exynos_dsi *dsi = dev_get_drvdata(dev);
+	struct drm_encoder *encoder = &dsi->encoder;
 
-	if (!(dsi->state & DSIM_STATE_ENABLED))
-		return -EINVAL;
-
-	if (!(dsi->state & DSIM_STATE_INITIALIZED)) {
-		ret = exynos_dsi_init(dsi);
-		if (ret)
-			return ret;
-		dsi->state |= DSIM_STATE_INITIALIZED;
-	}
-
-	ret = mipi_dsi_create_packet(&xfer.packet, msg);
-	if (ret < 0)
-		return ret;
+	samsung_dsim_unbind(dsi->dsi);
 
-	xfer.rx_len = msg->rx_len;
-	xfer.rx_payload = msg->rx_buf;
-	xfer.flags = msg->flags;
-
-	ret = exynos_dsi_transfer(dsi, &xfer);
-	return (ret < 0) ? ret : xfer.rx_done;
+	drm_encoder_cleanup(encoder);
 }
 
-static const struct mipi_dsi_host_ops exynos_dsi_ops = {
-	.attach = exynos_dsi_host_attach,
-	.detach = exynos_dsi_host_detach,
-	.transfer = exynos_dsi_host_transfer,
+static const struct component_ops exynos_dsi_component_ops = {
+	.bind	= exynos_dsi_bind,
+	.unbind	= exynos_dsi_unbind,
 };
 
-static int exynos_dsi_of_read_u32(const struct device_node *np,
-				  const char *propname, u32 *out_value)
-{
-	int ret = of_property_read_u32(np, propname, out_value);
-
-	if (ret < 0)
-		pr_err("%pOF: failed to get '%s' property\n", np, propname);
-
-	return ret;
-}
-
-static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
-{
-	struct device *dev = dsi->dev;
-	struct device_node *node = dev->of_node;
-	int ret;
-
-	ret = exynos_dsi_of_read_u32(node, "samsung,pll-clock-frequency",
-				     &dsi->pll_clk_rate);
-	if (ret < 0)
-		return ret;
-
-	ret = exynos_dsi_of_read_u32(node, "samsung,burst-clock-frequency",
-				     &dsi->burst_clk_rate);
-	if (ret < 0)
-		return ret;
-
-	ret = exynos_dsi_of_read_u32(node, "samsung,esc-clock-frequency",
-				     &dsi->esc_clk_rate);
-	if (ret < 0)
-		return ret;
-
-	return 0;
-}
-
-static struct exynos_dsi *__exynos_dsi_probe(struct platform_device *pdev)
+static int 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;
+	struct device *dev = &pdev->dev;
+	int ret;
 
 	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
 	if (!dsi)
-		return ERR_PTR(-ENOMEM);
+		return -ENOMEM;
+	platform_set_drvdata(pdev, dsi);
 
-	/* To be checked as invalid one */
-	dsi->te_gpio = -ENOENT;
+	dsi->dsi = samsung_dsim_probe(pdev);
+	if (IS_ERR(dsi->dsi))
+		return PTR_ERR(dsi->dsi);
 
-	init_completion(&dsi->completed);
-	spin_lock_init(&dsi->transfer_lock);
-	INIT_LIST_HEAD(&dsi->transfer_list);
-	INIT_LIST_HEAD(&dsi->bridge_chain);
+	pm_runtime_enable(dev);
 
-	dsi->dsi_host.ops = &exynos_dsi_ops;
-	dsi->dsi_host.dev = dev;
-
-	dsi->dev = dev;
-	dsi->driver_data = of_device_get_match_data(dev);
-
-	dsi->supplies[0].supply = "vddcore";
-	dsi->supplies[1].supply = "vddio";
-	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(dsi->supplies),
-				      dsi->supplies);
-	if (ret) {
-		if (ret != -EPROBE_DEFER)
-			dev_info(dev, "failed to get regulators: %d\n", ret);
-		return ERR_PTR(ret);
-	}
-
-	dsi->clks = devm_kcalloc(dev,
-			dsi->driver_data->num_clks, sizeof(*dsi->clks),
-			GFP_KERNEL);
-	if (!dsi->clks)
-		return ERR_PTR(-ENOMEM);
-
-	for (i = 0; i < dsi->driver_data->num_clks; i++) {
-		dsi->clks[i] = devm_clk_get(dev, clk_names[i]);
-		if (IS_ERR(dsi->clks[i])) {
-			if (strcmp(clk_names[i], "sclk_mipi") == 0) {
-				dsi->clks[i] = devm_clk_get(dev,
-							OLD_SCLK_MIPI_CLK_NAME);
-				if (!IS_ERR(dsi->clks[i]))
-					continue;
-			}
-
-			dev_info(dev, "failed to get the clock: %s\n",
-					clk_names[i]);
-			return ERR_PTR(PTR_ERR(dsi->clks[i]));
-		}
-	}
-
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	dsi->reg_base = devm_ioremap_resource(dev, res);
-	if (IS_ERR(dsi->reg_base)) {
-		dev_err(dev, "failed to remap io region\n");
-		return dsi->reg_base;
-	}
-
-	dsi->phy = devm_phy_get(dev, "dsim");
-	if (IS_ERR(dsi->phy)) {
-		dev_info(dev, "failed to get dsim phy\n");
-		return ERR_PTR(PTR_ERR(dsi->phy));
-	}
-
-	dsi->irq = platform_get_irq(pdev, 0);
-	if (dsi->irq < 0)
-		return ERR_PTR(dsi->irq);
-
-	irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
-	ret = devm_request_threaded_irq(dev, dsi->irq, NULL,
-					exynos_dsi_irq, IRQF_ONESHOT,
-					dev_name(dev), dsi);
-	if (ret) {
-		dev_err(dev, "failed to request dsi irq\n");
-		return ERR_PTR(ret);
-	}
-
-	ret = exynos_dsi_parse_dt(dsi);
+	ret = component_add(dev, &exynos_dsi_component_ops);
 	if (ret)
-		return ERR_PTR(ret);
+		goto err_disable_runtime;
 
-	ret = mipi_dsi_host_register(&dsi->dsi_host);
-	if (ret)
-		return ERR_PTR(ret);
+	return 0;
 
-	bridge = &dsi->bridge;
-	bridge->driver_private = dsi;
-	bridge->funcs = &exynos_dsi_bridge_funcs;
-	bridge->of_node = dev->of_node;
-	drm_bridge_add(bridge);
+err_disable_runtime:
+	pm_runtime_disable(dev);
 
-	return dsi;
+	return ret;
 }
 
-static void __exynos_dsi_remove(struct exynos_dsi *dsi)
+static int exynos_dsi_remove(struct platform_device *pdev)
 {
-	drm_bridge_remove(&dsi->bridge);
+	struct exynos_dsi *dsi = platform_get_drvdata(pdev);
 
-	mipi_dsi_host_unregister(&dsi->dsi_host);
-}
-
-/*
- * Probe/remove API, used from platforms based on the DRM bridge API.
- */
-struct exynos_dsi *exynos_dsi_probe(struct platform_device *pdev)
-{
-	return __exynos_dsi_probe(pdev);
-}
+	pm_runtime_disable(&pdev->dev);
 
-void exynos_dsi_remove(struct exynos_dsi *dsi)
-{
-	return __exynos_dsi_remove(dsi);
-}
+	samsung_dsim_remove(dsi->dsi);
 
-/*
- * Bind/unbind API, used from platforms based on the component framework.
- */
-int exynos_dsi_bind(struct exynos_dsi *dsi, struct drm_encoder *encoder)
-{
-	struct drm_bridge *previous = drm_bridge_chain_get_first_bridge(encoder);
+	component_del(&pdev->dev, &exynos_dsi_component_ops);
 
-	return drm_bridge_attach(encoder, &dsi->bridge, previous, 0);
-}
-
-void exynos_dsi_unbind(struct exynos_dsi *dsi)
-{
-	exynos_dsi_disable(dsi);
+	return 0;
 }
 
-int exynos_dsi_suspend(struct exynos_dsi *dsi)
+static int __maybe_unused exynos_dsi_suspend(struct device *dev)
 {
-	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
-	int ret, i;
-
-	usleep_range(10000, 20000);
-
-	if (dsi->state & DSIM_STATE_INITIALIZED) {
-		dsi->state &= ~DSIM_STATE_INITIALIZED;
-
-		exynos_dsi_disable_clock(dsi);
-
-		exynos_dsi_disable_irq(dsi);
-	}
-
-	dsi->state &= ~DSIM_STATE_CMD_LPM;
-
-	phy_power_off(dsi->phy);
-
-	for (i = driver_data->num_clks - 1; i > -1; i--)
-		clk_disable_unprepare(dsi->clks[i]);
-
-	ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
-	if (ret < 0)
-		dev_err(dsi->dev, "cannot disable regulators %d\n", ret);
+	struct exynos_dsi *dsi = dev_get_drvdata(dev);
 
-	return 0;
+	return samsung_dsim_suspend(dsi->dsi);
 }
 
-int exynos_dsi_resume(struct exynos_dsi *dsi)
+static int __maybe_unused exynos_dsi_resume(struct device *dev)
 {
-	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
-	int ret, i;
-
-	ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
-	if (ret < 0) {
-		dev_err(dsi->dev, "cannot enable regulators %d\n", ret);
-		return ret;
-	}
-
-	for (i = 0; i < driver_data->num_clks; i++) {
-		ret = clk_prepare_enable(dsi->clks[i]);
-		if (ret < 0)
-			goto err_clk;
-	}
-
-	ret = phy_power_on(dsi->phy);
-	if (ret < 0) {
-		dev_err(dsi->dev, "cannot enable phy %d\n", ret);
-		goto err_clk;
-	}
+	struct exynos_dsi *dsi = dev_get_drvdata(dev);
 
-	return 0;
+	return samsung_dsim_resume(dsi->dsi);
+}
 
-err_clk:
-	while (--i > -1)
-		clk_disable_unprepare(dsi->clks[i]);
-	regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
+static const struct dev_pm_ops exynos_dsi_pm_ops = {
+	SET_RUNTIME_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				pm_runtime_force_resume)
+};
 
-	return ret;
-}
+struct platform_driver dsi_driver = {
+	.probe = exynos_dsi_probe,
+	.remove = exynos_dsi_remove,
+	.driver = {
+		   .name = "exynos-dsi",
+		   .owner = THIS_MODULE,
+		   .pm = &exynos_dsi_pm_ops,
+		   .of_match_table = exynos_dsi_of_match,
+	},
+};
 
 MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
 MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c b/drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c
deleted file mode 100644
index 79d9ec6ade45..000000000000
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c
+++ /dev/null
@@ -1,333 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Samsung SoC MIPI DSI Master driver.
- *
- * Copyright (c) 2014 Samsung Electronics Co., Ltd
- *
- * Contacts: Tomasz Figa <t.figa@samsung.com>
- */
-
-#include <linux/component.h>
-#include <linux/of_device.h>
-#include <linux/of_graph.h>
-#include <linux/pm_runtime.h>
-
-#include <drm/drm_bridge.h>
-#include <drm/drm_encoder.h>
-#include <drm/drm_mipi_dsi.h>
-#include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
-
-#include "exynos_drm_crtc.h"
-#include "exynos_drm_drv.h"
-#include "exynos_drm_dsi.h"
-
-enum {
-	DSI_PORT_IN,
-	DSI_PORT_OUT
-};
-
-struct exynos_dsi_pltfm {
-	struct exynos_dsi *dsi;
-	struct drm_encoder encoder;
-};
-
-static const unsigned int reg_values[] = {
-	[RESET_TYPE] = DSIM_SWRST,
-	[PLL_TIMER] = 500,
-	[STOP_STATE_CNT] = 0xf,
-	[PHYCTRL_ULPS_EXIT] = 0x0af,
-	[PHYCTRL_VREG_LP] = 0,
-	[PHYCTRL_SLEW_UP] = 0,
-	[PHYTIMING_LPX] = 0x06,
-	[PHYTIMING_HS_EXIT] = 0x0b,
-	[PHYTIMING_CLK_PREPARE] = 0x07,
-	[PHYTIMING_CLK_ZERO] = 0x27,
-	[PHYTIMING_CLK_POST] = 0x0d,
-	[PHYTIMING_CLK_TRAIL] = 0x08,
-	[PHYTIMING_HS_PREPARE] = 0x09,
-	[PHYTIMING_HS_ZERO] = 0x0d,
-	[PHYTIMING_HS_TRAIL] = 0x0b,
-};
-
-static const unsigned int exynos5422_reg_values[] = {
-	[RESET_TYPE] = DSIM_SWRST,
-	[PLL_TIMER] = 500,
-	[STOP_STATE_CNT] = 0xf,
-	[PHYCTRL_ULPS_EXIT] = 0xaf,
-	[PHYCTRL_VREG_LP] = 0,
-	[PHYCTRL_SLEW_UP] = 0,
-	[PHYTIMING_LPX] = 0x08,
-	[PHYTIMING_HS_EXIT] = 0x0d,
-	[PHYTIMING_CLK_PREPARE] = 0x09,
-	[PHYTIMING_CLK_ZERO] = 0x30,
-	[PHYTIMING_CLK_POST] = 0x0e,
-	[PHYTIMING_CLK_TRAIL] = 0x0a,
-	[PHYTIMING_HS_PREPARE] = 0x0c,
-	[PHYTIMING_HS_ZERO] = 0x11,
-	[PHYTIMING_HS_TRAIL] = 0x0d,
-};
-
-static const unsigned int exynos5433_reg_values[] = {
-	[RESET_TYPE] = DSIM_FUNCRST,
-	[PLL_TIMER] = 22200,
-	[STOP_STATE_CNT] = 0xa,
-	[PHYCTRL_ULPS_EXIT] = 0x190,
-	[PHYCTRL_VREG_LP] = 1,
-	[PHYCTRL_SLEW_UP] = 1,
-	[PHYTIMING_LPX] = 0x07,
-	[PHYTIMING_HS_EXIT] = 0x0c,
-	[PHYTIMING_CLK_PREPARE] = 0x09,
-	[PHYTIMING_CLK_ZERO] = 0x2d,
-	[PHYTIMING_CLK_POST] = 0x0e,
-	[PHYTIMING_CLK_TRAIL] = 0x09,
-	[PHYTIMING_HS_PREPARE] = 0x0b,
-	[PHYTIMING_HS_ZERO] = 0x10,
-	[PHYTIMING_HS_TRAIL] = 0x0c,
-};
-
-static int __exynos_dsi_host_attach(struct device *dev,
-				    struct mipi_dsi_device *device)
-{
-	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
-	struct drm_device *drm = dsi->encoder.dev;
-	struct exynos_drm_crtc *crtc;
-
-	mutex_lock(&drm->mode_config.mutex);
-	crtc = exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD);
-	crtc->i80_mode = !(device->mode_flags & MIPI_DSI_MODE_VIDEO);
-	mutex_unlock(&drm->mode_config.mutex);
-
-	if (drm->mode_config.poll_enabled)
-		drm_kms_helper_hotplug_event(drm);
-
-	return 0;
-}
-
-static int __exynos_dsi_host_detach(struct device *dev,
-				    struct mipi_dsi_device *device)
-{
-	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
-	struct drm_device *drm = dsi->encoder.dev;
-
-	if (drm->mode_config.poll_enabled)
-		drm_kms_helper_hotplug_event(drm);
-
-	return 0;
-}
-
-static void __exynos_dsi_te_handler(struct device *dev)
-{
-	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
-
-	exynos_drm_crtc_te_handler(dsi->encoder.crtc);
-}
-
-static const struct exynos_dsi_host_ops exynos_dsi_host_ops = {
-	.attach = __exynos_dsi_host_attach,
-	.detach = __exynos_dsi_host_detach,
-	.te_handler = __exynos_dsi_te_handler,
-};
-
-static const struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
-	.reg_ofs = EXYNOS_REG_OFS,
-	.plltmr_reg = 0x50,
-	.has_freqband = 1,
-	.has_clklane_stop = 1,
-	.num_clks = 2,
-	.max_freq = 1000,
-	.wait_for_reset = 1,
-	.num_bits_resol = 11,
-	.reg_values = reg_values,
-	.host_ops = &exynos_dsi_host_ops,
-};
-
-static const struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
-	.reg_ofs = EXYNOS_REG_OFS,
-	.plltmr_reg = 0x50,
-	.has_freqband = 1,
-	.has_clklane_stop = 1,
-	.num_clks = 2,
-	.max_freq = 1000,
-	.wait_for_reset = 1,
-	.num_bits_resol = 11,
-	.reg_values = reg_values,
-	.host_ops = &exynos_dsi_host_ops,
-};
-
-static const struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
-	.reg_ofs = EXYNOS_REG_OFS,
-	.plltmr_reg = 0x58,
-	.num_clks = 2,
-	.max_freq = 1000,
-	.wait_for_reset = 1,
-	.num_bits_resol = 11,
-	.reg_values = reg_values,
-	.host_ops = &exynos_dsi_host_ops,
-};
-
-static const struct exynos_dsi_driver_data exynos5433_dsi_driver_data = {
-	.reg_ofs = EXYNOS5433_REG_OFS,
-	.plltmr_reg = 0xa0,
-	.has_clklane_stop = 1,
-	.num_clks = 5,
-	.max_freq = 1500,
-	.wait_for_reset = 0,
-	.num_bits_resol = 12,
-	.reg_values = exynos5433_reg_values,
-	.host_ops = &exynos_dsi_host_ops,
-};
-
-static const struct exynos_dsi_driver_data exynos5422_dsi_driver_data = {
-	.reg_ofs = EXYNOS5433_REG_OFS,
-	.plltmr_reg = 0xa0,
-	.has_clklane_stop = 1,
-	.num_clks = 2,
-	.max_freq = 1500,
-	.wait_for_reset = 1,
-	.num_bits_resol = 12,
-	.reg_values = exynos5422_reg_values,
-	.host_ops = &exynos_dsi_host_ops,
-};
-
-static const struct of_device_id exynos_dsi_of_match[] = {
-	{ .compatible = "samsung,exynos3250-mipi-dsi",
-	  .data = &exynos3_dsi_driver_data },
-	{ .compatible = "samsung,exynos4210-mipi-dsi",
-	  .data = &exynos4_dsi_driver_data },
-	{ .compatible = "samsung,exynos5410-mipi-dsi",
-	  .data = &exynos5_dsi_driver_data },
-	{ .compatible = "samsung,exynos5422-mipi-dsi",
-	  .data = &exynos5422_dsi_driver_data },
-	{ .compatible = "samsung,exynos5433-mipi-dsi",
-	  .data = &exynos5433_dsi_driver_data },
-	{ }
-};
-
-static int exynos_dsi_pltfm_bind(struct device *dev, struct device *master, void *data)
-{
-	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
-	struct drm_encoder *encoder = &dsi->encoder;
-	struct drm_device *drm_dev = data;
-	struct device_node *in_bridge_node;
-	struct drm_bridge *in_bridge;
-	int ret;
-
-	drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_TMDS);
-
-	ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
-	if (ret < 0)
-		return ret;
-
-	in_bridge_node = of_graph_get_remote_node(dev->of_node, DSI_PORT_IN, 0);
-	if (in_bridge_node) {
-		in_bridge = of_drm_find_bridge(in_bridge_node);
-		if (in_bridge)
-			drm_bridge_attach(encoder, in_bridge, NULL, 0);
-		of_node_put(in_bridge_node);
-	}
-
-	ret = exynos_dsi_bind(dsi->dsi, encoder);
-	if (ret)
-		goto err;
-
-	return 0;
-
-err:
-	drm_encoder_cleanup(encoder);
-	return ret;
-}
-
-static void exynos_dsi_pltfm_unbind(struct device *dev, struct device *master,
-				    void *data)
-{
-	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
-	struct drm_encoder *encoder = &dsi->encoder;
-
-	exynos_dsi_unbind(dsi->dsi);
-
-	drm_encoder_cleanup(encoder);
-}
-
-static const struct component_ops exynos_dsi_pltfm_component_ops = {
-	.bind	= exynos_dsi_pltfm_bind,
-	.unbind	= exynos_dsi_pltfm_unbind,
-};
-
-static int exynos_dsi_pltfm_probe(struct platform_device *pdev)
-{
-	struct exynos_dsi_pltfm *dsi;
-	struct device *dev = &pdev->dev;
-	int ret;
-
-	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
-	if (!dsi)
-		return -ENOMEM;
-	platform_set_drvdata(pdev, dsi);
-
-	dsi->dsi = exynos_dsi_probe(pdev);
-	if (IS_ERR(dsi->dsi))
-		return PTR_ERR(dsi->dsi);
-
-	pm_runtime_enable(dev);
-
-	ret = component_add(dev, &exynos_dsi_pltfm_component_ops);
-	if (ret)
-		goto err_disable_runtime;
-
-	return 0;
-
-err_disable_runtime:
-	pm_runtime_disable(dev);
-
-	return ret;
-}
-
-static int exynos_dsi_pltfm_remove(struct platform_device *pdev)
-{
-	struct exynos_dsi_pltfm *dsi = platform_get_drvdata(pdev);
-
-	pm_runtime_disable(&pdev->dev);
-
-	exynos_dsi_remove(dsi->dsi);
-
-	component_del(&pdev->dev, &exynos_dsi_pltfm_component_ops);
-
-	return 0;
-}
-
-static int __maybe_unused exynos_dsi_pltfm_suspend(struct device *dev)
-{
-	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
-
-	return exynos_dsi_suspend(dsi->dsi);
-}
-
-static int __maybe_unused exynos_dsi_pltfm_resume(struct device *dev)
-{
-	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
-
-	return exynos_dsi_resume(dsi->dsi);
-}
-
-static const struct dev_pm_ops exynos_dsi_pm_ops = {
-	SET_RUNTIME_PM_OPS(exynos_dsi_pltfm_suspend, exynos_dsi_pltfm_resume, NULL)
-	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
-				pm_runtime_force_resume)
-};
-
-struct platform_driver dsi_driver = {
-	.probe = exynos_dsi_pltfm_probe,
-	.remove = exynos_dsi_pltfm_remove,
-	.driver = {
-		   .name = "exynos-dsi",
-		   .owner = THIS_MODULE,
-		   .pm = &exynos_dsi_pm_ops,
-		   .of_match_table = exynos_dsi_of_match,
-	},
-};
-
-MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
-MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC MIPI DSI Master");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.h b/include/drm/bridge/samsung-dsim.h
similarity index 69%
rename from drivers/gpu/drm/exynos/exynos_drm_dsi.h
rename to include/drm/bridge/samsung-dsim.h
index 8fa3276889de..be8b4913aa9c 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.h
+++ b/include/drm/bridge/samsung-dsim.h
@@ -3,7 +3,7 @@
 #define __EXYNOS_DRM_DSI__
 
 struct drm_encoder;
-struct exynos_dsi;
+struct samsung_dsim;
 struct platform_device;
 struct mipi_dsi_device;
 
@@ -12,13 +12,13 @@ enum exynos_reg_offset {
 	EXYNOS5433_REG_OFS
 };
 
-struct exynos_dsi *exynos_dsi_probe(struct platform_device *pdev);
-void exynos_dsi_remove(struct exynos_dsi *dsi);
-int exynos_dsi_bind(struct exynos_dsi *dsi, struct drm_encoder *encoder);
-void exynos_dsi_unbind(struct exynos_dsi *dsi);
+struct samsung_dsim *samsung_dsim_probe(struct platform_device *pdev);
+void samsung_dsim_remove(struct samsung_dsim *dsi);
+int samsung_dsim_bind(struct samsung_dsim *dsi, struct drm_encoder *encoder);
+void samsung_dsim_unbind(struct samsung_dsim *dsi);
 
-int exynos_dsi_suspend(struct exynos_dsi *dsi);
-int exynos_dsi_resume(struct exynos_dsi *dsi);
+int samsung_dsim_suspend(struct samsung_dsim *dsi);
+int samsung_dsim_resume(struct samsung_dsim *dsi);
 
 enum reg_value_idx {
 	RESET_TYPE,
@@ -42,13 +42,13 @@ enum reg_value_idx {
 #define DSIM_FUNCRST			(1 << 16)
 #define DSIM_SWRST			(1 << 0)
 
-struct exynos_dsi_host_ops {
+struct samsung_dsim_host_ops {
 	int (*attach)(struct device *dev, struct mipi_dsi_device *device);
 	int (*detach)(struct device *dev, struct mipi_dsi_device *device);
 	void (*te_handler)(struct device *dev);
 };
 
-struct exynos_dsi_driver_data {
+struct samsung_dsim_driver_data {
 	enum exynos_reg_offset reg_ofs;
 	unsigned int plltmr_reg;
 	unsigned int has_freqband:1;
@@ -58,7 +58,7 @@ struct exynos_dsi_driver_data {
 	unsigned int wait_for_reset;
 	unsigned int num_bits_resol;
 	const unsigned int *reg_values;
-	const struct exynos_dsi_host_ops *host_ops;
+	const struct samsung_dsim_host_ops *host_ops;
 };
 
 #endif /* __EXYNOS_DRM_DSI__ */
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 10/16] drm/exynos: implement a drm bridge
  2020-09-11 13:54   ` [PATCH v2 10/16] drm/exynos: implement a drm bridge Michael Tretter
@ 2020-09-14  8:29     ` Marek Szyprowski
  2020-09-14 12:31       ` Marek Szyprowski
  0 siblings, 1 reply; 61+ messages in thread
From: Marek Szyprowski @ 2020-09-14  8:29 UTC (permalink / raw)
  To: Michael Tretter, dri-devel, linux-samsung-soc
  Cc: jy0922.shim, b.zolnierkie, narmstrong, sw0312.kim, krzk, a.hajda,
	Laurent.pinchart, kernel, sylvester.nawrocki

Hi Michael,

On 11.09.2020 15:54, Michael Tretter wrote:
> 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@pengutronix.de>

This one (and the whole series applied) still fails on Exynos boards:

[drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
OF: graph: no port node found in /soc/dsi@11c80000
8<--- cut here ---
Unable to handle kernel NULL pointer dereference at virtual address 00000084
pgd = (ptrval)
[00000084] *pgd=00000000
Internal error: Oops: 5 [#1] PREEMPT SMP ARM
Modules linked in:
CPU: 1 PID: 1 Comm: swapper/0 Not tainted 
5.9.0-rc4-next-20200911-00010-g417dc70d70ec #1608
Hardware name: Samsung Exynos (Flattened Device Tree)
PC is at drm_bridge_attach+0x18/0x164
LR is at exynos_dsi_bind+0x88/0xa8
pc : [<c0628c08>]    lr : [<c064d560>]    psr: 20000013
sp : ef0dfca8  ip : 00000002  fp : c13190e0
r10: 00000000  r9 : ee46d580  r8 : c13190e0
r7 : ee438800  r6 : 00000018  r5 : ef253810  r4 : ef39e840
r3 : 00000000  r2 : 00000018  r1 : ef39e888  r0 : ef39e840
Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
Control: 10c5387d  Table: 4000404a  DAC: 00000051
Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
Stack: (0xef0dfca8 to 0xef0e0000)
...
[<c0628c08>] (drm_bridge_attach) from [<c064d560>] 
(exynos_dsi_bind+0x88/0xa8)
[<c064d560>] (exynos_dsi_bind) from [<c066a800>] 
(component_bind_all+0xfc/0x290)
[<c066a800>] (component_bind_all) from [<c0649dc0>] 
(exynos_drm_bind+0xe4/0x19c)
[<c0649dc0>] (exynos_drm_bind) from [<c066ad74>] 
(try_to_bring_up_master+0x1e4/0x2c4)
[<c066ad74>] (try_to_bring_up_master) from [<c066b2b4>] 
(component_master_add_with_match+0xd4/0x108)
[<c066b2b4>] (component_master_add_with_match) from [<c0649ae8>] 
(exynos_drm_platform_probe+0xe4/0x110)
[<c0649ae8>] (exynos_drm_platform_probe) from [<c0674e6c>] 
(platform_drv_probe+0x6c/0xa4)
[<c0674e6c>] (platform_drv_probe) from [<c067242c>] 
(really_probe+0x200/0x4fc)
[<c067242c>] (really_probe) from [<c06728f0>] 
(driver_probe_device+0x78/0x1fc)
[<c06728f0>] (driver_probe_device) from [<c0672cd8>] 
(device_driver_attach+0x58/0x60)
[<c0672cd8>] (device_driver_attach) from [<c0672dbc>] 
(__driver_attach+0xdc/0x174)
[<c0672dbc>] (__driver_attach) from [<c06701b4>] 
(bus_for_each_dev+0x68/0xb4)
[<c06701b4>] (bus_for_each_dev) from [<c06714e8>] 
(bus_add_driver+0x158/0x214)
[<c06714e8>] (bus_add_driver) from [<c0673c1c>] (driver_register+0x78/0x110)
[<c0673c1c>] (driver_register) from [<c0649ca8>] 
(exynos_drm_init+0xe4/0x118)
[<c0649ca8>] (exynos_drm_init) from [<c0102484>] 
(do_one_initcall+0x8c/0x42c)
[<c0102484>] (do_one_initcall) from [<c11011c0>] 
(kernel_init_freeable+0x190/0x1dc)
[<c11011c0>] (kernel_init_freeable) from [<c0af7880>] 
(kernel_init+0x8/0x118)
[<c0af7880>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
Exception stack(0xef0dffb0 to 0xef0dfff8)
...
---[ end trace ee27f313f9ed9da1 ]---

# arm-linux-gnueabi-addr2line -e vmlinux c0628c08
drivers/gpu/drm/drm_bridge.c:184 (discriminator 1)

I will try to debug it a bit more today.

> ---
> 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);
>   }
>   

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 10/16] drm/exynos: implement a drm bridge
  2020-09-14  8:29     ` Marek Szyprowski
@ 2020-09-14 12:31       ` Marek Szyprowski
  2020-09-14 20:01         ` Michael Tretter
  0 siblings, 1 reply; 61+ messages in thread
From: Marek Szyprowski @ 2020-09-14 12:31 UTC (permalink / raw)
  To: Michael Tretter, dri-devel, linux-samsung-soc
  Cc: jy0922.shim, b.zolnierkie, narmstrong, sw0312.kim, krzk, a.hajda,
	Laurent.pinchart, kernel, sylvester.nawrocki

Hi,

On 14.09.2020 10:29, Marek Szyprowski wrote:
> On 11.09.2020 15:54, Michael Tretter wrote:
>> 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@pengutronix.de>
> This one (and the whole series applied) still fails on Exynos boards:
>
> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> OF: graph: no port node found in /soc/dsi@11c80000
> 8<--- cut here ---
> Unable to handle kernel NULL pointer dereference at virtual address 00000084
> pgd = (ptrval)
> [00000084] *pgd=00000000
> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> Modules linked in:
> CPU: 1 PID: 1 Comm: swapper/0 Not tainted
> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec #1608
> Hardware name: Samsung Exynos (Flattened Device Tree)
> PC is at drm_bridge_attach+0x18/0x164
> LR is at exynos_dsi_bind+0x88/0xa8
> pc : [<c0628c08>]    lr : [<c064d560>]    psr: 20000013
> sp : ef0dfca8  ip : 00000002  fp : c13190e0
> r10: 00000000  r9 : ee46d580  r8 : c13190e0
> r7 : ee438800  r6 : 00000018  r5 : ef253810  r4 : ef39e840
> r3 : 00000000  r2 : 00000018  r1 : ef39e888  r0 : ef39e840
> Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> Stack: (0xef0dfca8 to 0xef0e0000)
> ...
> [<c0628c08>] (drm_bridge_attach) from [<c064d560>]
> (exynos_dsi_bind+0x88/0xa8)
> [<c064d560>] (exynos_dsi_bind) from [<c066a800>]
> (component_bind_all+0xfc/0x290)
> [<c066a800>] (component_bind_all) from [<c0649dc0>]
> (exynos_drm_bind+0xe4/0x19c)
> [<c0649dc0>] (exynos_drm_bind) from [<c066ad74>]
> (try_to_bring_up_master+0x1e4/0x2c4)
> [<c066ad74>] (try_to_bring_up_master) from [<c066b2b4>]
> (component_master_add_with_match+0xd4/0x108)
> [<c066b2b4>] (component_master_add_with_match) from [<c0649ae8>]
> (exynos_drm_platform_probe+0xe4/0x110)
> [<c0649ae8>] (exynos_drm_platform_probe) from [<c0674e6c>]
> (platform_drv_probe+0x6c/0xa4)
> [<c0674e6c>] (platform_drv_probe) from [<c067242c>]
> (really_probe+0x200/0x4fc)
> [<c067242c>] (really_probe) from [<c06728f0>]
> (driver_probe_device+0x78/0x1fc)
> [<c06728f0>] (driver_probe_device) from [<c0672cd8>]
> (device_driver_attach+0x58/0x60)
> [<c0672cd8>] (device_driver_attach) from [<c0672dbc>]
> (__driver_attach+0xdc/0x174)
> [<c0672dbc>] (__driver_attach) from [<c06701b4>]
> (bus_for_each_dev+0x68/0xb4)
> [<c06701b4>] (bus_for_each_dev) from [<c06714e8>]
> (bus_add_driver+0x158/0x214)
> [<c06714e8>] (bus_add_driver) from [<c0673c1c>] (driver_register+0x78/0x110)
> [<c0673c1c>] (driver_register) from [<c0649ca8>]
> (exynos_drm_init+0xe4/0x118)
> [<c0649ca8>] (exynos_drm_init) from [<c0102484>]
> (do_one_initcall+0x8c/0x42c)
> [<c0102484>] (do_one_initcall) from [<c11011c0>]
> (kernel_init_freeable+0x190/0x1dc)
> [<c11011c0>] (kernel_init_freeable) from [<c0af7880>]
> (kernel_init+0x8/0x118)
> [<c0af7880>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> Exception stack(0xef0dffb0 to 0xef0dfff8)
> ...
> ---[ end trace ee27f313f9ed9da1 ]---
>
> # arm-linux-gnueabi-addr2line -e vmlinux c0628c08
> drivers/gpu/drm/drm_bridge.c:184 (discriminator 1)
>
> I will try to debug it a bit more today.

The above crash has been caused by lack of in_bridge initialization to 
NULL in exynos_dsi_bind() in this patch. However, fixing it reveals 
another issue:

[drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
OF: graph: no port node found in /soc/dsi@11c80000
8<--- cut here ---
Unable to handle kernel NULL pointer dereference at virtual address 00000280
pgd = (ptrval)
[00000280] *pgd=00000000
Internal error: Oops: 5 [#1] PREEMPT SMP ARM
Modules linked in:
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 
5.9.0-rc4-next-20200911-00010-g417dc70d70ec-dirty #1613
Hardware name: Samsung Exynos (Flattened Device Tree)
PC is at __mutex_lock+0x54/0xb18
LR is at lock_is_held_type+0x80/0x138
pc : [<c0afc920>]    lr : [<c0af63e8>]    psr: 60000013
sp : ef0dfd30  ip : 33937b74  fp : c13193c8
r10: c1208eec  r9 : 00000000  r8 : ee45f808
r7 : c19561a4  r6 : 00000000  r5 : 00000000  r4 : 0000024c
r3 : 00000000  r2 : 00204140  r1 : c124f13c  r0 : 00000000
Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
Control: 10c5387d  Table: 4000404a  DAC: 00000051
Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
Stack: (0xef0dfd30 to 0xef0e0000)
...
[<c0afc920>] (__mutex_lock) from [<c0afd400>] (mutex_lock_nested+0x1c/0x24)
[<c0afd400>] (mutex_lock_nested) from [<c064d4b8>] 
(__exynos_dsi_host_attach+0x20/0x6c)
[<c064d4b8>] (__exynos_dsi_host_attach) from [<c064d914>] 
(exynos_dsi_host_attach+0x70/0x194)
[<c064d914>] (exynos_dsi_host_attach) from [<c0656b64>] 
(s6e8aa0_probe+0x1b0/0x218)
[<c0656b64>] (s6e8aa0_probe) from [<c0672530>] (really_probe+0x200/0x4fc)
[<c0672530>] (really_probe) from [<c06729f4>] 
(driver_probe_device+0x78/0x1fc)
[<c06729f4>] (driver_probe_device) from [<c0672ddc>] 
(device_driver_attach+0x58/0x60)
[<c0672ddc>] (device_driver_attach) from [<c0672ec0>] 
(__driver_attach+0xdc/0x174)
[<c0672ec0>] (__driver_attach) from [<c06702b8>] 
(bus_for_each_dev+0x68/0xb4)
[<c06702b8>] (bus_for_each_dev) from [<c06715ec>] 
(bus_add_driver+0x158/0x214)
[<c06715ec>] (bus_add_driver) from [<c0673d20>] (driver_register+0x78/0x110)
[<c0673d20>] (driver_register) from [<c0102484>] 
(do_one_initcall+0x8c/0x42c)
[<c0102484>] (do_one_initcall) from [<c11011c0>] 
(kernel_init_freeable+0x190/0x1dc)
[<c11011c0>] (kernel_init_freeable) from [<c0af7988>] 
(kernel_init+0x8/0x118)
[<c0af7988>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
Exception stack(0xef0dffb0 to 0xef0dfff8)
...
---[ end trace c06e996ec2e8234d ]---

This means that dsi->encoder.dev is not initialized in 
__exynos_dsi_host_attach().

This happens, because drm_bridge_attach() in exynos_dsi_bind() returned 
earlier -517 (deferred probe), what causes cleanup of encoder and 
release of all drm resources.

Then however, the panel tries to register itself and 
exynos_dsi_host_attach() tries to access the released encoder (which is 
zeroed in drm_encoder_release) and rest of resources, what causes failure.

It looks that something is missing. Maybe mipi host has to be registered 
later, when bridge is ready? I have no idea how it is handled before 
this patch. Andrzej, could you comment it a bit?

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 10/16] drm/exynos: implement a drm bridge
  2020-09-14 12:31       ` Marek Szyprowski
@ 2020-09-14 20:01         ` Michael Tretter
  2020-09-14 21:19           ` Andrzej Hajda
  0 siblings, 1 reply; 61+ messages in thread
From: Michael Tretter @ 2020-09-14 20:01 UTC (permalink / raw)
  To: Marek Szyprowski
  Cc: linux-samsung-soc, jy0922.shim, narmstrong, b.zolnierkie,
	sw0312.kim, krzk, a.hajda, dri-devel, kernel, sylvester.nawrocki,
	Laurent.pinchart

Hi,

On Mon, 14 Sep 2020 14:31:19 +0200, Marek Szyprowski wrote:
> On 14.09.2020 10:29, Marek Szyprowski wrote:
> > On 11.09.2020 15:54, Michael Tretter wrote:
> >> 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@pengutronix.de>
> > This one (and the whole series applied) still fails on Exynos boards:
> >
> > [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
> > exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> > OF: graph: no port node found in /soc/dsi@11c80000
> > 8<--- cut here ---
> > Unable to handle kernel NULL pointer dereference at virtual address 00000084
> > pgd = (ptrval)
> > [00000084] *pgd=00000000
> > Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> > Modules linked in:
> > CPU: 1 PID: 1 Comm: swapper/0 Not tainted
> > 5.9.0-rc4-next-20200911-00010-g417dc70d70ec #1608
> > Hardware name: Samsung Exynos (Flattened Device Tree)
> > PC is at drm_bridge_attach+0x18/0x164
> > LR is at exynos_dsi_bind+0x88/0xa8
> > pc : [<c0628c08>]    lr : [<c064d560>]    psr: 20000013
> > sp : ef0dfca8  ip : 00000002  fp : c13190e0
> > r10: 00000000  r9 : ee46d580  r8 : c13190e0
> > r7 : ee438800  r6 : 00000018  r5 : ef253810  r4 : ef39e840
> > r3 : 00000000  r2 : 00000018  r1 : ef39e888  r0 : ef39e840
> > Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> > Control: 10c5387d  Table: 4000404a  DAC: 00000051
> > Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> > Stack: (0xef0dfca8 to 0xef0e0000)
> > ...
> > [<c0628c08>] (drm_bridge_attach) from [<c064d560>]
> > (exynos_dsi_bind+0x88/0xa8)
> > [<c064d560>] (exynos_dsi_bind) from [<c066a800>]
> > (component_bind_all+0xfc/0x290)
> > [<c066a800>] (component_bind_all) from [<c0649dc0>]
> > (exynos_drm_bind+0xe4/0x19c)
> > [<c0649dc0>] (exynos_drm_bind) from [<c066ad74>]
> > (try_to_bring_up_master+0x1e4/0x2c4)
> > [<c066ad74>] (try_to_bring_up_master) from [<c066b2b4>]
> > (component_master_add_with_match+0xd4/0x108)
> > [<c066b2b4>] (component_master_add_with_match) from [<c0649ae8>]
> > (exynos_drm_platform_probe+0xe4/0x110)
> > [<c0649ae8>] (exynos_drm_platform_probe) from [<c0674e6c>]
> > (platform_drv_probe+0x6c/0xa4)
> > [<c0674e6c>] (platform_drv_probe) from [<c067242c>]
> > (really_probe+0x200/0x4fc)
> > [<c067242c>] (really_probe) from [<c06728f0>]
> > (driver_probe_device+0x78/0x1fc)
> > [<c06728f0>] (driver_probe_device) from [<c0672cd8>]
> > (device_driver_attach+0x58/0x60)
> > [<c0672cd8>] (device_driver_attach) from [<c0672dbc>]
> > (__driver_attach+0xdc/0x174)
> > [<c0672dbc>] (__driver_attach) from [<c06701b4>]
> > (bus_for_each_dev+0x68/0xb4)
> > [<c06701b4>] (bus_for_each_dev) from [<c06714e8>]
> > (bus_add_driver+0x158/0x214)
> > [<c06714e8>] (bus_add_driver) from [<c0673c1c>] (driver_register+0x78/0x110)
> > [<c0673c1c>] (driver_register) from [<c0649ca8>]
> > (exynos_drm_init+0xe4/0x118)
> > [<c0649ca8>] (exynos_drm_init) from [<c0102484>]
> > (do_one_initcall+0x8c/0x42c)
> > [<c0102484>] (do_one_initcall) from [<c11011c0>]
> > (kernel_init_freeable+0x190/0x1dc)
> > [<c11011c0>] (kernel_init_freeable) from [<c0af7880>]
> > (kernel_init+0x8/0x118)
> > [<c0af7880>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> > Exception stack(0xef0dffb0 to 0xef0dfff8)
> > ...
> > ---[ end trace ee27f313f9ed9da1 ]---
> >
> > # arm-linux-gnueabi-addr2line -e vmlinux c0628c08
> > drivers/gpu/drm/drm_bridge.c:184 (discriminator 1)
> >
> > I will try to debug it a bit more today.
> 
> The above crash has been caused by lack of in_bridge initialization to 
> NULL in exynos_dsi_bind() in this patch. However, fixing it reveals 
> another issue:
> 
> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> OF: graph: no port node found in /soc/dsi@11c80000
> 8<--- cut here ---
> Unable to handle kernel NULL pointer dereference at virtual address 00000280
> pgd = (ptrval)
> [00000280] *pgd=00000000
> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> Modules linked in:
> CPU: 0 PID: 1 Comm: swapper/0 Not tainted 
> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec-dirty #1613
> Hardware name: Samsung Exynos (Flattened Device Tree)
> PC is at __mutex_lock+0x54/0xb18
> LR is at lock_is_held_type+0x80/0x138
> pc : [<c0afc920>]    lr : [<c0af63e8>]    psr: 60000013
> sp : ef0dfd30  ip : 33937b74  fp : c13193c8
> r10: c1208eec  r9 : 00000000  r8 : ee45f808
> r7 : c19561a4  r6 : 00000000  r5 : 00000000  r4 : 0000024c
> r3 : 00000000  r2 : 00204140  r1 : c124f13c  r0 : 00000000
> Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> Stack: (0xef0dfd30 to 0xef0e0000)
> ...
> [<c0afc920>] (__mutex_lock) from [<c0afd400>] (mutex_lock_nested+0x1c/0x24)
> [<c0afd400>] (mutex_lock_nested) from [<c064d4b8>] 
> (__exynos_dsi_host_attach+0x20/0x6c)
> [<c064d4b8>] (__exynos_dsi_host_attach) from [<c064d914>] 
> (exynos_dsi_host_attach+0x70/0x194)
> [<c064d914>] (exynos_dsi_host_attach) from [<c0656b64>] 
> (s6e8aa0_probe+0x1b0/0x218)
> [<c0656b64>] (s6e8aa0_probe) from [<c0672530>] (really_probe+0x200/0x4fc)
> [<c0672530>] (really_probe) from [<c06729f4>] 
> (driver_probe_device+0x78/0x1fc)
> [<c06729f4>] (driver_probe_device) from [<c0672ddc>] 
> (device_driver_attach+0x58/0x60)
> [<c0672ddc>] (device_driver_attach) from [<c0672ec0>] 
> (__driver_attach+0xdc/0x174)
> [<c0672ec0>] (__driver_attach) from [<c06702b8>] 
> (bus_for_each_dev+0x68/0xb4)
> [<c06702b8>] (bus_for_each_dev) from [<c06715ec>] 
> (bus_add_driver+0x158/0x214)
> [<c06715ec>] (bus_add_driver) from [<c0673d20>] (driver_register+0x78/0x110)
> [<c0673d20>] (driver_register) from [<c0102484>] 
> (do_one_initcall+0x8c/0x42c)
> [<c0102484>] (do_one_initcall) from [<c11011c0>] 
> (kernel_init_freeable+0x190/0x1dc)
> [<c11011c0>] (kernel_init_freeable) from [<c0af7988>] 
> (kernel_init+0x8/0x118)
> [<c0af7988>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> Exception stack(0xef0dffb0 to 0xef0dfff8)
> ...
> ---[ end trace c06e996ec2e8234d ]---
> 
> This means that dsi->encoder.dev is not initialized in 
> __exynos_dsi_host_attach().
> 
> This happens, because drm_bridge_attach() in exynos_dsi_bind() returned 
> earlier -517 (deferred probe), what causes cleanup of encoder and 
> release of all drm resources.
> 
> Then however, the panel tries to register itself and 
> exynos_dsi_host_attach() tries to access the released encoder (which is 
> zeroed in drm_encoder_release) and rest of resources, what causes failure.
> 
> It looks that something is missing. Maybe mipi host has to be registered 
> later, when bridge is ready? I have no idea how it is handled before 
> this patch. Andrzej, could you comment it a bit?

I intentionally changed the order, because if another bridge follows in the
pipeline, the probe of the drm driver has to be deferred until some bridge
provides a connector. The next bridge registers itself via the host_attach
function and the deferral is ensured via the bind for the bind/unbind API or
the bridge_attach function otherwise.

On the other hand, the bridge does not have an encoder until the mipi device
has been attached.

As a solution, the exynos dsi driver must initialize the encoder in
exynos_dsi_probe instead of in exynos_dsi_bind and access the encoder via
exynos_dsi instead of the bridge.

Can you try to move everything except samsung_dsim_bind from exynos_dsi_bind
to exynos_dsi_probe (respectively for unbind) and report if it fixes the
crash.

Michael
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 10/16] drm/exynos: implement a drm bridge
  2020-09-14 20:01         ` Michael Tretter
@ 2020-09-14 21:19           ` Andrzej Hajda
  2020-09-15 19:40             ` Andrzej Hajda
  0 siblings, 1 reply; 61+ messages in thread
From: Andrzej Hajda @ 2020-09-14 21:19 UTC (permalink / raw)
  To: Michael Tretter, Marek Szyprowski
  Cc: linux-samsung-soc, jy0922.shim, b.zolnierkie, narmstrong,
	sw0312.kim, krzk, dri-devel, kernel, sylvester.nawrocki,
	Laurent.pinchart

Hi Marek, Michael,

On 14.09.2020 22:01, Michael Tretter wrote:
> Hi,
>
> On Mon, 14 Sep 2020 14:31:19 +0200, Marek Szyprowski wrote:
>> On 14.09.2020 10:29, Marek Szyprowski wrote:
>>> On 11.09.2020 15:54, Michael Tretter wrote:
>>>> 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@pengutronix.de>
>>> This one (and the whole series applied) still fails on Exynos boards:
>>>
>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
>>> OF: graph: no port node found in /soc/dsi@11c80000
>>> 8<--- cut here ---
>>> Unable to handle kernel NULL pointer dereference at virtual address 00000084
>>> pgd = (ptrval)
>>> [00000084] *pgd=00000000
>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
>>> Modules linked in:
>>> CPU: 1 PID: 1 Comm: swapper/0 Not tainted
>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec #1608
>>> Hardware name: Samsung Exynos (Flattened Device Tree)
>>> PC is at drm_bridge_attach+0x18/0x164
>>> LR is at exynos_dsi_bind+0x88/0xa8
>>> pc : [<c0628c08>]    lr : [<c064d560>]    psr: 20000013
>>> sp : ef0dfca8  ip : 00000002  fp : c13190e0
>>> r10: 00000000  r9 : ee46d580  r8 : c13190e0
>>> r7 : ee438800  r6 : 00000018  r5 : ef253810  r4 : ef39e840
>>> r3 : 00000000  r2 : 00000018  r1 : ef39e888  r0 : ef39e840
>>> Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
>>> Stack: (0xef0dfca8 to 0xef0e0000)
>>> ...
>>> [<c0628c08>] (drm_bridge_attach) from [<c064d560>]
>>> (exynos_dsi_bind+0x88/0xa8)
>>> [<c064d560>] (exynos_dsi_bind) from [<c066a800>]
>>> (component_bind_all+0xfc/0x290)
>>> [<c066a800>] (component_bind_all) from [<c0649dc0>]
>>> (exynos_drm_bind+0xe4/0x19c)
>>> [<c0649dc0>] (exynos_drm_bind) from [<c066ad74>]
>>> (try_to_bring_up_master+0x1e4/0x2c4)
>>> [<c066ad74>] (try_to_bring_up_master) from [<c066b2b4>]
>>> (component_master_add_with_match+0xd4/0x108)
>>> [<c066b2b4>] (component_master_add_with_match) from [<c0649ae8>]
>>> (exynos_drm_platform_probe+0xe4/0x110)
>>> [<c0649ae8>] (exynos_drm_platform_probe) from [<c0674e6c>]
>>> (platform_drv_probe+0x6c/0xa4)
>>> [<c0674e6c>] (platform_drv_probe) from [<c067242c>]
>>> (really_probe+0x200/0x4fc)
>>> [<c067242c>] (really_probe) from [<c06728f0>]
>>> (driver_probe_device+0x78/0x1fc)
>>> [<c06728f0>] (driver_probe_device) from [<c0672cd8>]
>>> (device_driver_attach+0x58/0x60)
>>> [<c0672cd8>] (device_driver_attach) from [<c0672dbc>]
>>> (__driver_attach+0xdc/0x174)
>>> [<c0672dbc>] (__driver_attach) from [<c06701b4>]
>>> (bus_for_each_dev+0x68/0xb4)
>>> [<c06701b4>] (bus_for_each_dev) from [<c06714e8>]
>>> (bus_add_driver+0x158/0x214)
>>> [<c06714e8>] (bus_add_driver) from [<c0673c1c>] (driver_register+0x78/0x110)
>>> [<c0673c1c>] (driver_register) from [<c0649ca8>]
>>> (exynos_drm_init+0xe4/0x118)
>>> [<c0649ca8>] (exynos_drm_init) from [<c0102484>]
>>> (do_one_initcall+0x8c/0x42c)
>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
>>> (kernel_init_freeable+0x190/0x1dc)
>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7880>]
>>> (kernel_init+0x8/0x118)
>>> [<c0af7880>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
>>> ...
>>> ---[ end trace ee27f313f9ed9da1 ]---
>>>
>>> # arm-linux-gnueabi-addr2line -e vmlinux c0628c08
>>> drivers/gpu/drm/drm_bridge.c:184 (discriminator 1)
>>>
>>> I will try to debug it a bit more today.
>> The above crash has been caused by lack of in_bridge initialization to
>> NULL in exynos_dsi_bind() in this patch. However, fixing it reveals
>> another issue:
>>
>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
>> OF: graph: no port node found in /soc/dsi@11c80000
>> 8<--- cut here ---
>> Unable to handle kernel NULL pointer dereference at virtual address 00000280
>> pgd = (ptrval)
>> [00000280] *pgd=00000000
>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
>> Modules linked in:
>> CPU: 0 PID: 1 Comm: swapper/0 Not tainted
>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec-dirty #1613
>> Hardware name: Samsung Exynos (Flattened Device Tree)
>> PC is at __mutex_lock+0x54/0xb18
>> LR is at lock_is_held_type+0x80/0x138
>> pc : [<c0afc920>]    lr : [<c0af63e8>]    psr: 60000013
>> sp : ef0dfd30  ip : 33937b74  fp : c13193c8
>> r10: c1208eec  r9 : 00000000  r8 : ee45f808
>> r7 : c19561a4  r6 : 00000000  r5 : 00000000  r4 : 0000024c
>> r3 : 00000000  r2 : 00204140  r1 : c124f13c  r0 : 00000000
>> Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
>> Stack: (0xef0dfd30 to 0xef0e0000)
>> ...
>> [<c0afc920>] (__mutex_lock) from [<c0afd400>] (mutex_lock_nested+0x1c/0x24)
>> [<c0afd400>] (mutex_lock_nested) from [<c064d4b8>]
>> (__exynos_dsi_host_attach+0x20/0x6c)
>> [<c064d4b8>] (__exynos_dsi_host_attach) from [<c064d914>]
>> (exynos_dsi_host_attach+0x70/0x194)
>> [<c064d914>] (exynos_dsi_host_attach) from [<c0656b64>]
>> (s6e8aa0_probe+0x1b0/0x218)
>> [<c0656b64>] (s6e8aa0_probe) from [<c0672530>] (really_probe+0x200/0x4fc)
>> [<c0672530>] (really_probe) from [<c06729f4>]
>> (driver_probe_device+0x78/0x1fc)
>> [<c06729f4>] (driver_probe_device) from [<c0672ddc>]
>> (device_driver_attach+0x58/0x60)
>> [<c0672ddc>] (device_driver_attach) from [<c0672ec0>]
>> (__driver_attach+0xdc/0x174)
>> [<c0672ec0>] (__driver_attach) from [<c06702b8>]
>> (bus_for_each_dev+0x68/0xb4)
>> [<c06702b8>] (bus_for_each_dev) from [<c06715ec>]
>> (bus_add_driver+0x158/0x214)
>> [<c06715ec>] (bus_add_driver) from [<c0673d20>] (driver_register+0x78/0x110)
>> [<c0673d20>] (driver_register) from [<c0102484>]
>> (do_one_initcall+0x8c/0x42c)
>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
>> (kernel_init_freeable+0x190/0x1dc)
>> [<c11011c0>] (kernel_init_freeable) from [<c0af7988>]
>> (kernel_init+0x8/0x118)
>> [<c0af7988>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
>> Exception stack(0xef0dffb0 to 0xef0dfff8)
>> ...
>> ---[ end trace c06e996ec2e8234d ]---
>>
>> This means that dsi->encoder.dev is not initialized in
>> __exynos_dsi_host_attach().
>>
>> This happens, because drm_bridge_attach() in exynos_dsi_bind() returned
>> earlier -517 (deferred probe), what causes cleanup of encoder and
>> release of all drm resources.
>>
>> Then however, the panel tries to register itself and
>> exynos_dsi_host_attach() tries to access the released encoder (which is
>> zeroed in drm_encoder_release) and rest of resources, what causes failure.
>>
>> It looks that something is missing. Maybe mipi host has to be registered
>> later, when bridge is ready? I have no idea how it is handled before
>> this patch. Andrzej, could you comment it a bit?
> I intentionally changed the order, because if another bridge follows in the
> pipeline, the probe of the drm driver has to be deferred until some bridge
> provides a connector. The next bridge registers itself via the host_attach
> function and the deferral is ensured via the bind for the bind/unbind API or
> the bridge_attach function otherwise.
>
> On the other hand, the bridge does not have an encoder until the mipi device
> has been attached.
>
> As a solution, the exynos dsi driver must initialize the encoder in
> exynos_dsi_probe instead of in exynos_dsi_bind and access the encoder via
> exynos_dsi instead of the bridge.
>
> Can you try to move everything except samsung_dsim_bind from exynos_dsi_bind
> to exynos_dsi_probe (respectively for unbind) and report if it fixes the
> crash.


The original behaviour is that encoder (exynos_dsi) is registered 
regardless of sink presence (initially panel, later also bridge) - it 
avoids multiple issues with deferred probe, device driver bind/unbind 
and module load/unload. Appearance or disappearance of sink is reported 
to host nicely via DSI attach/detach callbacks - and it is reflected in 
drm world as change state of the connector.

Registering DSI host in bind and unregistering in unbind assures that if 
mipi_dsi device is attached/detached the drm device is always present - 
it makes device/driver binding race free and allows to avoid additional 
locking.

Moving DSI host registration to probe changes everything, for sure it 
breaks the nice feature of DSI attach/detach callbacks and apparently 
can cause different issues depending on device bind order.

I will try to look at the patches tomorrow and maybe I can find more 
constructive comments :)


Regards

Andrzej


>
> Michael
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://protect2.fireeye.com/v1/url?k=4f0be936-129547ac-4f0a6279-0cc47a336fae-e9aecfc5418740e8&q=1&e=1d4b0871-5b85-47f3-9506-79c768643aee&u=https%3A%2F%2Flists.freedesktop.org%2Fmailman%2Flistinfo%2Fdri-devel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 08/16] drm/exynos: add host_ops callback for platform drivers
  2020-09-11 13:54   ` [PATCH v2 08/16] drm/exynos: add host_ops callback for platform drivers Michael Tretter
@ 2020-09-15 17:07     ` Andrzej Hajda
  2020-09-15 18:02       ` Michael Tretter
  0 siblings, 1 reply; 61+ messages in thread
From: Andrzej Hajda @ 2020-09-15 17:07 UTC (permalink / raw)
  To: Michael Tretter, dri-devel, linux-samsung-soc
  Cc: jy0922.shim, narmstrong, b.zolnierkie, sw0312.kim, krzk,
	Laurent.pinchart, kernel, sylvester.nawrocki


W dniu 11.09.2020 o 15:54, Michael Tretter pisze:
> Platform drivers need to be aware if a mipi dsi device attaches or
> detaches. Add host_ops to the driver_data for attach and detach
> callbacks and move the i80 mode selection and the hotplug handling into
> the callback, because these depend on the drm driver.
>
> Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
> ---
> v2:
> - new patch
> ---
>   drivers/gpu/drm/exynos/exynos_drm_dsi.c | 64 ++++++++++++++++++++-----
>   1 file changed, 53 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> index 1a15ae71205d..684a2fbef08a 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> @@ -239,6 +239,12 @@ struct exynos_dsi_transfer {
>   #define DSIM_STATE_CMD_LPM		BIT(2)
>   #define DSIM_STATE_VIDOUT_AVAILABLE	BIT(3)
>   
> +struct exynos_dsi;
> +struct exynos_dsi_host_ops {
> +	int (*attach)(struct device *dev, struct mipi_dsi_device *device);
> +	int (*detach)(struct device *dev, struct mipi_dsi_device *device);
> +};
> +
>   enum exynos_reg_offset {
>   	EXYNOS_REG_OFS,
>   	EXYNOS5433_REG_OFS
> @@ -254,6 +260,7 @@ struct exynos_dsi_driver_data {
>   	unsigned int wait_for_reset;
>   	unsigned int num_bits_resol;
>   	const unsigned int *reg_values;
> +	const struct exynos_dsi_host_ops *host_ops;
>   };
>   
>   struct exynos_dsi {
> @@ -467,6 +474,41 @@ static const unsigned int exynos5433_reg_values[] = {
>   	[PHYTIMING_HS_TRAIL] = 0x0c,
>   };
>   
> +static int __exynos_dsi_host_attach(struct device *dev,
> +				    struct mipi_dsi_device *device)
> +{
> +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> +	struct drm_device *drm = dsi->encoder.dev;


As I wrote yesterday drm device was guaranteed to be present here only 
if mipi dsi host registration was performed in component bind. In case 
it is performed in probe it will be always NULL, and the code does not 
make sense.


Regards

Andrzej



> +	struct exynos_drm_crtc *crtc;
> +
> +	mutex_lock(&drm->mode_config.mutex);
> +	crtc = exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD);
> +	crtc->i80_mode = !(device->mode_flags & MIPI_DSI_MODE_VIDEO);
> +	mutex_unlock(&drm->mode_config.mutex);
> +
> +	if (drm->mode_config.poll_enabled)
> +		drm_kms_helper_hotplug_event(drm);
> +
> +	return 0;
> +}
> +
> +static int __exynos_dsi_host_detach(struct device *dev,
> +				     struct mipi_dsi_device *device)
> +{
> +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> +	struct drm_device *drm = dsi->encoder.dev;
> +
> +	if (drm->mode_config.poll_enabled)
> +		drm_kms_helper_hotplug_event(drm);
> +
> +	return 0;
> +}
> +
> +static const struct exynos_dsi_host_ops exynos_dsi_host_ops = {
> +	.attach = __exynos_dsi_host_attach,
> +	.detach = __exynos_dsi_host_detach,
> +};
> +
>   static const struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
>   	.reg_ofs = EXYNOS_REG_OFS,
>   	.plltmr_reg = 0x50,
> @@ -477,6 +519,7 @@ static const struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
>   	.wait_for_reset = 1,
>   	.num_bits_resol = 11,
>   	.reg_values = reg_values,
> +	.host_ops = &exynos_dsi_host_ops,
>   };
>   
>   static const struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
> @@ -489,6 +532,7 @@ static const struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
>   	.wait_for_reset = 1,
>   	.num_bits_resol = 11,
>   	.reg_values = reg_values,
> +	.host_ops = &exynos_dsi_host_ops,
>   };
>   
>   static const struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
> @@ -499,6 +543,7 @@ static const struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
>   	.wait_for_reset = 1,
>   	.num_bits_resol = 11,
>   	.reg_values = reg_values,
> +	.host_ops = &exynos_dsi_host_ops,
>   };
>   
>   static const struct exynos_dsi_driver_data exynos5433_dsi_driver_data = {
> @@ -510,6 +555,7 @@ static const struct exynos_dsi_driver_data exynos5433_dsi_driver_data = {
>   	.wait_for_reset = 0,
>   	.num_bits_resol = 12,
>   	.reg_values = exynos5433_reg_values,
> +	.host_ops = &exynos_dsi_host_ops,
>   };
>   
>   static const struct exynos_dsi_driver_data exynos5422_dsi_driver_data = {
> @@ -521,6 +567,7 @@ static const struct exynos_dsi_driver_data exynos5422_dsi_driver_data = {
>   	.wait_for_reset = 1,
>   	.num_bits_resol = 12,
>   	.reg_values = exynos5422_reg_values,
> +	.host_ops = &exynos_dsi_host_ops,
>   };
>   
>   static const struct of_device_id exynos_dsi_of_match[] = {
> @@ -1551,8 +1598,8 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
>   				  struct mipi_dsi_device *device)
>   {
>   	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_device *drm = encoder->dev;
>   	struct drm_bridge *out_bridge;
>   
>   	out_bridge  = of_drm_find_bridge(device->dev.of_node);
> @@ -1590,18 +1637,12 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
>   			return ret;
>   	}
>   
> -	mutex_lock(&drm->mode_config.mutex);
> -
>   	dsi->lanes = device->lanes;
>   	dsi->format = device->format;
>   	dsi->mode_flags = device->mode_flags;
> -	exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD)->i80_mode =
> -			!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO);
>   
> -	mutex_unlock(&drm->mode_config.mutex);
> -
> -	if (drm->mode_config.poll_enabled)
> -		drm_kms_helper_hotplug_event(drm);
> +	if (ops && ops->attach)
> +		ops->attach(dsi->dsi_host.dev, device);
>   
>   	return 0;
>   }
> @@ -1610,6 +1651,7 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
>   				  struct mipi_dsi_device *device)
>   {
>   	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) {
> @@ -1625,8 +1667,8 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
>   		INIT_LIST_HEAD(&dsi->bridge_chain);
>   	}
>   
> -	if (drm->mode_config.poll_enabled)
> -		drm_kms_helper_hotplug_event(drm);
> +	if (ops && ops->detach)
> +		ops->detach(dsi->dsi_host.dev, device);
>   
>   	exynos_dsi_unregister_te_irq(dsi);
>   
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 08/16] drm/exynos: add host_ops callback for platform drivers
  2020-09-15 17:07     ` Andrzej Hajda
@ 2020-09-15 18:02       ` Michael Tretter
  2020-09-16 22:01         ` Andrzej Hajda
  0 siblings, 1 reply; 61+ messages in thread
From: Michael Tretter @ 2020-09-15 18:02 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: linux-samsung-soc, jy0922.shim, narmstrong, b.zolnierkie,
	sw0312.kim, dri-devel, krzk, kernel, sylvester.nawrocki,
	Laurent.pinchart

On Tue, 15 Sep 2020 19:07:59 +0200, Andrzej Hajda wrote:
> 
> W dniu 11.09.2020 o 15:54, Michael Tretter pisze:
> > Platform drivers need to be aware if a mipi dsi device attaches or
> > detaches. Add host_ops to the driver_data for attach and detach
> > callbacks and move the i80 mode selection and the hotplug handling into
> > the callback, because these depend on the drm driver.
> >
> > Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
> > ---
> > v2:
> > - new patch
> > ---
> >   drivers/gpu/drm/exynos/exynos_drm_dsi.c | 64 ++++++++++++++++++++-----
> >   1 file changed, 53 insertions(+), 11 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > index 1a15ae71205d..684a2fbef08a 100644
> > --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > @@ -239,6 +239,12 @@ struct exynos_dsi_transfer {
> >   #define DSIM_STATE_CMD_LPM		BIT(2)
> >   #define DSIM_STATE_VIDOUT_AVAILABLE	BIT(3)
> >   
> > +struct exynos_dsi;
> > +struct exynos_dsi_host_ops {
> > +	int (*attach)(struct device *dev, struct mipi_dsi_device *device);
> > +	int (*detach)(struct device *dev, struct mipi_dsi_device *device);
> > +};
> > +
> >   enum exynos_reg_offset {
> >   	EXYNOS_REG_OFS,
> >   	EXYNOS5433_REG_OFS
> > @@ -254,6 +260,7 @@ struct exynos_dsi_driver_data {
> >   	unsigned int wait_for_reset;
> >   	unsigned int num_bits_resol;
> >   	const unsigned int *reg_values;
> > +	const struct exynos_dsi_host_ops *host_ops;
> >   };
> >   
> >   struct exynos_dsi {
> > @@ -467,6 +474,41 @@ static const unsigned int exynos5433_reg_values[] = {
> >   	[PHYTIMING_HS_TRAIL] = 0x0c,
> >   };
> >   
> > +static int __exynos_dsi_host_attach(struct device *dev,
> > +				    struct mipi_dsi_device *device)
> > +{
> > +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> > +	struct drm_device *drm = dsi->encoder.dev;
> 
> 
> As I wrote yesterday drm device was guaranteed to be present here only 
> if mipi dsi host registration was performed in component bind. In case 
> it is performed in probe it will be always NULL, and the code does not 
> make sense.

Correct, but if the driver is used as a drm bridge, there won't be an encoder
until bridge_attach. Mipi dsi devices defer their probe until the mipi dsi
host is available. If I move the mipi dsi host registration into
bridge_attach, the driver does not know if the next device is another bridge
or a panel during bridge_attach, but the driver cannot successfully return
from bridge_attach, because then the drm driver expects a connector.

I guess that the encoder should be initialized before registering the mipi dsi
host during probe instead of bind. The probe won't be rolled back on
PROBE_DEFER during bind and the encoder will be available in host_attach.
When splitting the driver into the exynos platform specific code and the more
generic code, there won't be an encoder during host_attach in the generic
code, but the host_ops callback could (and will) use the platform specific
encoder, which is available before bridge_attach.

Does this make sense to you?

Michael

> 
> 
> Regards
> 
> Andrzej
> 
> 
> 
> > +	struct exynos_drm_crtc *crtc;
> > +
> > +	mutex_lock(&drm->mode_config.mutex);
> > +	crtc = exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD);
> > +	crtc->i80_mode = !(device->mode_flags & MIPI_DSI_MODE_VIDEO);
> > +	mutex_unlock(&drm->mode_config.mutex);
> > +
> > +	if (drm->mode_config.poll_enabled)
> > +		drm_kms_helper_hotplug_event(drm);
> > +
> > +	return 0;
> > +}
> > +
> > +static int __exynos_dsi_host_detach(struct device *dev,
> > +				     struct mipi_dsi_device *device)
> > +{
> > +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> > +	struct drm_device *drm = dsi->encoder.dev;
> > +
> > +	if (drm->mode_config.poll_enabled)
> > +		drm_kms_helper_hotplug_event(drm);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct exynos_dsi_host_ops exynos_dsi_host_ops = {
> > +	.attach = __exynos_dsi_host_attach,
> > +	.detach = __exynos_dsi_host_detach,
> > +};
> > +
> >   static const struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
> >   	.reg_ofs = EXYNOS_REG_OFS,
> >   	.plltmr_reg = 0x50,
> > @@ -477,6 +519,7 @@ static const struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
> >   	.wait_for_reset = 1,
> >   	.num_bits_resol = 11,
> >   	.reg_values = reg_values,
> > +	.host_ops = &exynos_dsi_host_ops,
> >   };
> >   
> >   static const struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
> > @@ -489,6 +532,7 @@ static const struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
> >   	.wait_for_reset = 1,
> >   	.num_bits_resol = 11,
> >   	.reg_values = reg_values,
> > +	.host_ops = &exynos_dsi_host_ops,
> >   };
> >   
> >   static const struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
> > @@ -499,6 +543,7 @@ static const struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
> >   	.wait_for_reset = 1,
> >   	.num_bits_resol = 11,
> >   	.reg_values = reg_values,
> > +	.host_ops = &exynos_dsi_host_ops,
> >   };
> >   
> >   static const struct exynos_dsi_driver_data exynos5433_dsi_driver_data = {
> > @@ -510,6 +555,7 @@ static const struct exynos_dsi_driver_data exynos5433_dsi_driver_data = {
> >   	.wait_for_reset = 0,
> >   	.num_bits_resol = 12,
> >   	.reg_values = exynos5433_reg_values,
> > +	.host_ops = &exynos_dsi_host_ops,
> >   };
> >   
> >   static const struct exynos_dsi_driver_data exynos5422_dsi_driver_data = {
> > @@ -521,6 +567,7 @@ static const struct exynos_dsi_driver_data exynos5422_dsi_driver_data = {
> >   	.wait_for_reset = 1,
> >   	.num_bits_resol = 12,
> >   	.reg_values = exynos5422_reg_values,
> > +	.host_ops = &exynos_dsi_host_ops,
> >   };
> >   
> >   static const struct of_device_id exynos_dsi_of_match[] = {
> > @@ -1551,8 +1598,8 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
> >   				  struct mipi_dsi_device *device)
> >   {
> >   	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_device *drm = encoder->dev;
> >   	struct drm_bridge *out_bridge;
> >   
> >   	out_bridge  = of_drm_find_bridge(device->dev.of_node);
> > @@ -1590,18 +1637,12 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
> >   			return ret;
> >   	}
> >   
> > -	mutex_lock(&drm->mode_config.mutex);
> > -
> >   	dsi->lanes = device->lanes;
> >   	dsi->format = device->format;
> >   	dsi->mode_flags = device->mode_flags;
> > -	exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD)->i80_mode =
> > -			!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO);
> >   
> > -	mutex_unlock(&drm->mode_config.mutex);
> > -
> > -	if (drm->mode_config.poll_enabled)
> > -		drm_kms_helper_hotplug_event(drm);
> > +	if (ops && ops->attach)
> > +		ops->attach(dsi->dsi_host.dev, device);
> >   
> >   	return 0;
> >   }
> > @@ -1610,6 +1651,7 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
> >   				  struct mipi_dsi_device *device)
> >   {
> >   	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) {
> > @@ -1625,8 +1667,8 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
> >   		INIT_LIST_HEAD(&dsi->bridge_chain);
> >   	}
> >   
> > -	if (drm->mode_config.poll_enabled)
> > -		drm_kms_helper_hotplug_event(drm);
> > +	if (ops && ops->detach)
> > +		ops->detach(dsi->dsi_host.dev, device);
> >   
> >   	exynos_dsi_unregister_te_irq(dsi);
> >   
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 10/16] drm/exynos: implement a drm bridge
  2020-09-14 21:19           ` Andrzej Hajda
@ 2020-09-15 19:40             ` Andrzej Hajda
  2021-02-01 16:33               ` Michael Tretter
  0 siblings, 1 reply; 61+ messages in thread
From: Andrzej Hajda @ 2020-09-15 19:40 UTC (permalink / raw)
  To: Michael Tretter, Marek Szyprowski
  Cc: linux-samsung-soc, jy0922.shim, b.zolnierkie, narmstrong,
	sw0312.kim, krzk, dri-devel, kernel, sylvester.nawrocki,
	Laurent.pinchart

Hi again,

W dniu 14.09.2020 o 23:19, Andrzej Hajda pisze:
> Hi Marek, Michael,
>
> On 14.09.2020 22:01, Michael Tretter wrote:
>> Hi,
>>
>> On Mon, 14 Sep 2020 14:31:19 +0200, Marek Szyprowski wrote:
>>> On 14.09.2020 10:29, Marek Szyprowski wrote:
>>>> On 11.09.2020 15:54, Michael Tretter wrote:
>>>>> 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@pengutronix.de>
>>>> This one (and the whole series applied) still fails on Exynos boards:
>>>>
>>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping 
>>>> operations
>>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
>>>> OF: graph: no port node found in /soc/dsi@11c80000
>>>> 8<--- cut here ---
>>>> Unable to handle kernel NULL pointer dereference at virtual address 
>>>> 00000084
>>>> pgd = (ptrval)
>>>> [00000084] *pgd=00000000
>>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
>>>> Modules linked in:
>>>> CPU: 1 PID: 1 Comm: swapper/0 Not tainted
>>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec #1608
>>>> Hardware name: Samsung Exynos (Flattened Device Tree)
>>>> PC is at drm_bridge_attach+0x18/0x164
>>>> LR is at exynos_dsi_bind+0x88/0xa8
>>>> pc : [<c0628c08>]    lr : [<c064d560>]    psr: 20000013
>>>> sp : ef0dfca8  ip : 00000002  fp : c13190e0
>>>> r10: 00000000  r9 : ee46d580  r8 : c13190e0
>>>> r7 : ee438800  r6 : 00000018  r5 : ef253810  r4 : ef39e840
>>>> r3 : 00000000  r2 : 00000018  r1 : ef39e888  r0 : ef39e840
>>>> Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
>>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
>>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
>>>> Stack: (0xef0dfca8 to 0xef0e0000)
>>>> ...
>>>> [<c0628c08>] (drm_bridge_attach) from [<c064d560>]
>>>> (exynos_dsi_bind+0x88/0xa8)
>>>> [<c064d560>] (exynos_dsi_bind) from [<c066a800>]
>>>> (component_bind_all+0xfc/0x290)
>>>> [<c066a800>] (component_bind_all) from [<c0649dc0>]
>>>> (exynos_drm_bind+0xe4/0x19c)
>>>> [<c0649dc0>] (exynos_drm_bind) from [<c066ad74>]
>>>> (try_to_bring_up_master+0x1e4/0x2c4)
>>>> [<c066ad74>] (try_to_bring_up_master) from [<c066b2b4>]
>>>> (component_master_add_with_match+0xd4/0x108)
>>>> [<c066b2b4>] (component_master_add_with_match) from [<c0649ae8>]
>>>> (exynos_drm_platform_probe+0xe4/0x110)
>>>> [<c0649ae8>] (exynos_drm_platform_probe) from [<c0674e6c>]
>>>> (platform_drv_probe+0x6c/0xa4)
>>>> [<c0674e6c>] (platform_drv_probe) from [<c067242c>]
>>>> (really_probe+0x200/0x4fc)
>>>> [<c067242c>] (really_probe) from [<c06728f0>]
>>>> (driver_probe_device+0x78/0x1fc)
>>>> [<c06728f0>] (driver_probe_device) from [<c0672cd8>]
>>>> (device_driver_attach+0x58/0x60)
>>>> [<c0672cd8>] (device_driver_attach) from [<c0672dbc>]
>>>> (__driver_attach+0xdc/0x174)
>>>> [<c0672dbc>] (__driver_attach) from [<c06701b4>]
>>>> (bus_for_each_dev+0x68/0xb4)
>>>> [<c06701b4>] (bus_for_each_dev) from [<c06714e8>]
>>>> (bus_add_driver+0x158/0x214)
>>>> [<c06714e8>] (bus_add_driver) from [<c0673c1c>] 
>>>> (driver_register+0x78/0x110)
>>>> [<c0673c1c>] (driver_register) from [<c0649ca8>]
>>>> (exynos_drm_init+0xe4/0x118)
>>>> [<c0649ca8>] (exynos_drm_init) from [<c0102484>]
>>>> (do_one_initcall+0x8c/0x42c)
>>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
>>>> (kernel_init_freeable+0x190/0x1dc)
>>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7880>]
>>>> (kernel_init+0x8/0x118)
>>>> [<c0af7880>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
>>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
>>>> ...
>>>> ---[ end trace ee27f313f9ed9da1 ]---
>>>>
>>>> # arm-linux-gnueabi-addr2line -e vmlinux c0628c08
>>>> drivers/gpu/drm/drm_bridge.c:184 (discriminator 1)
>>>>
>>>> I will try to debug it a bit more today.
>>> The above crash has been caused by lack of in_bridge initialization to
>>> NULL in exynos_dsi_bind() in this patch. However, fixing it reveals
>>> another issue:
>>>
>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
>>> OF: graph: no port node found in /soc/dsi@11c80000
>>> 8<--- cut here ---
>>> Unable to handle kernel NULL pointer dereference at virtual address 
>>> 00000280
>>> pgd = (ptrval)
>>> [00000280] *pgd=00000000
>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
>>> Modules linked in:
>>> CPU: 0 PID: 1 Comm: swapper/0 Not tainted
>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec-dirty #1613
>>> Hardware name: Samsung Exynos (Flattened Device Tree)
>>> PC is at __mutex_lock+0x54/0xb18
>>> LR is at lock_is_held_type+0x80/0x138
>>> pc : [<c0afc920>]    lr : [<c0af63e8>]    psr: 60000013
>>> sp : ef0dfd30  ip : 33937b74  fp : c13193c8
>>> r10: c1208eec  r9 : 00000000  r8 : ee45f808
>>> r7 : c19561a4  r6 : 00000000  r5 : 00000000  r4 : 0000024c
>>> r3 : 00000000  r2 : 00204140  r1 : c124f13c  r0 : 00000000
>>> Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
>>> Stack: (0xef0dfd30 to 0xef0e0000)
>>> ...
>>> [<c0afc920>] (__mutex_lock) from [<c0afd400>] 
>>> (mutex_lock_nested+0x1c/0x24)
>>> [<c0afd400>] (mutex_lock_nested) from [<c064d4b8>]
>>> (__exynos_dsi_host_attach+0x20/0x6c)
>>> [<c064d4b8>] (__exynos_dsi_host_attach) from [<c064d914>]
>>> (exynos_dsi_host_attach+0x70/0x194)
>>> [<c064d914>] (exynos_dsi_host_attach) from [<c0656b64>]
>>> (s6e8aa0_probe+0x1b0/0x218)
>>> [<c0656b64>] (s6e8aa0_probe) from [<c0672530>] 
>>> (really_probe+0x200/0x4fc)
>>> [<c0672530>] (really_probe) from [<c06729f4>]
>>> (driver_probe_device+0x78/0x1fc)
>>> [<c06729f4>] (driver_probe_device) from [<c0672ddc>]
>>> (device_driver_attach+0x58/0x60)
>>> [<c0672ddc>] (device_driver_attach) from [<c0672ec0>]
>>> (__driver_attach+0xdc/0x174)
>>> [<c0672ec0>] (__driver_attach) from [<c06702b8>]
>>> (bus_for_each_dev+0x68/0xb4)
>>> [<c06702b8>] (bus_for_each_dev) from [<c06715ec>]
>>> (bus_add_driver+0x158/0x214)
>>> [<c06715ec>] (bus_add_driver) from [<c0673d20>] 
>>> (driver_register+0x78/0x110)
>>> [<c0673d20>] (driver_register) from [<c0102484>]
>>> (do_one_initcall+0x8c/0x42c)
>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
>>> (kernel_init_freeable+0x190/0x1dc)
>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7988>]
>>> (kernel_init+0x8/0x118)
>>> [<c0af7988>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
>>> ...
>>> ---[ end trace c06e996ec2e8234d ]---
>>>
>>> This means that dsi->encoder.dev is not initialized in
>>> __exynos_dsi_host_attach().
>>>
>>> This happens, because drm_bridge_attach() in exynos_dsi_bind() returned
>>> earlier -517 (deferred probe), what causes cleanup of encoder and
>>> release of all drm resources.
>>>
>>> Then however, the panel tries to register itself and
>>> exynos_dsi_host_attach() tries to access the released encoder (which is
>>> zeroed in drm_encoder_release) and rest of resources, what causes 
>>> failure.
>>>
>>> It looks that something is missing. Maybe mipi host has to be 
>>> registered
>>> later, when bridge is ready? I have no idea how it is handled before
>>> this patch. Andrzej, could you comment it a bit?
>> I intentionally changed the order, because if another bridge follows 
>> in the
>> pipeline, the probe of the drm driver has to be deferred until some 
>> bridge
>> provides a connector. The next bridge registers itself via the 
>> host_attach
>> function and the deferral is ensured via the bind for the bind/unbind 
>> API or
>> the bridge_attach function otherwise.
>>
>> On the other hand, the bridge does not have an encoder until the mipi 
>> device
>> has been attached.
>>
>> As a solution, the exynos dsi driver must initialize the encoder in
>> exynos_dsi_probe instead of in exynos_dsi_bind and access the encoder 
>> via
>> exynos_dsi instead of the bridge.
>>
>> Can you try to move everything except samsung_dsim_bind from 
>> exynos_dsi_bind
>> to exynos_dsi_probe (respectively for unbind) and report if it fixes the
>> crash.
>
>
> The original behaviour is that encoder (exynos_dsi) is registered 
> regardless of sink presence (initially panel, later also bridge) - it 
> avoids multiple issues with deferred probe, device driver bind/unbind 
> and module load/unload. Appearance or disappearance of sink is 
> reported to host nicely via DSI attach/detach callbacks - and it is 
> reflected in drm world as change state of the connector.
>
> Registering DSI host in bind and unregistering in unbind assures that 
> if mipi_dsi device is attached/detached the drm device is always 
> present - it makes device/driver binding race free and allows to avoid 
> additional locking.
>
> Moving DSI host registration to probe changes everything, for sure it 
> breaks the nice feature of DSI attach/detach callbacks and apparently 
> can cause different issues depending on device bind order.
>
> I will try to look at the patches tomorrow and maybe I can find more 
> constructive comments :)


As I said yesterday, exynos_dsi driver uses dsi host attach/detach 
callbacks to control appearance/disappearance of downstream device. It 
allows to:

1. Safely bind/unbind different device drivers at any time and at any 
order, without killing exynos_drm and/or crashing system.

2. Avoid issues with late drm init - on some platforms exynos_drm device 
appeared too late, due to deferred probe, and resulted in black screen 
in userspace.


Now if we want to convert exynos_dsi to drm_bridge I see following options:

A. Forgot about callbacks and make the exynos_drm to defer probing until 
exynos_dsi bridge is available, probably it will cause later exynos_drm 
appearance, thus probably black screen on some targets. So for sure it 
will be suboptimal. Making it bridge unbind safe would be another 
problem, but most developers do not care about it so why should we? :)

B. Try to mimic current behaviour - exynos_dsi register bridge ASAP, 
even if downstream devices are not yet attached, on attach/detach notify 
drm about it via connector status change, for this dsi_host registration 
should be performed from drm_bridge attach, I guess.


Option A is more standard, but is unsafe and causes other issues.

Option B keeps current behaviour.


Regards

Andrzej


>
>
> Regards
>
> Andrzej
>
>
>>
>> Michael
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> https://protect2.fireeye.com/v1/url?k=4f0be936-129547ac-4f0a6279-0cc47a336fae-e9aecfc5418740e8&q=1&e=1d4b0871-5b85-47f3-9506-79c768643aee&u=https%3A%2F%2Flists.freedesktop.org%2Fmailman%2Flistinfo%2Fdri-devel 
>>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 16/16] drm/exynos: move bridge driver to bridges
  2020-09-11 13:54   ` [PATCH v2 16/16] drm/exynos: move bridge driver to bridges Michael Tretter
@ 2020-09-16  7:58     ` Daniel Vetter
  2020-09-16  8:58       ` Michael Tretter
  0 siblings, 1 reply; 61+ messages in thread
From: Daniel Vetter @ 2020-09-16  7:58 UTC (permalink / raw)
  To: Michael Tretter
  Cc: linux-samsung-soc, jy0922.shim, narmstrong, b.zolnierkie,
	sw0312.kim, dri-devel, a.hajda, krzk, kernel, sylvester.nawrocki,
	Laurent.pinchart

On Fri, Sep 11, 2020 at 03:54:13PM +0200, Michael Tretter wrote:
> As the driver is not platform dependent anymore, move it to the drm
> bridge driver directory.
> 
> Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>

So new drm_bridge drivers that still use the component stuff is a bit
uncool. We're trying to get away from that everywhere, bridges should be
abstracted enough that just going through the of lookup functions to get
at your bridge should work.

Is there anything here that prevents this, or could this be included?
-Daniel

> ---
> v2:
> - select DRM_SAMSUNG_DSIM from DRM_EXYNOS_DSI
> - add removal of depends on !FB_S3C
> ---
>  drivers/gpu/drm/bridge/Kconfig                |    9 +
>  drivers/gpu/drm/bridge/Makefile               |    1 +
>  drivers/gpu/drm/bridge/samsung-dsim.c         | 1790 ++++++++++++++++
>  drivers/gpu/drm/exynos/Kconfig                |    5 +-
>  drivers/gpu/drm/exynos/Makefile               |    2 +-
>  drivers/gpu/drm/exynos/exynos_drm_dsi.c       | 1896 ++---------------
>  drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c |  333 ---
>  .../drm/bridge/samsung-dsim.h                 |   20 +-
>  8 files changed, 2037 insertions(+), 2019 deletions(-)
>  create mode 100644 drivers/gpu/drm/bridge/samsung-dsim.c
>  delete mode 100644 drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c
>  rename drivers/gpu/drm/exynos/exynos_drm_dsi.h => include/drm/bridge/samsung-dsim.h (69%)
> 
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index 3e11af4e9f63..55ab5030c6cf 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -125,6 +125,15 @@ config DRM_PARADE_PS8640
>  	  The PS8640 is a high-performance and low-power
>  	  MIPI DSI to eDP converter
>  
> +config DRM_SAMSUNG_DSIM
> +	tristate "Samsung MIPI DSI bridge"
> +	depends on OF
> +	select DRM_KMS_HELPER
> +	select DRM_MIPI_DSI
> +	select DRM_PANEL
> +	help
> +	  Samsung MIPI DSI bridge driver.
> +
>  config DRM_SIL_SII8620
>  	tristate "Silicon Image SII8620 HDMI/MHL bridge"
>  	depends on OF
> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> index c589a6a7cbe1..5ac7a5c413dc 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -8,6 +8,7 @@ obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v
>  obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
>  obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
>  obj-$(CONFIG_DRM_PARADE_PS8640) += parade-ps8640.o
> +obj-$(CONFIG_DRM_SAMSUNG_DSIM) += samsung-dsim.o
>  obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o
>  obj-$(CONFIG_DRM_SII902X) += sii902x.o
>  obj-$(CONFIG_DRM_SII9234) += sii9234.o
> diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
> new file mode 100644
> index 000000000000..6d2d8dc027de
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/samsung-dsim.c
> @@ -0,0 +1,1790 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Samsung SoC MIPI DSI Master driver.
> + *
> + * Copyright (c) 2014 Samsung Electronics Co., Ltd
> + *
> + * Contacts: Tomasz Figa <t.figa@samsung.com>
> +*/
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/component.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/irq.h>
> +#include <linux/of_device.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_graph.h>
> +#include <linux/phy/phy.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include <asm/unaligned.h>
> +
> +#include <video/mipi_display.h>
> +#include <video/videomode.h>
> +
> +#include <drm/bridge/samsung-dsim.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_simple_kms_helper.h>
> +
> +/* returns true iff both arguments logically differs */
> +#define NEQV(a, b) (!(a) ^ !(b))
> +
> +/* DSIM_STATUS */
> +#define DSIM_STOP_STATE_DAT(x)		(((x) & 0xf) << 0)
> +#define DSIM_STOP_STATE_CLK		(1 << 8)
> +#define DSIM_TX_READY_HS_CLK		(1 << 10)
> +#define DSIM_PLL_STABLE			(1 << 31)
> +
> +/* DSIM_TIMEOUT */
> +#define DSIM_LPDR_TIMEOUT(x)		((x) << 0)
> +#define DSIM_BTA_TIMEOUT(x)		((x) << 16)
> +
> +/* DSIM_CLKCTRL */
> +#define DSIM_ESC_PRESCALER(x)		(((x) & 0xffff) << 0)
> +#define DSIM_ESC_PRESCALER_MASK		(0xffff << 0)
> +#define DSIM_LANE_ESC_CLK_EN_CLK	(1 << 19)
> +#define DSIM_LANE_ESC_CLK_EN_DATA(x)	(((x) & 0xf) << 20)
> +#define DSIM_LANE_ESC_CLK_EN_DATA_MASK	(0xf << 20)
> +#define DSIM_BYTE_CLKEN			(1 << 24)
> +#define DSIM_BYTE_CLK_SRC(x)		(((x) & 0x3) << 25)
> +#define DSIM_BYTE_CLK_SRC_MASK		(0x3 << 25)
> +#define DSIM_PLL_BYPASS			(1 << 27)
> +#define DSIM_ESC_CLKEN			(1 << 28)
> +#define DSIM_TX_REQUEST_HSCLK		(1 << 31)
> +
> +/* DSIM_CONFIG */
> +#define DSIM_LANE_EN_CLK		(1 << 0)
> +#define DSIM_LANE_EN(x)			(((x) & 0xf) << 1)
> +#define DSIM_NUM_OF_DATA_LANE(x)	(((x) & 0x3) << 5)
> +#define DSIM_SUB_PIX_FORMAT(x)		(((x) & 0x7) << 8)
> +#define DSIM_MAIN_PIX_FORMAT_MASK	(0x7 << 12)
> +#define DSIM_MAIN_PIX_FORMAT_RGB888	(0x7 << 12)
> +#define DSIM_MAIN_PIX_FORMAT_RGB666	(0x6 << 12)
> +#define DSIM_MAIN_PIX_FORMAT_RGB666_P	(0x5 << 12)
> +#define DSIM_MAIN_PIX_FORMAT_RGB565	(0x4 << 12)
> +#define DSIM_SUB_VC			(((x) & 0x3) << 16)
> +#define DSIM_MAIN_VC			(((x) & 0x3) << 18)
> +#define DSIM_HSA_MODE			(1 << 20)
> +#define DSIM_HBP_MODE			(1 << 21)
> +#define DSIM_HFP_MODE			(1 << 22)
> +#define DSIM_HSE_MODE			(1 << 23)
> +#define DSIM_AUTO_MODE			(1 << 24)
> +#define DSIM_VIDEO_MODE			(1 << 25)
> +#define DSIM_BURST_MODE			(1 << 26)
> +#define DSIM_SYNC_INFORM		(1 << 27)
> +#define DSIM_EOT_DISABLE		(1 << 28)
> +#define DSIM_MFLUSH_VS			(1 << 29)
> +/* This flag is valid only for exynos3250/3472/5260/5430 */
> +#define DSIM_CLKLANE_STOP		(1 << 30)
> +
> +/* DSIM_ESCMODE */
> +#define DSIM_TX_TRIGGER_RST		(1 << 4)
> +#define DSIM_TX_LPDT_LP			(1 << 6)
> +#define DSIM_CMD_LPDT_LP		(1 << 7)
> +#define DSIM_FORCE_BTA			(1 << 16)
> +#define DSIM_FORCE_STOP_STATE		(1 << 20)
> +#define DSIM_STOP_STATE_CNT(x)		(((x) & 0x7ff) << 21)
> +#define DSIM_STOP_STATE_CNT_MASK	(0x7ff << 21)
> +
> +/* DSIM_MDRESOL */
> +#define DSIM_MAIN_STAND_BY		(1 << 31)
> +#define DSIM_MAIN_VRESOL(x, num_bits)	(((x) & ((1 << (num_bits)) - 1)) << 16)
> +#define DSIM_MAIN_HRESOL(x, num_bits)	(((x) & ((1 << (num_bits)) - 1)) << 0)
> +
> +/* DSIM_MVPORCH */
> +#define DSIM_CMD_ALLOW(x)		((x) << 28)
> +#define DSIM_STABLE_VFP(x)		((x) << 16)
> +#define DSIM_MAIN_VBP(x)		((x) << 0)
> +#define DSIM_CMD_ALLOW_MASK		(0xf << 28)
> +#define DSIM_STABLE_VFP_MASK		(0x7ff << 16)
> +#define DSIM_MAIN_VBP_MASK		(0x7ff << 0)
> +
> +/* DSIM_MHPORCH */
> +#define DSIM_MAIN_HFP(x)		((x) << 16)
> +#define DSIM_MAIN_HBP(x)		((x) << 0)
> +#define DSIM_MAIN_HFP_MASK		((0xffff) << 16)
> +#define DSIM_MAIN_HBP_MASK		((0xffff) << 0)
> +
> +/* DSIM_MSYNC */
> +#define DSIM_MAIN_VSA(x)		((x) << 22)
> +#define DSIM_MAIN_HSA(x)		((x) << 0)
> +#define DSIM_MAIN_VSA_MASK		((0x3ff) << 22)
> +#define DSIM_MAIN_HSA_MASK		((0xffff) << 0)
> +
> +/* DSIM_SDRESOL */
> +#define DSIM_SUB_STANDY(x)		((x) << 31)
> +#define DSIM_SUB_VRESOL(x)		((x) << 16)
> +#define DSIM_SUB_HRESOL(x)		((x) << 0)
> +#define DSIM_SUB_STANDY_MASK		((0x1) << 31)
> +#define DSIM_SUB_VRESOL_MASK		((0x7ff) << 16)
> +#define DSIM_SUB_HRESOL_MASK		((0x7ff) << 0)
> +
> +/* DSIM_INTSRC */
> +#define DSIM_INT_PLL_STABLE		(1 << 31)
> +#define DSIM_INT_SW_RST_RELEASE		(1 << 30)
> +#define DSIM_INT_SFR_FIFO_EMPTY		(1 << 29)
> +#define DSIM_INT_SFR_HDR_FIFO_EMPTY	(1 << 28)
> +#define DSIM_INT_BTA			(1 << 25)
> +#define DSIM_INT_FRAME_DONE		(1 << 24)
> +#define DSIM_INT_RX_TIMEOUT		(1 << 21)
> +#define DSIM_INT_BTA_TIMEOUT		(1 << 20)
> +#define DSIM_INT_RX_DONE		(1 << 18)
> +#define DSIM_INT_RX_TE			(1 << 17)
> +#define DSIM_INT_RX_ACK			(1 << 16)
> +#define DSIM_INT_RX_ECC_ERR		(1 << 15)
> +#define DSIM_INT_RX_CRC_ERR		(1 << 14)
> +
> +/* DSIM_FIFOCTRL */
> +#define DSIM_RX_DATA_FULL		(1 << 25)
> +#define DSIM_RX_DATA_EMPTY		(1 << 24)
> +#define DSIM_SFR_HEADER_FULL		(1 << 23)
> +#define DSIM_SFR_HEADER_EMPTY		(1 << 22)
> +#define DSIM_SFR_PAYLOAD_FULL		(1 << 21)
> +#define DSIM_SFR_PAYLOAD_EMPTY		(1 << 20)
> +#define DSIM_I80_HEADER_FULL		(1 << 19)
> +#define DSIM_I80_HEADER_EMPTY		(1 << 18)
> +#define DSIM_I80_PAYLOAD_FULL		(1 << 17)
> +#define DSIM_I80_PAYLOAD_EMPTY		(1 << 16)
> +#define DSIM_SD_HEADER_FULL		(1 << 15)
> +#define DSIM_SD_HEADER_EMPTY		(1 << 14)
> +#define DSIM_SD_PAYLOAD_FULL		(1 << 13)
> +#define DSIM_SD_PAYLOAD_EMPTY		(1 << 12)
> +#define DSIM_MD_HEADER_FULL		(1 << 11)
> +#define DSIM_MD_HEADER_EMPTY		(1 << 10)
> +#define DSIM_MD_PAYLOAD_FULL		(1 << 9)
> +#define DSIM_MD_PAYLOAD_EMPTY		(1 << 8)
> +#define DSIM_RX_FIFO			(1 << 4)
> +#define DSIM_SFR_FIFO			(1 << 3)
> +#define DSIM_I80_FIFO			(1 << 2)
> +#define DSIM_SD_FIFO			(1 << 1)
> +#define DSIM_MD_FIFO			(1 << 0)
> +
> +/* DSIM_PHYACCHR */
> +#define DSIM_AFC_EN			(1 << 14)
> +#define DSIM_AFC_CTL(x)			(((x) & 0x7) << 5)
> +
> +/* DSIM_PLLCTRL */
> +#define DSIM_FREQ_BAND(x)		((x) << 24)
> +#define DSIM_PLL_EN			(1 << 23)
> +#define DSIM_PLL_P(x)			((x) << 13)
> +#define DSIM_PLL_M(x)			((x) << 4)
> +#define DSIM_PLL_S(x)			((x) << 1)
> +
> +/* DSIM_PHYCTRL */
> +#define DSIM_PHYCTRL_ULPS_EXIT(x)	(((x) & 0x1ff) << 0)
> +#define DSIM_PHYCTRL_B_DPHYCTL_VREG_LP	(1 << 30)
> +#define DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP	(1 << 14)
> +
> +/* DSIM_PHYTIMING */
> +#define DSIM_PHYTIMING_LPX(x)		((x) << 8)
> +#define DSIM_PHYTIMING_HS_EXIT(x)	((x) << 0)
> +
> +/* DSIM_PHYTIMING1 */
> +#define DSIM_PHYTIMING1_CLK_PREPARE(x)	((x) << 24)
> +#define DSIM_PHYTIMING1_CLK_ZERO(x)	((x) << 16)
> +#define DSIM_PHYTIMING1_CLK_POST(x)	((x) << 8)
> +#define DSIM_PHYTIMING1_CLK_TRAIL(x)	((x) << 0)
> +
> +/* DSIM_PHYTIMING2 */
> +#define DSIM_PHYTIMING2_HS_PREPARE(x)	((x) << 16)
> +#define DSIM_PHYTIMING2_HS_ZERO(x)	((x) << 8)
> +#define DSIM_PHYTIMING2_HS_TRAIL(x)	((x) << 0)
> +
> +#define DSI_MAX_BUS_WIDTH		4
> +#define DSI_NUM_VIRTUAL_CHANNELS	4
> +#define DSI_TX_FIFO_SIZE		2048
> +#define DSI_RX_FIFO_SIZE		256
> +#define DSI_XFER_TIMEOUT_MS		100
> +#define DSI_RX_FIFO_EMPTY		0x30800002
> +
> +#define OLD_SCLK_MIPI_CLK_NAME "pll_clk"
> +
> +static const char *const clk_names[5] = { "bus_clk", "sclk_mipi",
> +	"phyclk_mipidphy0_bitclkdiv8", "phyclk_mipidphy0_rxclkesc0",
> +	"sclk_rgb_vclk_to_dsim0" };
> +
> +enum samsung_dsim_transfer_type {
> +	EXYNOS_DSI_TX,
> +	EXYNOS_DSI_RX,
> +};
> +
> +struct samsung_dsim_transfer {
> +	struct list_head list;
> +	struct completion completed;
> +	int result;
> +	struct mipi_dsi_packet packet;
> +	u16 flags;
> +	u16 tx_done;
> +
> +	u8 *rx_payload;
> +	u16 rx_len;
> +	u16 rx_done;
> +};
> +
> +#define DSIM_STATE_ENABLED		BIT(0)
> +#define DSIM_STATE_INITIALIZED		BIT(1)
> +#define DSIM_STATE_CMD_LPM		BIT(2)
> +#define DSIM_STATE_VIDOUT_AVAILABLE	BIT(3)
> +
> +struct samsung_dsim {
> +	struct drm_bridge bridge;
> +	struct mipi_dsi_host dsi_host;
> +	struct drm_connector connector;
> +	struct drm_panel *panel;
> +	struct list_head bridge_chain;
> +	struct drm_bridge *out_bridge;
> +	struct device *dev;
> +
> +	void __iomem *reg_base;
> +	struct phy *phy;
> +	struct clk **clks;
> +	struct regulator_bulk_data supplies[2];
> +	int irq;
> +	int te_gpio;
> +
> +	u32 pll_clk_rate;
> +	u32 burst_clk_rate;
> +	u32 esc_clk_rate;
> +	u32 lanes;
> +	u32 mode_flags;
> +	u32 format;
> +
> +	struct drm_display_mode mode;
> +
> +	int state;
> +	struct drm_property *brightness;
> +	struct completion completed;
> +
> +	spinlock_t transfer_lock; /* protects transfer_list */
> +	struct list_head transfer_list;
> +
> +	const struct samsung_dsim_driver_data *driver_data;
> +};
> +
> +#define host_to_dsi(host) container_of(host, struct samsung_dsim, dsi_host)
> +#define connector_to_dsi(c) container_of(c, struct samsung_dsim, connector)
> +
> +enum reg_idx {
> +	DSIM_STATUS_REG,	/* Status register */
> +	DSIM_SWRST_REG,		/* Software reset register */
> +	DSIM_CLKCTRL_REG,	/* Clock control register */
> +	DSIM_TIMEOUT_REG,	/* Time out register */
> +	DSIM_CONFIG_REG,	/* Configuration register */
> +	DSIM_ESCMODE_REG,	/* Escape mode register */
> +	DSIM_MDRESOL_REG,
> +	DSIM_MVPORCH_REG,	/* Main display Vporch register */
> +	DSIM_MHPORCH_REG,	/* Main display Hporch register */
> +	DSIM_MSYNC_REG,		/* Main display sync area register */
> +	DSIM_INTSRC_REG,	/* Interrupt source register */
> +	DSIM_INTMSK_REG,	/* Interrupt mask register */
> +	DSIM_PKTHDR_REG,	/* Packet Header FIFO register */
> +	DSIM_PAYLOAD_REG,	/* Payload FIFO register */
> +	DSIM_RXFIFO_REG,	/* Read FIFO register */
> +	DSIM_FIFOCTRL_REG,	/* FIFO status and control register */
> +	DSIM_PLLCTRL_REG,	/* PLL control register */
> +	DSIM_PHYCTRL_REG,
> +	DSIM_PHYTIMING_REG,
> +	DSIM_PHYTIMING1_REG,
> +	DSIM_PHYTIMING2_REG,
> +	NUM_REGS
> +};
> +
> +static const unsigned int exynos_reg_ofs[] = {
> +	[DSIM_STATUS_REG] =  0x00,
> +	[DSIM_SWRST_REG] =  0x04,
> +	[DSIM_CLKCTRL_REG] =  0x08,
> +	[DSIM_TIMEOUT_REG] =  0x0c,
> +	[DSIM_CONFIG_REG] =  0x10,
> +	[DSIM_ESCMODE_REG] =  0x14,
> +	[DSIM_MDRESOL_REG] =  0x18,
> +	[DSIM_MVPORCH_REG] =  0x1c,
> +	[DSIM_MHPORCH_REG] =  0x20,
> +	[DSIM_MSYNC_REG] =  0x24,
> +	[DSIM_INTSRC_REG] =  0x2c,
> +	[DSIM_INTMSK_REG] =  0x30,
> +	[DSIM_PKTHDR_REG] =  0x34,
> +	[DSIM_PAYLOAD_REG] =  0x38,
> +	[DSIM_RXFIFO_REG] =  0x3c,
> +	[DSIM_FIFOCTRL_REG] =  0x44,
> +	[DSIM_PLLCTRL_REG] =  0x4c,
> +	[DSIM_PHYCTRL_REG] =  0x5c,
> +	[DSIM_PHYTIMING_REG] =  0x64,
> +	[DSIM_PHYTIMING1_REG] =  0x68,
> +	[DSIM_PHYTIMING2_REG] =  0x6c,
> +};
> +
> +static const unsigned int exynos5433_reg_ofs[] = {
> +	[DSIM_STATUS_REG] = 0x04,
> +	[DSIM_SWRST_REG] = 0x0C,
> +	[DSIM_CLKCTRL_REG] = 0x10,
> +	[DSIM_TIMEOUT_REG] = 0x14,
> +	[DSIM_CONFIG_REG] = 0x18,
> +	[DSIM_ESCMODE_REG] = 0x1C,
> +	[DSIM_MDRESOL_REG] = 0x20,
> +	[DSIM_MVPORCH_REG] = 0x24,
> +	[DSIM_MHPORCH_REG] = 0x28,
> +	[DSIM_MSYNC_REG] = 0x2C,
> +	[DSIM_INTSRC_REG] = 0x34,
> +	[DSIM_INTMSK_REG] = 0x38,
> +	[DSIM_PKTHDR_REG] = 0x3C,
> +	[DSIM_PAYLOAD_REG] = 0x40,
> +	[DSIM_RXFIFO_REG] = 0x44,
> +	[DSIM_FIFOCTRL_REG] = 0x4C,
> +	[DSIM_PLLCTRL_REG] = 0x94,
> +	[DSIM_PHYCTRL_REG] = 0xA4,
> +	[DSIM_PHYTIMING_REG] = 0xB4,
> +	[DSIM_PHYTIMING1_REG] = 0xB8,
> +	[DSIM_PHYTIMING2_REG] = 0xBC,
> +};
> +
> +static inline void samsung_dsim_write(struct samsung_dsim *dsi,
> +				      enum reg_idx idx, u32 val)
> +{
> +	const unsigned int *reg_ofs;
> +
> +	if (dsi->driver_data->reg_ofs == EXYNOS5433_REG_OFS)
> +		reg_ofs = exynos5433_reg_ofs;
> +	else
> +		reg_ofs = exynos_reg_ofs;
> +
> +	writel(val, dsi->reg_base + reg_ofs[idx]);
> +}
> +
> +static inline u32 samsung_dsim_read(struct samsung_dsim *dsi, enum reg_idx idx)
> +{
> +	const unsigned int *reg_ofs;
> +
> +	if (dsi->driver_data->reg_ofs == EXYNOS5433_REG_OFS)
> +		reg_ofs = exynos5433_reg_ofs;
> +	else
> +		reg_ofs = exynos_reg_ofs;
> +
> +	return readl(dsi->reg_base + reg_ofs[idx]);
> +}
> +
> +static void samsung_dsim_wait_for_reset(struct samsung_dsim *dsi)
> +{
> +	if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300)))
> +		return;
> +
> +	dev_err(dsi->dev, "timeout waiting for reset\n");
> +}
> +
> +static void samsung_dsim_reset(struct samsung_dsim *dsi)
> +{
> +	u32 reset_val = dsi->driver_data->reg_values[RESET_TYPE];
> +
> +	reinit_completion(&dsi->completed);
> +	samsung_dsim_write(dsi, DSIM_SWRST_REG, reset_val);
> +}
> +
> +#ifndef MHZ
> +#define MHZ	(1000*1000)
> +#endif
> +
> +static unsigned long samsung_dsim_pll_find_pms(struct samsung_dsim *dsi,
> +					       unsigned long fin,
> +					       unsigned long fout,
> +					       u8 *p, u16 *m, u8 *s)
> +{
> +	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
> +	unsigned long best_freq = 0;
> +	u32 min_delta = 0xffffffff;
> +	u8 p_min, p_max;
> +	u8 _p, best_p;
> +	u16 _m, best_m;
> +	u8 _s, best_s;
> +
> +	p_min = DIV_ROUND_UP(fin, (12 * MHZ));
> +	p_max = fin / (6 * MHZ);
> +
> +	for (_p = p_min; _p <= p_max; ++_p) {
> +		for (_s = 0; _s <= 5; ++_s) {
> +			u64 tmp;
> +			u32 delta;
> +
> +			tmp = (u64)fout * (_p << _s);
> +			do_div(tmp, fin);
> +			_m = tmp;
> +			if (_m < 41 || _m > 125)
> +				continue;
> +
> +			tmp = (u64)_m * fin;
> +			do_div(tmp, _p);
> +			if (tmp < 500 * MHZ ||
> +					tmp > driver_data->max_freq * MHZ)
> +				continue;
> +
> +			tmp = (u64)_m * fin;
> +			do_div(tmp, _p << _s);
> +
> +			delta = abs(fout - tmp);
> +			if (delta < min_delta) {
> +				best_p = _p;
> +				best_m = _m;
> +				best_s = _s;
> +				min_delta = delta;
> +				best_freq = tmp;
> +			}
> +		}
> +	}
> +
> +	if (best_freq) {
> +		*p = best_p;
> +		*m = best_m;
> +		*s = best_s;
> +	}
> +
> +	return best_freq;
> +}
> +
> +static unsigned long samsung_dsim_set_pll(struct samsung_dsim *dsi,
> +					  unsigned long freq)
> +{
> +	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
> +	unsigned long fin, fout;
> +	int timeout;
> +	u8 p, s;
> +	u16 m;
> +	u32 reg;
> +
> +	fin = dsi->pll_clk_rate;
> +	fout = samsung_dsim_pll_find_pms(dsi, fin, freq, &p, &m, &s);
> +	if (!fout) {
> +		dev_err(dsi->dev,
> +			"failed to find PLL PMS for requested frequency\n");
> +		return 0;
> +	}
> +	dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s);
> +
> +	writel(driver_data->reg_values[PLL_TIMER],
> +			dsi->reg_base + driver_data->plltmr_reg);
> +
> +	reg = DSIM_PLL_EN | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s);
> +
> +	if (driver_data->has_freqband) {
> +		static const unsigned long freq_bands[] = {
> +			100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ,
> +			270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ,
> +			510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ,
> +			770 * MHZ, 870 * MHZ, 950 * MHZ,
> +		};
> +		int band;
> +
> +		for (band = 0; band < ARRAY_SIZE(freq_bands); ++band)
> +			if (fout < freq_bands[band])
> +				break;
> +
> +		dev_dbg(dsi->dev, "band %d\n", band);
> +
> +		reg |= DSIM_FREQ_BAND(band);
> +	}
> +
> +	samsung_dsim_write(dsi, DSIM_PLLCTRL_REG, reg);
> +
> +	timeout = 1000;
> +	do {
> +		if (timeout-- == 0) {
> +			dev_err(dsi->dev, "PLL failed to stabilize\n");
> +			return 0;
> +		}
> +		reg = samsung_dsim_read(dsi, DSIM_STATUS_REG);
> +	} while ((reg & DSIM_PLL_STABLE) == 0);
> +
> +	return fout;
> +}
> +
> +static int samsung_dsim_enable_clock(struct samsung_dsim *dsi)
> +{
> +	unsigned long hs_clk, byte_clk, esc_clk;
> +	unsigned long esc_div;
> +	u32 reg;
> +
> +	hs_clk = samsung_dsim_set_pll(dsi, dsi->burst_clk_rate);
> +	if (!hs_clk) {
> +		dev_err(dsi->dev, "failed to configure DSI PLL\n");
> +		return -EFAULT;
> +	}
> +
> +	byte_clk = hs_clk / 8;
> +	esc_div = DIV_ROUND_UP(byte_clk, dsi->esc_clk_rate);
> +	esc_clk = byte_clk / esc_div;
> +
> +	if (esc_clk > 20 * MHZ) {
> +		++esc_div;
> +		esc_clk = byte_clk / esc_div;
> +	}
> +
> +	dev_dbg(dsi->dev, "hs_clk = %lu, byte_clk = %lu, esc_clk = %lu\n",
> +		hs_clk, byte_clk, esc_clk);
> +
> +	reg = samsung_dsim_read(dsi, DSIM_CLKCTRL_REG);
> +	reg &= ~(DSIM_ESC_PRESCALER_MASK | DSIM_LANE_ESC_CLK_EN_CLK
> +			| DSIM_LANE_ESC_CLK_EN_DATA_MASK | DSIM_PLL_BYPASS
> +			| DSIM_BYTE_CLK_SRC_MASK);
> +	reg |= DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN
> +			| DSIM_ESC_PRESCALER(esc_div)
> +			| DSIM_LANE_ESC_CLK_EN_CLK
> +			| DSIM_LANE_ESC_CLK_EN_DATA(BIT(dsi->lanes) - 1)
> +			| DSIM_BYTE_CLK_SRC(0)
> +			| DSIM_TX_REQUEST_HSCLK;
> +	samsung_dsim_write(dsi, DSIM_CLKCTRL_REG, reg);
> +
> +	return 0;
> +}
> +
> +static void samsung_dsim_set_phy_ctrl(struct samsung_dsim *dsi)
> +{
> +	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
> +	const unsigned int *reg_values = driver_data->reg_values;
> +	u32 reg;
> +
> +	if (driver_data->has_freqband)
> +		return;
> +
> +	/* B D-PHY: D-PHY Master & Slave Analog Block control */
> +	reg = DSIM_PHYCTRL_ULPS_EXIT(reg_values[PHYCTRL_ULPS_EXIT]);
> +	if (reg_values[PHYCTRL_VREG_LP])
> +		reg |= DSIM_PHYCTRL_B_DPHYCTL_VREG_LP;
> +	if (reg_values[PHYCTRL_SLEW_UP])
> +		reg |= DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP;
> +	samsung_dsim_write(dsi, DSIM_PHYCTRL_REG, reg);
> +
> +	/*
> +	 * T LPX: Transmitted length of any Low-Power state period
> +	 * T HS-EXIT: Time that the transmitter drives LP-11 following a HS
> +	 *	burst
> +	 */
> +	reg = DSIM_PHYTIMING_LPX(reg_values[PHYTIMING_LPX]) |
> +		DSIM_PHYTIMING_HS_EXIT(reg_values[PHYTIMING_HS_EXIT]);
> +	samsung_dsim_write(dsi, DSIM_PHYTIMING_REG, reg);
> +
> +	/*
> +	 * T CLK-PREPARE: Time that the transmitter drives the Clock Lane LP-00
> +	 *	Line state immediately before the HS-0 Line state starting the
> +	 *	HS transmission
> +	 * T CLK-ZERO: Time that the transmitter drives the HS-0 state prior to
> +	 *	transmitting the Clock.
> +	 * T CLK_POST: Time that the transmitter continues to send HS clock
> +	 *	after the last associated Data Lane has transitioned to LP Mode
> +	 *	Interval is defined as the period from the end of T HS-TRAIL to
> +	 *	the beginning of T CLK-TRAIL
> +	 * T CLK-TRAIL: Time that the transmitter drives the HS-0 state after
> +	 *	the last payload clock bit of a HS transmission burst
> +	 */
> +	reg = DSIM_PHYTIMING1_CLK_PREPARE(reg_values[PHYTIMING_CLK_PREPARE]) |
> +		DSIM_PHYTIMING1_CLK_ZERO(reg_values[PHYTIMING_CLK_ZERO]) |
> +		DSIM_PHYTIMING1_CLK_POST(reg_values[PHYTIMING_CLK_POST]) |
> +		DSIM_PHYTIMING1_CLK_TRAIL(reg_values[PHYTIMING_CLK_TRAIL]);
> +	samsung_dsim_write(dsi, DSIM_PHYTIMING1_REG, reg);
> +
> +	/*
> +	 * T HS-PREPARE: Time that the transmitter drives the Data Lane LP-00
> +	 *	Line state immediately before the HS-0 Line state starting the
> +	 *	HS transmission
> +	 * T HS-ZERO: Time that the transmitter drives the HS-0 state prior to
> +	 *	transmitting the Sync sequence.
> +	 * T HS-TRAIL: Time that the transmitter drives the flipped differential
> +	 *	state after last payload data bit of a HS transmission burst
> +	 */
> +	reg = DSIM_PHYTIMING2_HS_PREPARE(reg_values[PHYTIMING_HS_PREPARE]) |
> +		DSIM_PHYTIMING2_HS_ZERO(reg_values[PHYTIMING_HS_ZERO]) |
> +		DSIM_PHYTIMING2_HS_TRAIL(reg_values[PHYTIMING_HS_TRAIL]);
> +	samsung_dsim_write(dsi, DSIM_PHYTIMING2_REG, reg);
> +}
> +
> +static void samsung_dsim_disable_clock(struct samsung_dsim *dsi)
> +{
> +	u32 reg;
> +
> +	reg = samsung_dsim_read(dsi, DSIM_CLKCTRL_REG);
> +	reg &= ~(DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK
> +			| DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN);
> +	samsung_dsim_write(dsi, DSIM_CLKCTRL_REG, reg);
> +
> +	reg = samsung_dsim_read(dsi, DSIM_PLLCTRL_REG);
> +	reg &= ~DSIM_PLL_EN;
> +	samsung_dsim_write(dsi, DSIM_PLLCTRL_REG, reg);
> +}
> +
> +static void samsung_dsim_enable_lane(struct samsung_dsim *dsi, u32 lane)
> +{
> +	u32 reg = samsung_dsim_read(dsi, DSIM_CONFIG_REG);
> +	reg |= (DSIM_NUM_OF_DATA_LANE(dsi->lanes - 1) | DSIM_LANE_EN_CLK |
> +			DSIM_LANE_EN(lane));
> +	samsung_dsim_write(dsi, DSIM_CONFIG_REG, reg);
> +}
> +
> +static int samsung_dsim_init_link(struct samsung_dsim *dsi)
> +{
> +	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
> +	int timeout;
> +	u32 reg;
> +	u32 lanes_mask;
> +
> +	/* Initialize FIFO pointers */
> +	reg = samsung_dsim_read(dsi, DSIM_FIFOCTRL_REG);
> +	reg &= ~0x1f;
> +	samsung_dsim_write(dsi, DSIM_FIFOCTRL_REG, reg);
> +
> +	usleep_range(9000, 11000);
> +
> +	reg |= 0x1f;
> +	samsung_dsim_write(dsi, DSIM_FIFOCTRL_REG, reg);
> +	usleep_range(9000, 11000);
> +
> +	/* DSI configuration */
> +	reg = 0;
> +
> +	/*
> +	 * The first bit of mode_flags specifies display configuration.
> +	 * If this bit is set[= MIPI_DSI_MODE_VIDEO], dsi will support video
> +	 * mode, otherwise it will support command mode.
> +	 */
> +	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> +		reg |= DSIM_VIDEO_MODE;
> +
> +		/*
> +		 * The user manual describes that following bits are ignored in
> +		 * command mode.
> +		 */
> +		if (!(dsi->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH))
> +			reg |= DSIM_MFLUSH_VS;
> +		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
> +			reg |= DSIM_SYNC_INFORM;
> +		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
> +			reg |= DSIM_BURST_MODE;
> +		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT)
> +			reg |= DSIM_AUTO_MODE;
> +		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSE)
> +			reg |= DSIM_HSE_MODE;
> +		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HFP))
> +			reg |= DSIM_HFP_MODE;
> +		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HBP))
> +			reg |= DSIM_HBP_MODE;
> +		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSA))
> +			reg |= DSIM_HSA_MODE;
> +	}
> +
> +	if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET))
> +		reg |= DSIM_EOT_DISABLE;
> +
> +	switch (dsi->format) {
> +	case MIPI_DSI_FMT_RGB888:
> +		reg |= DSIM_MAIN_PIX_FORMAT_RGB888;
> +		break;
> +	case MIPI_DSI_FMT_RGB666:
> +		reg |= DSIM_MAIN_PIX_FORMAT_RGB666;
> +		break;
> +	case MIPI_DSI_FMT_RGB666_PACKED:
> +		reg |= DSIM_MAIN_PIX_FORMAT_RGB666_P;
> +		break;
> +	case MIPI_DSI_FMT_RGB565:
> +		reg |= DSIM_MAIN_PIX_FORMAT_RGB565;
> +		break;
> +	default:
> +		dev_err(dsi->dev, "invalid pixel format\n");
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Use non-continuous clock mode if the periparal wants and
> +	 * host controller supports
> +	 *
> +	 * In non-continous clock mode, host controller will turn off
> +	 * the HS clock between high-speed transmissions to reduce
> +	 * power consumption.
> +	 */
> +	if (driver_data->has_clklane_stop &&
> +			dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
> +		reg |= DSIM_CLKLANE_STOP;
> +	}
> +	samsung_dsim_write(dsi, DSIM_CONFIG_REG, reg);
> +
> +	lanes_mask = BIT(dsi->lanes) - 1;
> +	samsung_dsim_enable_lane(dsi, lanes_mask);
> +
> +	/* Check clock and data lane state are stop state */
> +	timeout = 100;
> +	do {
> +		if (timeout-- == 0) {
> +			dev_err(dsi->dev, "waiting for bus lanes timed out\n");
> +			return -EFAULT;
> +		}
> +
> +		reg = samsung_dsim_read(dsi, DSIM_STATUS_REG);
> +		if ((reg & DSIM_STOP_STATE_DAT(lanes_mask))
> +		    != DSIM_STOP_STATE_DAT(lanes_mask))
> +			continue;
> +	} while (!(reg & (DSIM_STOP_STATE_CLK | DSIM_TX_READY_HS_CLK)));
> +
> +	reg = samsung_dsim_read(dsi, DSIM_ESCMODE_REG);
> +	reg &= ~DSIM_STOP_STATE_CNT_MASK;
> +	reg |= DSIM_STOP_STATE_CNT(driver_data->reg_values[STOP_STATE_CNT]);
> +	samsung_dsim_write(dsi, DSIM_ESCMODE_REG, reg);
> +
> +	reg = DSIM_BTA_TIMEOUT(0xff) | DSIM_LPDR_TIMEOUT(0xffff);
> +	samsung_dsim_write(dsi, DSIM_TIMEOUT_REG, reg);
> +
> +	return 0;
> +}
> +
> +static void samsung_dsim_set_display_mode(struct samsung_dsim *dsi)
> +{
> +	struct drm_display_mode *m = &dsi->mode;
> +	unsigned int num_bits_resol = dsi->driver_data->num_bits_resol;
> +	u32 reg;
> +
> +	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> +		reg = DSIM_CMD_ALLOW(0xf)
> +			| DSIM_STABLE_VFP(m->vsync_start - m->vdisplay)
> +			| DSIM_MAIN_VBP(m->vtotal - m->vsync_end);
> +		samsung_dsim_write(dsi, DSIM_MVPORCH_REG, reg);
> +
> +		reg = DSIM_MAIN_HFP(m->hsync_start - m->hdisplay)
> +			| DSIM_MAIN_HBP(m->htotal - m->hsync_end);
> +		samsung_dsim_write(dsi, DSIM_MHPORCH_REG, reg);
> +
> +		reg = DSIM_MAIN_VSA(m->vsync_end - m->vsync_start)
> +			| DSIM_MAIN_HSA(m->hsync_end - m->hsync_start);
> +		samsung_dsim_write(dsi, DSIM_MSYNC_REG, reg);
> +	}
> +	reg =  DSIM_MAIN_HRESOL(m->hdisplay, num_bits_resol) |
> +		DSIM_MAIN_VRESOL(m->vdisplay, num_bits_resol);
> +
> +	samsung_dsim_write(dsi, DSIM_MDRESOL_REG, reg);
> +
> +	dev_dbg(dsi->dev, "LCD size = %dx%d\n", m->hdisplay, m->vdisplay);
> +}
> +
> +static void samsung_dsim_set_display_enable(struct samsung_dsim *dsi,
> +					    bool enable)
> +{
> +	u32 reg;
> +
> +	reg = samsung_dsim_read(dsi, DSIM_MDRESOL_REG);
> +	if (enable)
> +		reg |= DSIM_MAIN_STAND_BY;
> +	else
> +		reg &= ~DSIM_MAIN_STAND_BY;
> +	samsung_dsim_write(dsi, DSIM_MDRESOL_REG, reg);
> +}
> +
> +static int samsung_dsim_wait_for_hdr_fifo(struct samsung_dsim *dsi)
> +{
> +	int timeout = 2000;
> +
> +	do {
> +		u32 reg = samsung_dsim_read(dsi, DSIM_FIFOCTRL_REG);
> +
> +		if (!(reg & DSIM_SFR_HEADER_FULL))
> +			return 0;
> +
> +		if (!cond_resched())
> +			usleep_range(950, 1050);
> +	} while (--timeout);
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static void samsung_dsim_set_cmd_lpm(struct samsung_dsim *dsi, bool lpm)
> +{
> +	u32 v = samsung_dsim_read(dsi, DSIM_ESCMODE_REG);
> +
> +	if (lpm)
> +		v |= DSIM_CMD_LPDT_LP;
> +	else
> +		v &= ~DSIM_CMD_LPDT_LP;
> +
> +	samsung_dsim_write(dsi, DSIM_ESCMODE_REG, v);
> +}
> +
> +static void samsung_dsim_force_bta(struct samsung_dsim *dsi)
> +{
> +	u32 v = samsung_dsim_read(dsi, DSIM_ESCMODE_REG);
> +	v |= DSIM_FORCE_BTA;
> +	samsung_dsim_write(dsi, DSIM_ESCMODE_REG, v);
> +}
> +
> +static void samsung_dsim_send_to_fifo(struct samsung_dsim *dsi,
> +				      struct samsung_dsim_transfer *xfer)
> +{
> +	struct device *dev = dsi->dev;
> +	struct mipi_dsi_packet *pkt = &xfer->packet;
> +	const u8 *payload = pkt->payload + xfer->tx_done;
> +	u16 length = pkt->payload_length - xfer->tx_done;
> +	bool first = !xfer->tx_done;
> +	u32 reg;
> +
> +	dev_dbg(dev, "< xfer %pK: tx len %u, done %u, rx len %u, done %u\n",
> +		xfer, length, xfer->tx_done, xfer->rx_len, xfer->rx_done);
> +
> +	if (length > DSI_TX_FIFO_SIZE)
> +		length = DSI_TX_FIFO_SIZE;
> +
> +	xfer->tx_done += length;
> +
> +	/* Send payload */
> +	while (length >= 4) {
> +		reg = get_unaligned_le32(payload);
> +		samsung_dsim_write(dsi, DSIM_PAYLOAD_REG, reg);
> +		payload += 4;
> +		length -= 4;
> +	}
> +
> +	reg = 0;
> +	switch (length) {
> +	case 3:
> +		reg |= payload[2] << 16;
> +		fallthrough;
> +	case 2:
> +		reg |= payload[1] << 8;
> +		fallthrough;
> +	case 1:
> +		reg |= payload[0];
> +		samsung_dsim_write(dsi, DSIM_PAYLOAD_REG, reg);
> +		break;
> +	}
> +
> +	/* Send packet header */
> +	if (!first)
> +		return;
> +
> +	reg = get_unaligned_le32(pkt->header);
> +	if (samsung_dsim_wait_for_hdr_fifo(dsi)) {
> +		dev_err(dev, "waiting for header FIFO timed out\n");
> +		return;
> +	}
> +
> +	if (NEQV(xfer->flags & MIPI_DSI_MSG_USE_LPM,
> +		 dsi->state & DSIM_STATE_CMD_LPM)) {
> +		samsung_dsim_set_cmd_lpm(dsi, xfer->flags & MIPI_DSI_MSG_USE_LPM);
> +		dsi->state ^= DSIM_STATE_CMD_LPM;
> +	}
> +
> +	samsung_dsim_write(dsi, DSIM_PKTHDR_REG, reg);
> +
> +	if (xfer->flags & MIPI_DSI_MSG_REQ_ACK)
> +		samsung_dsim_force_bta(dsi);
> +}
> +
> +static void samsung_dsim_read_from_fifo(struct samsung_dsim *dsi,
> +					struct samsung_dsim_transfer *xfer)
> +{
> +	u8 *payload = xfer->rx_payload + xfer->rx_done;
> +	bool first = !xfer->rx_done;
> +	struct device *dev = dsi->dev;
> +	u16 length;
> +	u32 reg;
> +
> +	if (first) {
> +		reg = samsung_dsim_read(dsi, DSIM_RXFIFO_REG);
> +
> +		switch (reg & 0x3f) {
> +		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
> +		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
> +			if (xfer->rx_len >= 2) {
> +				payload[1] = reg >> 16;
> +				++xfer->rx_done;
> +			}
> +			fallthrough;
> +		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
> +		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
> +			payload[0] = reg >> 8;
> +			++xfer->rx_done;
> +			xfer->rx_len = xfer->rx_done;
> +			xfer->result = 0;
> +			goto clear_fifo;
> +		case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
> +			dev_err(dev, "DSI Error Report: 0x%04x\n",
> +				(reg >> 8) & 0xffff);
> +			xfer->result = 0;
> +			goto clear_fifo;
> +		}
> +
> +		length = (reg >> 8) & 0xffff;
> +		if (length > xfer->rx_len) {
> +			dev_err(dev,
> +				"response too long (%u > %u bytes), stripping\n",
> +				xfer->rx_len, length);
> +			length = xfer->rx_len;
> +		} else if (length < xfer->rx_len)
> +			xfer->rx_len = length;
> +	}
> +
> +	length = xfer->rx_len - xfer->rx_done;
> +	xfer->rx_done += length;
> +
> +	/* Receive payload */
> +	while (length >= 4) {
> +		reg = samsung_dsim_read(dsi, DSIM_RXFIFO_REG);
> +		payload[0] = (reg >>  0) & 0xff;
> +		payload[1] = (reg >>  8) & 0xff;
> +		payload[2] = (reg >> 16) & 0xff;
> +		payload[3] = (reg >> 24) & 0xff;
> +		payload += 4;
> +		length -= 4;
> +	}
> +
> +	if (length) {
> +		reg = samsung_dsim_read(dsi, DSIM_RXFIFO_REG);
> +		switch (length) {
> +		case 3:
> +			payload[2] = (reg >> 16) & 0xff;
> +			fallthrough;
> +		case 2:
> +			payload[1] = (reg >> 8) & 0xff;
> +			fallthrough;
> +		case 1:
> +			payload[0] = reg & 0xff;
> +		}
> +	}
> +
> +	if (xfer->rx_done == xfer->rx_len)
> +		xfer->result = 0;
> +
> +clear_fifo:
> +	length = DSI_RX_FIFO_SIZE / 4;
> +	do {
> +		reg = samsung_dsim_read(dsi, DSIM_RXFIFO_REG);
> +		if (reg == DSI_RX_FIFO_EMPTY)
> +			break;
> +	} while (--length);
> +}
> +
> +static void samsung_dsim_transfer_start(struct samsung_dsim *dsi)
> +{
> +	unsigned long flags;
> +	struct samsung_dsim_transfer *xfer;
> +	bool start = false;
> +
> +again:
> +	spin_lock_irqsave(&dsi->transfer_lock, flags);
> +
> +	if (list_empty(&dsi->transfer_list)) {
> +		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> +		return;
> +	}
> +
> +	xfer = list_first_entry(&dsi->transfer_list,
> +					struct samsung_dsim_transfer, list);
> +
> +	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> +
> +	if (xfer->packet.payload_length &&
> +	    xfer->tx_done == xfer->packet.payload_length)
> +		/* waiting for RX */
> +		return;
> +
> +	samsung_dsim_send_to_fifo(dsi, xfer);
> +
> +	if (xfer->packet.payload_length || xfer->rx_len)
> +		return;
> +
> +	xfer->result = 0;
> +	complete(&xfer->completed);
> +
> +	spin_lock_irqsave(&dsi->transfer_lock, flags);
> +
> +	list_del_init(&xfer->list);
> +	start = !list_empty(&dsi->transfer_list);
> +
> +	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> +
> +	if (start)
> +		goto again;
> +}
> +
> +static bool samsung_dsim_transfer_finish(struct samsung_dsim *dsi)
> +{
> +	struct samsung_dsim_transfer *xfer;
> +	unsigned long flags;
> +	bool start = true;
> +
> +	spin_lock_irqsave(&dsi->transfer_lock, flags);
> +
> +	if (list_empty(&dsi->transfer_list)) {
> +		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> +		return false;
> +	}
> +
> +	xfer = list_first_entry(&dsi->transfer_list,
> +					struct samsung_dsim_transfer, list);
> +
> +	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> +
> +	dev_dbg(dsi->dev,
> +		"> xfer %pK, tx_len %zu, tx_done %u, rx_len %u, rx_done %u\n",
> +		xfer, xfer->packet.payload_length, xfer->tx_done, xfer->rx_len,
> +		xfer->rx_done);
> +
> +	if (xfer->tx_done != xfer->packet.payload_length)
> +		return true;
> +
> +	if (xfer->rx_done != xfer->rx_len)
> +		samsung_dsim_read_from_fifo(dsi, xfer);
> +
> +	if (xfer->rx_done != xfer->rx_len)
> +		return true;
> +
> +	spin_lock_irqsave(&dsi->transfer_lock, flags);
> +
> +	list_del_init(&xfer->list);
> +	start = !list_empty(&dsi->transfer_list);
> +
> +	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> +
> +	if (!xfer->rx_len)
> +		xfer->result = 0;
> +	complete(&xfer->completed);
> +
> +	return start;
> +}
> +
> +static void samsung_dsim_remove_transfer(struct samsung_dsim *dsi,
> +					 struct samsung_dsim_transfer *xfer)
> +{
> +	unsigned long flags;
> +	bool start;
> +
> +	spin_lock_irqsave(&dsi->transfer_lock, flags);
> +
> +	if (!list_empty(&dsi->transfer_list) &&
> +	    xfer == list_first_entry(&dsi->transfer_list,
> +				     struct samsung_dsim_transfer, list)) {
> +		list_del_init(&xfer->list);
> +		start = !list_empty(&dsi->transfer_list);
> +		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> +		if (start)
> +			samsung_dsim_transfer_start(dsi);
> +		return;
> +	}
> +
> +	list_del_init(&xfer->list);
> +
> +	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> +}
> +
> +static int samsung_dsim_transfer(struct samsung_dsim *dsi,
> +				 struct samsung_dsim_transfer *xfer)
> +{
> +	unsigned long flags;
> +	bool stopped;
> +
> +	xfer->tx_done = 0;
> +	xfer->rx_done = 0;
> +	xfer->result = -ETIMEDOUT;
> +	init_completion(&xfer->completed);
> +
> +	spin_lock_irqsave(&dsi->transfer_lock, flags);
> +
> +	stopped = list_empty(&dsi->transfer_list);
> +	list_add_tail(&xfer->list, &dsi->transfer_list);
> +
> +	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> +
> +	if (stopped)
> +		samsung_dsim_transfer_start(dsi);
> +
> +	wait_for_completion_timeout(&xfer->completed,
> +				    msecs_to_jiffies(DSI_XFER_TIMEOUT_MS));
> +	if (xfer->result == -ETIMEDOUT) {
> +		struct mipi_dsi_packet *pkt = &xfer->packet;
> +		samsung_dsim_remove_transfer(dsi, xfer);
> +		dev_err(dsi->dev, "xfer timed out: %*ph %*ph\n", 4, pkt->header,
> +			(int)pkt->payload_length, pkt->payload);
> +		return -ETIMEDOUT;
> +	}
> +
> +	/* Also covers hardware timeout condition */
> +	return xfer->result;
> +}
> +
> +static irqreturn_t samsung_dsim_irq(int irq, void *dev_id)
> +{
> +	struct samsung_dsim *dsi = dev_id;
> +	u32 status;
> +
> +	status = samsung_dsim_read(dsi, DSIM_INTSRC_REG);
> +	if (!status) {
> +		static unsigned long int j;
> +		if (printk_timed_ratelimit(&j, 500))
> +			dev_warn(dsi->dev, "spurious interrupt\n");
> +		return IRQ_HANDLED;
> +	}
> +	samsung_dsim_write(dsi, DSIM_INTSRC_REG, status);
> +
> +	if (status & DSIM_INT_SW_RST_RELEASE) {
> +		u32 mask = ~(DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY |
> +			DSIM_INT_SFR_HDR_FIFO_EMPTY | DSIM_INT_RX_ECC_ERR |
> +			DSIM_INT_SW_RST_RELEASE);
> +		samsung_dsim_write(dsi, DSIM_INTMSK_REG, mask);
> +		complete(&dsi->completed);
> +		return IRQ_HANDLED;
> +	}
> +
> +	if (!(status & (DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY |
> +			DSIM_INT_PLL_STABLE)))
> +		return IRQ_HANDLED;
> +
> +	if (samsung_dsim_transfer_finish(dsi))
> +		samsung_dsim_transfer_start(dsi);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t samsung_dsim_te_irq_handler(int irq, void *dev_id)
> +{
> +	struct samsung_dsim *dsi = dev_id;
> +	const struct samsung_dsim_host_ops *ops = dsi->driver_data->host_ops;
> +
> +	if (ops && ops->te_handler &&
> +	    (dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
> +		ops->te_handler(dsi->dsi_host.dev);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void samsung_dsim_enable_irq(struct samsung_dsim *dsi)
> +{
> +	enable_irq(dsi->irq);
> +
> +	if (gpio_is_valid(dsi->te_gpio))
> +		enable_irq(gpio_to_irq(dsi->te_gpio));
> +}
> +
> +static void samsung_dsim_disable_irq(struct samsung_dsim *dsi)
> +{
> +	if (gpio_is_valid(dsi->te_gpio))
> +		disable_irq(gpio_to_irq(dsi->te_gpio));
> +
> +	disable_irq(dsi->irq);
> +}
> +
> +static int samsung_dsim_init(struct samsung_dsim *dsi)
> +{
> +	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
> +
> +	samsung_dsim_reset(dsi);
> +	samsung_dsim_enable_irq(dsi);
> +
> +	if (driver_data->reg_values[RESET_TYPE] == DSIM_FUNCRST)
> +		samsung_dsim_enable_lane(dsi, BIT(dsi->lanes) - 1);
> +
> +	samsung_dsim_enable_clock(dsi);
> +	if (driver_data->wait_for_reset)
> +		samsung_dsim_wait_for_reset(dsi);
> +	samsung_dsim_set_phy_ctrl(dsi);
> +	samsung_dsim_init_link(dsi);
> +
> +	return 0;
> +}
> +
> +static int samsung_dsim_register_te_irq(struct samsung_dsim *dsi,
> +					struct device *panel)
> +{
> +	int ret;
> +	int te_gpio_irq;
> +
> +	dsi->te_gpio = of_get_named_gpio(panel->of_node, "te-gpios", 0);
> +	if (dsi->te_gpio == -ENOENT)
> +		return 0;
> +
> +	if (!gpio_is_valid(dsi->te_gpio)) {
> +		ret = dsi->te_gpio;
> +		dev_err(dsi->dev, "cannot get te-gpios, %d\n", ret);
> +		goto out;
> +	}
> +
> +	ret = gpio_request(dsi->te_gpio, "te_gpio");
> +	if (ret) {
> +		dev_err(dsi->dev, "gpio request failed with %d\n", ret);
> +		goto out;
> +	}
> +
> +	te_gpio_irq = gpio_to_irq(dsi->te_gpio);
> +	irq_set_status_flags(te_gpio_irq, IRQ_NOAUTOEN);
> +
> +	ret = request_threaded_irq(te_gpio_irq, samsung_dsim_te_irq_handler,
> +				   NULL, IRQF_TRIGGER_RISING, "TE", dsi);
> +	if (ret) {
> +		dev_err(dsi->dev, "request interrupt failed with %d\n", ret);
> +		gpio_free(dsi->te_gpio);
> +		goto out;
> +	}
> +
> +out:
> +	return ret;
> +}
> +
> +static void samsung_dsim_unregister_te_irq(struct samsung_dsim *dsi)
> +{
> +	if (gpio_is_valid(dsi->te_gpio)) {
> +		free_irq(gpio_to_irq(dsi->te_gpio), dsi);
> +		gpio_free(dsi->te_gpio);
> +		dsi->te_gpio = -ENOENT;
> +	}
> +}
> +
> +static void samsung_dsim_enable(struct samsung_dsim *dsi)
> +{
> +	struct drm_bridge *iter;
> +	int ret;
> +
> +	if (dsi->state & DSIM_STATE_ENABLED)
> +		return;
> +
> +	pm_runtime_get_sync(dsi->dev);
> +	dsi->state |= DSIM_STATE_ENABLED;
> +
> +	if (dsi->panel) {
> +		ret = drm_panel_prepare(dsi->panel);
> +		if (ret < 0)
> +			goto err_put_sync;
> +	} else {
> +		list_for_each_entry_reverse(iter, &dsi->bridge_chain,
> +					    chain_node) {
> +			if (iter->funcs->pre_enable)
> +				iter->funcs->pre_enable(iter);
> +		}
> +	}
> +
> +	samsung_dsim_set_display_mode(dsi);
> +	samsung_dsim_set_display_enable(dsi, true);
> +
> +	if (dsi->panel) {
> +		ret = drm_panel_enable(dsi->panel);
> +		if (ret < 0)
> +			goto err_display_disable;
> +	} else {
> +		list_for_each_entry(iter, &dsi->bridge_chain, chain_node) {
> +			if (iter->funcs->enable)
> +				iter->funcs->enable(iter);
> +		}
> +	}
> +
> +	dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
> +	return;
> +
> +err_display_disable:
> +	samsung_dsim_set_display_enable(dsi, false);
> +	drm_panel_unprepare(dsi->panel);
> +
> +err_put_sync:
> +	dsi->state &= ~DSIM_STATE_ENABLED;
> +	pm_runtime_put(dsi->dev);
> +}
> +
> +static void samsung_dsim_disable(struct samsung_dsim *dsi)
> +{
> +	struct drm_bridge *iter;
> +
> +	if (!(dsi->state & DSIM_STATE_ENABLED))
> +		return;
> +
> +	dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
> +
> +	drm_panel_disable(dsi->panel);
> +
> +	list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) {
> +		if (iter->funcs->disable)
> +			iter->funcs->disable(iter);
> +	}
> +
> +	samsung_dsim_set_display_enable(dsi, false);
> +	drm_panel_unprepare(dsi->panel);
> +
> +	list_for_each_entry(iter, &dsi->bridge_chain, chain_node) {
> +		if (iter->funcs->post_disable)
> +			iter->funcs->post_disable(iter);
> +	}
> +
> +	dsi->state &= ~DSIM_STATE_ENABLED;
> +	pm_runtime_put_sync(dsi->dev);
> +}
> +
> +static enum drm_connector_status
> +samsung_dsim_detect(struct drm_connector *connector, bool force)
> +{
> +	return connector->status;
> +}
> +
> +static void samsung_dsim_connector_destroy(struct drm_connector *connector)
> +{
> +	drm_connector_unregister(connector);
> +	drm_connector_cleanup(connector);
> +	connector->dev = NULL;
> +}
> +
> +static const struct drm_connector_funcs samsung_dsim_connector_funcs = {
> +	.detect = samsung_dsim_detect,
> +	.fill_modes = drm_helper_probe_single_connector_modes,
> +	.destroy = samsung_dsim_connector_destroy,
> +	.reset = drm_atomic_helper_connector_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static int samsung_dsim_get_modes(struct drm_connector *connector)
> +{
> +	struct samsung_dsim *dsi = connector_to_dsi(connector);
> +
> +	if (dsi->panel)
> +		return drm_panel_get_modes(dsi->panel, connector);
> +
> +	return 0;
> +}
> +
> +static const struct drm_connector_helper_funcs samsung_dsim_connector_helper_funcs = {
> +	.get_modes = samsung_dsim_get_modes,
> +};
> +
> +static int samsung_dsim_create_connector(struct samsung_dsim *dsi)
> +{
> +	struct drm_connector *connector = &dsi->connector;
> +	struct drm_device *drm = dsi->bridge.dev;
> +	int ret;
> +
> +	connector->polled = DRM_CONNECTOR_POLL_HPD;
> +
> +	ret = drm_connector_init(drm, connector, &samsung_dsim_connector_funcs,
> +				 DRM_MODE_CONNECTOR_DSI);
> +	if (ret) {
> +		DRM_DEV_ERROR(dsi->dev,
> +			      "Failed to initialize connector with drm\n");
> +		return ret;
> +	}
> +
> +	connector->status = connector_status_disconnected;
> +	drm_connector_helper_add(connector, &samsung_dsim_connector_helper_funcs);
> +	drm_connector_attach_encoder(connector, dsi->bridge.encoder);
> +	if (!drm->registered)
> +		return 0;
> +
> +	connector->funcs->reset(connector);
> +	drm_connector_register(connector);
> +	return 0;
> +}
> +
> +static int samsung_dsim_bridge_attach(struct drm_bridge *bridge,
> +				      enum drm_bridge_attach_flags flags)
> +{
> +	struct samsung_dsim *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 = samsung_dsim_create_connector(dsi);
> +		if (ret)
> +			return ret;
> +
> +		if (dsi->panel) {
> +			dsi->connector.status = connector_status_connected;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static void samsung_dsim_bridge_detach(struct drm_bridge *bridge)
> +{
> +	struct samsung_dsim *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);
> +		samsung_dsim_disable(dsi);
> +		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 void samsung_dsim_bridge_enable(struct drm_bridge *bridge)
> +{
> +	struct samsung_dsim *dsi = bridge->driver_private;
> +
> +	samsung_dsim_enable(dsi);
> +}
> +
> +static void samsung_dsim_bridge_disable(struct drm_bridge *bridge)
> +{
> +	struct samsung_dsim *dsi = bridge->driver_private;
> +
> +	samsung_dsim_disable(dsi);
> +}
> +
> +static void samsung_dsim_bridge_mode_set(struct drm_bridge *bridge,
> +					 const struct drm_display_mode *mode,
> +					 const struct drm_display_mode *adjusted_mode)
> +{
> +	struct samsung_dsim *dsi = bridge->driver_private;
> +
> +	/* The mode is set when actually enabling the device. */
> +	drm_mode_copy(&dsi->mode, adjusted_mode);
> +}
> +
> +static const struct drm_bridge_funcs samsung_dsim_bridge_funcs = {
> +	.attach = samsung_dsim_bridge_attach,
> +	.detach = samsung_dsim_bridge_detach,
> +	.enable = samsung_dsim_bridge_enable,
> +	.disable = samsung_dsim_bridge_disable,
> +	.mode_set = samsung_dsim_bridge_mode_set,
> +};
> +
> +static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
> +				    struct mipi_dsi_device *device)
> +{
> +	struct samsung_dsim *dsi = host_to_dsi(host);
> +	const struct samsung_dsim_host_ops *ops = dsi->driver_data->host_ops;
> +	struct drm_bridge *out_bridge;
> +
> +	out_bridge = of_drm_find_bridge(device->dev.of_node);
> +	if (out_bridge) {
> +		dsi->out_bridge = out_bridge;
> +	} else {
> +		dsi->panel = of_drm_find_panel(device->dev.of_node);
> +		if (IS_ERR(dsi->panel))
> +			dsi->panel = NULL;
> +		else
> +			dsi->connector.status = connector_status_connected;
> +	}
> +
> +	/*
> +	 * This is a temporary solution and should be made by more generic way.
> +	 *
> +	 * If attached panel device is for command mode one, dsi should register
> +	 * TE interrupt handler.
> +	 */
> +	if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO)) {
> +		int ret = samsung_dsim_register_te_irq(dsi, &device->dev);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	dsi->lanes = device->lanes;
> +	dsi->format = device->format;
> +	dsi->mode_flags = device->mode_flags;
> +
> +	if (ops && ops->attach)
> +		ops->attach(dsi->dsi_host.dev, device);
> +
> +	return 0;
> +}
> +
> +static int samsung_dsim_host_detach(struct mipi_dsi_host *host,
> +				    struct mipi_dsi_device *device)
> +{
> +	struct samsung_dsim *dsi = host_to_dsi(host);
> +	const struct samsung_dsim_host_ops *ops = dsi->driver_data->host_ops;
> +
> +	samsung_dsim_unregister_te_irq(dsi);
> +
> +	if (ops && ops->detach)
> +		ops->detach(dsi->dsi_host.dev, device);
> +
> +	samsung_dsim_unregister_te_irq(dsi);
> +
> +	return 0;
> +}
> +
> +static ssize_t samsung_dsim_host_transfer(struct mipi_dsi_host *host,
> +					  const struct mipi_dsi_msg *msg)
> +{
> +	struct samsung_dsim *dsi = host_to_dsi(host);
> +	struct samsung_dsim_transfer xfer;
> +	int ret;
> +
> +	if (!(dsi->state & DSIM_STATE_ENABLED))
> +		return -EINVAL;
> +
> +	if (!(dsi->state & DSIM_STATE_INITIALIZED)) {
> +		ret = samsung_dsim_init(dsi);
> +		if (ret)
> +			return ret;
> +		dsi->state |= DSIM_STATE_INITIALIZED;
> +	}
> +
> +	ret = mipi_dsi_create_packet(&xfer.packet, msg);
> +	if (ret < 0)
> +		return ret;
> +
> +	xfer.rx_len = msg->rx_len;
> +	xfer.rx_payload = msg->rx_buf;
> +	xfer.flags = msg->flags;
> +
> +	ret = samsung_dsim_transfer(dsi, &xfer);
> +	return (ret < 0) ? ret : xfer.rx_done;
> +}
> +
> +static const struct mipi_dsi_host_ops samsung_dsim_ops = {
> +	.attach = samsung_dsim_host_attach,
> +	.detach = samsung_dsim_host_detach,
> +	.transfer = samsung_dsim_host_transfer,
> +};
> +
> +static int samsung_dsim_of_read_u32(const struct device_node *np,
> +				    const char *propname, u32 *out_value)
> +{
> +	int ret = of_property_read_u32(np, propname, out_value);
> +
> +	if (ret < 0)
> +		pr_err("%pOF: failed to get '%s' property\n", np, propname);
> +
> +	return ret;
> +}
> +
> +static int samsung_dsim_parse_dt(struct samsung_dsim *dsi)
> +{
> +	struct device *dev = dsi->dev;
> +	struct device_node *node = dev->of_node;
> +	int ret;
> +
> +	ret = samsung_dsim_of_read_u32(node, "samsung,pll-clock-frequency",
> +				       &dsi->pll_clk_rate);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = samsung_dsim_of_read_u32(node, "samsung,burst-clock-frequency",
> +				       &dsi->burst_clk_rate);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = samsung_dsim_of_read_u32(node, "samsung,esc-clock-frequency",
> +				       &dsi->esc_clk_rate);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static struct samsung_dsim *__samsung_dsim_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct drm_bridge *bridge;
> +	struct resource *res;
> +	struct samsung_dsim *dsi;
> +	int ret, i;
> +
> +	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
> +	if (!dsi)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* To be checked as invalid one */
> +	dsi->te_gpio = -ENOENT;
> +
> +	init_completion(&dsi->completed);
> +	spin_lock_init(&dsi->transfer_lock);
> +	INIT_LIST_HEAD(&dsi->transfer_list);
> +	INIT_LIST_HEAD(&dsi->bridge_chain);
> +
> +	dsi->dsi_host.ops = &samsung_dsim_ops;
> +	dsi->dsi_host.dev = dev;
> +
> +	dsi->dev = dev;
> +	dsi->driver_data = of_device_get_match_data(dev);
> +
> +	dsi->supplies[0].supply = "vddcore";
> +	dsi->supplies[1].supply = "vddio";
> +	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(dsi->supplies),
> +				      dsi->supplies);
> +	if (ret) {
> +		if (ret != -EPROBE_DEFER)
> +			dev_info(dev, "failed to get regulators: %d\n", ret);
> +		return ERR_PTR(ret);
> +	}
> +
> +	dsi->clks = devm_kcalloc(dev,
> +			dsi->driver_data->num_clks, sizeof(*dsi->clks),
> +			GFP_KERNEL);
> +	if (!dsi->clks)
> +		return ERR_PTR(-ENOMEM);
> +
> +	for (i = 0; i < dsi->driver_data->num_clks; i++) {
> +		dsi->clks[i] = devm_clk_get(dev, clk_names[i]);
> +		if (IS_ERR(dsi->clks[i])) {
> +			if (strcmp(clk_names[i], "sclk_mipi") == 0) {
> +				dsi->clks[i] = devm_clk_get(dev,
> +							OLD_SCLK_MIPI_CLK_NAME);
> +				if (!IS_ERR(dsi->clks[i]))
> +					continue;
> +			}
> +
> +			dev_info(dev, "failed to get the clock: %s\n",
> +					clk_names[i]);
> +			return ERR_PTR(PTR_ERR(dsi->clks[i]));
> +		}
> +	}
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	dsi->reg_base = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(dsi->reg_base)) {
> +		dev_err(dev, "failed to remap io region\n");
> +		return dsi->reg_base;
> +	}
> +
> +	dsi->phy = devm_phy_get(dev, "dsim");
> +	if (IS_ERR(dsi->phy)) {
> +		dev_info(dev, "failed to get dsim phy\n");
> +		return ERR_PTR(PTR_ERR(dsi->phy));
> +	}
> +
> +	dsi->irq = platform_get_irq(pdev, 0);
> +	if (dsi->irq < 0)
> +		return ERR_PTR(dsi->irq);
> +
> +	irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
> +	ret = devm_request_threaded_irq(dev, dsi->irq, NULL,
> +					samsung_dsim_irq, IRQF_ONESHOT,
> +					dev_name(dev), dsi);
> +	if (ret) {
> +		dev_err(dev, "failed to request dsi irq\n");
> +		return ERR_PTR(ret);
> +	}
> +
> +	ret = samsung_dsim_parse_dt(dsi);
> +	if (ret)
> +		return ERR_PTR(ret);
> +
> +	ret = mipi_dsi_host_register(&dsi->dsi_host);
> +	if (ret)
> +		return ERR_PTR(ret);
> +
> +	bridge = &dsi->bridge;
> +	bridge->driver_private = dsi;
> +	bridge->funcs = &samsung_dsim_bridge_funcs;
> +	bridge->of_node = dev->of_node;
> +	drm_bridge_add(bridge);
> +
> +	return dsi;
> +}
> +
> +static void __samsung_dsim_remove(struct samsung_dsim *dsi)
> +{
> +	drm_bridge_remove(&dsi->bridge);
> +
> +	mipi_dsi_host_unregister(&dsi->dsi_host);
> +}
> +
> +/*
> + * Probe/remove API, used from platforms based on the DRM bridge API.
> + */
> +struct samsung_dsim *samsung_dsim_probe(struct platform_device *pdev)
> +{
> +	return __samsung_dsim_probe(pdev);
> +}
> +EXPORT_SYMBOL_GPL(samsung_dsim_probe);
> +
> +void samsung_dsim_remove(struct samsung_dsim *dsi)
> +{
> +	return __samsung_dsim_remove(dsi);
> +}
> +EXPORT_SYMBOL_GPL(samsung_dsim_remove);
> +
> +/*
> + * Bind/unbind API, used from platforms based on the component framework.
> + */
> +int samsung_dsim_bind(struct samsung_dsim *dsi, struct drm_encoder *encoder)
> +{
> +	struct drm_bridge *previous = drm_bridge_chain_get_first_bridge(encoder);
> +
> +	return drm_bridge_attach(encoder, &dsi->bridge, previous, 0);
> +}
> +EXPORT_SYMBOL_GPL(samsung_dsim_bind);
> +
> +void samsung_dsim_unbind(struct samsung_dsim *dsi)
> +{
> +	samsung_dsim_disable(dsi);
> +}
> +EXPORT_SYMBOL_GPL(samsung_dsim_unbind);
> +
> +int samsung_dsim_suspend(struct samsung_dsim *dsi)
> +{
> +	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
> +	int ret, i;
> +
> +	usleep_range(10000, 20000);
> +
> +	if (dsi->state & DSIM_STATE_INITIALIZED) {
> +		dsi->state &= ~DSIM_STATE_INITIALIZED;
> +
> +		samsung_dsim_disable_clock(dsi);
> +
> +		samsung_dsim_disable_irq(dsi);
> +	}
> +
> +	dsi->state &= ~DSIM_STATE_CMD_LPM;
> +
> +	phy_power_off(dsi->phy);
> +
> +	for (i = driver_data->num_clks - 1; i > -1; i--)
> +		clk_disable_unprepare(dsi->clks[i]);
> +
> +	ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
> +	if (ret < 0)
> +		dev_err(dsi->dev, "cannot disable regulators %d\n", ret);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(samsung_dsim_suspend);
> +
> +int samsung_dsim_resume(struct samsung_dsim *dsi)
> +{
> +	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
> +	int ret, i;
> +
> +	ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
> +	if (ret < 0) {
> +		dev_err(dsi->dev, "cannot enable regulators %d\n", ret);
> +		return ret;
> +	}
> +
> +	for (i = 0; i < driver_data->num_clks; i++) {
> +		ret = clk_prepare_enable(dsi->clks[i]);
> +		if (ret < 0)
> +			goto err_clk;
> +	}
> +
> +	ret = phy_power_on(dsi->phy);
> +	if (ret < 0) {
> +		dev_err(dsi->dev, "cannot enable phy %d\n", ret);
> +		goto err_clk;
> +	}
> +
> +	return 0;
> +
> +err_clk:
> +	while (--i > -1)
> +		clk_disable_unprepare(dsi->clks[i]);
> +	regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(samsung_dsim_resume);
> +
> +MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
> +MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
> +MODULE_DESCRIPTION("Samsung SoC MIPI DSI Master");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
> index 6417f374b923..3bc321ab5bc8 100644
> --- a/drivers/gpu/drm/exynos/Kconfig
> +++ b/drivers/gpu/drm/exynos/Kconfig
> @@ -16,7 +16,6 @@ comment "CRTCs"
>  
>  config DRM_EXYNOS_FIMD
>  	bool "FIMD"
> -	depends on !FB_S3C
>  	select MFD_SYSCON
>  	help
>  	  Choose this option if you want to use Exynos FIMD for DRM.
> @@ -28,7 +27,6 @@ config DRM_EXYNOS5433_DECON
>  
>  config DRM_EXYNOS7_DECON
>  	bool "DECON on Exynos7"
> -	depends on !FB_S3C
>  	help
>  	  Choose this option if you want to use Exynos DECON for DRM.
>  
> @@ -55,8 +53,7 @@ config DRM_EXYNOS_DPI
>  config DRM_EXYNOS_DSI
>  	bool "MIPI-DSI host"
>  	depends on DRM_EXYNOS_FIMD || DRM_EXYNOS5433_DECON || DRM_EXYNOS7_DECON
> -	select DRM_MIPI_DSI
> -	select DRM_PANEL
> +	select DRM_SAMSUNG_DSIM
>  	default n
>  	help
>  	  This enables support for Exynos MIPI-DSI device.
> diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile
> index add70b336935..2fd2f3ee4fcf 100644
> --- a/drivers/gpu/drm/exynos/Makefile
> +++ b/drivers/gpu/drm/exynos/Makefile
> @@ -11,7 +11,7 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD)	+= exynos_drm_fimd.o
>  exynosdrm-$(CONFIG_DRM_EXYNOS5433_DECON)	+= exynos5433_drm_decon.o
>  exynosdrm-$(CONFIG_DRM_EXYNOS7_DECON)	+= exynos7_drm_decon.o
>  exynosdrm-$(CONFIG_DRM_EXYNOS_DPI)	+= exynos_drm_dpi.o
> -exynosdrm-$(CONFIG_DRM_EXYNOS_DSI)	+= exynos_drm_dsi.o exynos_drm_dsi_pltfm.o
> +exynosdrm-$(CONFIG_DRM_EXYNOS_DSI)	+= exynos_drm_dsi.o
>  exynosdrm-$(CONFIG_DRM_EXYNOS_DP)	+= exynos_dp.o
>  exynosdrm-$(CONFIG_DRM_EXYNOS_MIXER)	+= exynos_mixer.o
>  exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI)	+= exynos_hdmi.o
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> index e8aea9d90c34..17f37fa74718 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> @@ -5,1774 +5,328 @@
>   * Copyright (c) 2014 Samsung Electronics Co., Ltd
>   *
>   * Contacts: Tomasz Figa <t.figa@samsung.com>
> -*/
> + */
>  
> -#include <linux/clk.h>
> -#include <linux/delay.h>
>  #include <linux/component.h>
> -#include <linux/gpio/consumer.h>
> -#include <linux/irq.h>
>  #include <linux/of_device.h>
> -#include <linux/of_gpio.h>
>  #include <linux/of_graph.h>
> -#include <linux/phy/phy.h>
> -#include <linux/regulator/consumer.h>
> -
> -#include <asm/unaligned.h>
> +#include <linux/pm_runtime.h>
>  
> -#include <video/mipi_display.h>
> -#include <video/videomode.h>
> -
> -#include <drm/drm_atomic_helper.h>
> +#include <drm/bridge/samsung-dsim.h>
>  #include <drm/drm_bridge.h>
> -#include <drm/drm_fb_helper.h>
> +#include <drm/drm_encoder.h>
>  #include <drm/drm_mipi_dsi.h>
> -#include <drm/drm_panel.h>
> -#include <drm/drm_print.h>
>  #include <drm/drm_probe_helper.h>
>  #include <drm/drm_simple_kms_helper.h>
>  
> -#include "exynos_drm_dsi.h"
> -
> -/* returns true iff both arguments logically differs */
> -#define NEQV(a, b) (!(a) ^ !(b))
> -
> -/* DSIM_STATUS */
> -#define DSIM_STOP_STATE_DAT(x)		(((x) & 0xf) << 0)
> -#define DSIM_STOP_STATE_CLK		(1 << 8)
> -#define DSIM_TX_READY_HS_CLK		(1 << 10)
> -#define DSIM_PLL_STABLE			(1 << 31)
> -
> -/* DSIM_TIMEOUT */
> -#define DSIM_LPDR_TIMEOUT(x)		((x) << 0)
> -#define DSIM_BTA_TIMEOUT(x)		((x) << 16)
> -
> -/* DSIM_CLKCTRL */
> -#define DSIM_ESC_PRESCALER(x)		(((x) & 0xffff) << 0)
> -#define DSIM_ESC_PRESCALER_MASK		(0xffff << 0)
> -#define DSIM_LANE_ESC_CLK_EN_CLK	(1 << 19)
> -#define DSIM_LANE_ESC_CLK_EN_DATA(x)	(((x) & 0xf) << 20)
> -#define DSIM_LANE_ESC_CLK_EN_DATA_MASK	(0xf << 20)
> -#define DSIM_BYTE_CLKEN			(1 << 24)
> -#define DSIM_BYTE_CLK_SRC(x)		(((x) & 0x3) << 25)
> -#define DSIM_BYTE_CLK_SRC_MASK		(0x3 << 25)
> -#define DSIM_PLL_BYPASS			(1 << 27)
> -#define DSIM_ESC_CLKEN			(1 << 28)
> -#define DSIM_TX_REQUEST_HSCLK		(1 << 31)
> -
> -/* DSIM_CONFIG */
> -#define DSIM_LANE_EN_CLK		(1 << 0)
> -#define DSIM_LANE_EN(x)			(((x) & 0xf) << 1)
> -#define DSIM_NUM_OF_DATA_LANE(x)	(((x) & 0x3) << 5)
> -#define DSIM_SUB_PIX_FORMAT(x)		(((x) & 0x7) << 8)
> -#define DSIM_MAIN_PIX_FORMAT_MASK	(0x7 << 12)
> -#define DSIM_MAIN_PIX_FORMAT_RGB888	(0x7 << 12)
> -#define DSIM_MAIN_PIX_FORMAT_RGB666	(0x6 << 12)
> -#define DSIM_MAIN_PIX_FORMAT_RGB666_P	(0x5 << 12)
> -#define DSIM_MAIN_PIX_FORMAT_RGB565	(0x4 << 12)
> -#define DSIM_SUB_VC			(((x) & 0x3) << 16)
> -#define DSIM_MAIN_VC			(((x) & 0x3) << 18)
> -#define DSIM_HSA_MODE			(1 << 20)
> -#define DSIM_HBP_MODE			(1 << 21)
> -#define DSIM_HFP_MODE			(1 << 22)
> -#define DSIM_HSE_MODE			(1 << 23)
> -#define DSIM_AUTO_MODE			(1 << 24)
> -#define DSIM_VIDEO_MODE			(1 << 25)
> -#define DSIM_BURST_MODE			(1 << 26)
> -#define DSIM_SYNC_INFORM		(1 << 27)
> -#define DSIM_EOT_DISABLE		(1 << 28)
> -#define DSIM_MFLUSH_VS			(1 << 29)
> -/* This flag is valid only for exynos3250/3472/5260/5430 */
> -#define DSIM_CLKLANE_STOP		(1 << 30)
> -
> -/* DSIM_ESCMODE */
> -#define DSIM_TX_TRIGGER_RST		(1 << 4)
> -#define DSIM_TX_LPDT_LP			(1 << 6)
> -#define DSIM_CMD_LPDT_LP		(1 << 7)
> -#define DSIM_FORCE_BTA			(1 << 16)
> -#define DSIM_FORCE_STOP_STATE		(1 << 20)
> -#define DSIM_STOP_STATE_CNT(x)		(((x) & 0x7ff) << 21)
> -#define DSIM_STOP_STATE_CNT_MASK	(0x7ff << 21)
> -
> -/* DSIM_MDRESOL */
> -#define DSIM_MAIN_STAND_BY		(1 << 31)
> -#define DSIM_MAIN_VRESOL(x, num_bits)	(((x) & ((1 << (num_bits)) - 1)) << 16)
> -#define DSIM_MAIN_HRESOL(x, num_bits)	(((x) & ((1 << (num_bits)) - 1)) << 0)
> -
> -/* DSIM_MVPORCH */
> -#define DSIM_CMD_ALLOW(x)		((x) << 28)
> -#define DSIM_STABLE_VFP(x)		((x) << 16)
> -#define DSIM_MAIN_VBP(x)		((x) << 0)
> -#define DSIM_CMD_ALLOW_MASK		(0xf << 28)
> -#define DSIM_STABLE_VFP_MASK		(0x7ff << 16)
> -#define DSIM_MAIN_VBP_MASK		(0x7ff << 0)
> -
> -/* DSIM_MHPORCH */
> -#define DSIM_MAIN_HFP(x)		((x) << 16)
> -#define DSIM_MAIN_HBP(x)		((x) << 0)
> -#define DSIM_MAIN_HFP_MASK		((0xffff) << 16)
> -#define DSIM_MAIN_HBP_MASK		((0xffff) << 0)
> -
> -/* DSIM_MSYNC */
> -#define DSIM_MAIN_VSA(x)		((x) << 22)
> -#define DSIM_MAIN_HSA(x)		((x) << 0)
> -#define DSIM_MAIN_VSA_MASK		((0x3ff) << 22)
> -#define DSIM_MAIN_HSA_MASK		((0xffff) << 0)
> -
> -/* DSIM_SDRESOL */
> -#define DSIM_SUB_STANDY(x)		((x) << 31)
> -#define DSIM_SUB_VRESOL(x)		((x) << 16)
> -#define DSIM_SUB_HRESOL(x)		((x) << 0)
> -#define DSIM_SUB_STANDY_MASK		((0x1) << 31)
> -#define DSIM_SUB_VRESOL_MASK		((0x7ff) << 16)
> -#define DSIM_SUB_HRESOL_MASK		((0x7ff) << 0)
> -
> -/* DSIM_INTSRC */
> -#define DSIM_INT_PLL_STABLE		(1 << 31)
> -#define DSIM_INT_SW_RST_RELEASE		(1 << 30)
> -#define DSIM_INT_SFR_FIFO_EMPTY		(1 << 29)
> -#define DSIM_INT_SFR_HDR_FIFO_EMPTY	(1 << 28)
> -#define DSIM_INT_BTA			(1 << 25)
> -#define DSIM_INT_FRAME_DONE		(1 << 24)
> -#define DSIM_INT_RX_TIMEOUT		(1 << 21)
> -#define DSIM_INT_BTA_TIMEOUT		(1 << 20)
> -#define DSIM_INT_RX_DONE		(1 << 18)
> -#define DSIM_INT_RX_TE			(1 << 17)
> -#define DSIM_INT_RX_ACK			(1 << 16)
> -#define DSIM_INT_RX_ECC_ERR		(1 << 15)
> -#define DSIM_INT_RX_CRC_ERR		(1 << 14)
> +#include "exynos_drm_crtc.h"
> +#include "exynos_drm_drv.h"
>  
> -/* DSIM_FIFOCTRL */
> -#define DSIM_RX_DATA_FULL		(1 << 25)
> -#define DSIM_RX_DATA_EMPTY		(1 << 24)
> -#define DSIM_SFR_HEADER_FULL		(1 << 23)
> -#define DSIM_SFR_HEADER_EMPTY		(1 << 22)
> -#define DSIM_SFR_PAYLOAD_FULL		(1 << 21)
> -#define DSIM_SFR_PAYLOAD_EMPTY		(1 << 20)
> -#define DSIM_I80_HEADER_FULL		(1 << 19)
> -#define DSIM_I80_HEADER_EMPTY		(1 << 18)
> -#define DSIM_I80_PAYLOAD_FULL		(1 << 17)
> -#define DSIM_I80_PAYLOAD_EMPTY		(1 << 16)
> -#define DSIM_SD_HEADER_FULL		(1 << 15)
> -#define DSIM_SD_HEADER_EMPTY		(1 << 14)
> -#define DSIM_SD_PAYLOAD_FULL		(1 << 13)
> -#define DSIM_SD_PAYLOAD_EMPTY		(1 << 12)
> -#define DSIM_MD_HEADER_FULL		(1 << 11)
> -#define DSIM_MD_HEADER_EMPTY		(1 << 10)
> -#define DSIM_MD_PAYLOAD_FULL		(1 << 9)
> -#define DSIM_MD_PAYLOAD_EMPTY		(1 << 8)
> -#define DSIM_RX_FIFO			(1 << 4)
> -#define DSIM_SFR_FIFO			(1 << 3)
> -#define DSIM_I80_FIFO			(1 << 2)
> -#define DSIM_SD_FIFO			(1 << 1)
> -#define DSIM_MD_FIFO			(1 << 0)
> -
> -/* DSIM_PHYACCHR */
> -#define DSIM_AFC_EN			(1 << 14)
> -#define DSIM_AFC_CTL(x)			(((x) & 0x7) << 5)
> -
> -/* DSIM_PLLCTRL */
> -#define DSIM_FREQ_BAND(x)		((x) << 24)
> -#define DSIM_PLL_EN			(1 << 23)
> -#define DSIM_PLL_P(x)			((x) << 13)
> -#define DSIM_PLL_M(x)			((x) << 4)
> -#define DSIM_PLL_S(x)			((x) << 1)
> -
> -/* DSIM_PHYCTRL */
> -#define DSIM_PHYCTRL_ULPS_EXIT(x)	(((x) & 0x1ff) << 0)
> -#define DSIM_PHYCTRL_B_DPHYCTL_VREG_LP	(1 << 30)
> -#define DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP	(1 << 14)
> -
> -/* DSIM_PHYTIMING */
> -#define DSIM_PHYTIMING_LPX(x)		((x) << 8)
> -#define DSIM_PHYTIMING_HS_EXIT(x)	((x) << 0)
> -
> -/* DSIM_PHYTIMING1 */
> -#define DSIM_PHYTIMING1_CLK_PREPARE(x)	((x) << 24)
> -#define DSIM_PHYTIMING1_CLK_ZERO(x)	((x) << 16)
> -#define DSIM_PHYTIMING1_CLK_POST(x)	((x) << 8)
> -#define DSIM_PHYTIMING1_CLK_TRAIL(x)	((x) << 0)
> -
> -/* DSIM_PHYTIMING2 */
> -#define DSIM_PHYTIMING2_HS_PREPARE(x)	((x) << 16)
> -#define DSIM_PHYTIMING2_HS_ZERO(x)	((x) << 8)
> -#define DSIM_PHYTIMING2_HS_TRAIL(x)	((x) << 0)
> -
> -#define DSI_MAX_BUS_WIDTH		4
> -#define DSI_NUM_VIRTUAL_CHANNELS	4
> -#define DSI_TX_FIFO_SIZE		2048
> -#define DSI_RX_FIFO_SIZE		256
> -#define DSI_XFER_TIMEOUT_MS		100
> -#define DSI_RX_FIFO_EMPTY		0x30800002
> -
> -#define OLD_SCLK_MIPI_CLK_NAME "pll_clk"
> -
> -static const char *const clk_names[5] = { "bus_clk", "sclk_mipi",
> -	"phyclk_mipidphy0_bitclkdiv8", "phyclk_mipidphy0_rxclkesc0",
> -	"sclk_rgb_vclk_to_dsim0" };
> -
> -enum exynos_dsi_transfer_type {
> -	EXYNOS_DSI_TX,
> -	EXYNOS_DSI_RX,
> +enum {
> +	DSI_PORT_IN,
> +	DSI_PORT_OUT
>  };
>  
> -struct exynos_dsi_transfer {
> -	struct list_head list;
> -	struct completion completed;
> -	int result;
> -	struct mipi_dsi_packet packet;
> -	u16 flags;
> -	u16 tx_done;
> -
> -	u8 *rx_payload;
> -	u16 rx_len;
> -	u16 rx_done;
> -};
> -
> -#define DSIM_STATE_ENABLED		BIT(0)
> -#define DSIM_STATE_INITIALIZED		BIT(1)
> -#define DSIM_STATE_CMD_LPM		BIT(2)
> -#define DSIM_STATE_VIDOUT_AVAILABLE	BIT(3)
> -
>  struct exynos_dsi {
> -	struct drm_bridge bridge;
> -	struct mipi_dsi_host dsi_host;
> -	struct drm_connector connector;
> -	struct drm_panel *panel;
> -	struct list_head bridge_chain;
> -	struct drm_bridge *out_bridge;
> -	struct device *dev;
> -
> -	void __iomem *reg_base;
> -	struct phy *phy;
> -	struct clk **clks;
> -	struct regulator_bulk_data supplies[2];
> -	int irq;
> -	int te_gpio;
> -
> -	u32 pll_clk_rate;
> -	u32 burst_clk_rate;
> -	u32 esc_clk_rate;
> -	u32 lanes;
> -	u32 mode_flags;
> -	u32 format;
> -
> -	struct drm_display_mode mode;
> -
> -	int state;
> -	struct drm_property *brightness;
> -	struct completion completed;
> -
> -	spinlock_t transfer_lock; /* protects transfer_list */
> -	struct list_head transfer_list;
> -
> -	const struct exynos_dsi_driver_data *driver_data;
> +	struct samsung_dsim *dsi;
> +	struct drm_encoder encoder;
>  };
>  
> -#define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host)
> -#define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector)
> -
> -enum reg_idx {
> -	DSIM_STATUS_REG,	/* Status register */
> -	DSIM_SWRST_REG,		/* Software reset register */
> -	DSIM_CLKCTRL_REG,	/* Clock control register */
> -	DSIM_TIMEOUT_REG,	/* Time out register */
> -	DSIM_CONFIG_REG,	/* Configuration register */
> -	DSIM_ESCMODE_REG,	/* Escape mode register */
> -	DSIM_MDRESOL_REG,
> -	DSIM_MVPORCH_REG,	/* Main display Vporch register */
> -	DSIM_MHPORCH_REG,	/* Main display Hporch register */
> -	DSIM_MSYNC_REG,		/* Main display sync area register */
> -	DSIM_INTSRC_REG,	/* Interrupt source register */
> -	DSIM_INTMSK_REG,	/* Interrupt mask register */
> -	DSIM_PKTHDR_REG,	/* Packet Header FIFO register */
> -	DSIM_PAYLOAD_REG,	/* Payload FIFO register */
> -	DSIM_RXFIFO_REG,	/* Read FIFO register */
> -	DSIM_FIFOCTRL_REG,	/* FIFO status and control register */
> -	DSIM_PLLCTRL_REG,	/* PLL control register */
> -	DSIM_PHYCTRL_REG,
> -	DSIM_PHYTIMING_REG,
> -	DSIM_PHYTIMING1_REG,
> -	DSIM_PHYTIMING2_REG,
> -	NUM_REGS
> +static const unsigned int reg_values[] = {
> +	[RESET_TYPE] = DSIM_SWRST,
> +	[PLL_TIMER] = 500,
> +	[STOP_STATE_CNT] = 0xf,
> +	[PHYCTRL_ULPS_EXIT] = 0x0af,
> +	[PHYCTRL_VREG_LP] = 0,
> +	[PHYCTRL_SLEW_UP] = 0,
> +	[PHYTIMING_LPX] = 0x06,
> +	[PHYTIMING_HS_EXIT] = 0x0b,
> +	[PHYTIMING_CLK_PREPARE] = 0x07,
> +	[PHYTIMING_CLK_ZERO] = 0x27,
> +	[PHYTIMING_CLK_POST] = 0x0d,
> +	[PHYTIMING_CLK_TRAIL] = 0x08,
> +	[PHYTIMING_HS_PREPARE] = 0x09,
> +	[PHYTIMING_HS_ZERO] = 0x0d,
> +	[PHYTIMING_HS_TRAIL] = 0x0b,
>  };
>  
> -static const unsigned int exynos_reg_ofs[] = {
> -	[DSIM_STATUS_REG] =  0x00,
> -	[DSIM_SWRST_REG] =  0x04,
> -	[DSIM_CLKCTRL_REG] =  0x08,
> -	[DSIM_TIMEOUT_REG] =  0x0c,
> -	[DSIM_CONFIG_REG] =  0x10,
> -	[DSIM_ESCMODE_REG] =  0x14,
> -	[DSIM_MDRESOL_REG] =  0x18,
> -	[DSIM_MVPORCH_REG] =  0x1c,
> -	[DSIM_MHPORCH_REG] =  0x20,
> -	[DSIM_MSYNC_REG] =  0x24,
> -	[DSIM_INTSRC_REG] =  0x2c,
> -	[DSIM_INTMSK_REG] =  0x30,
> -	[DSIM_PKTHDR_REG] =  0x34,
> -	[DSIM_PAYLOAD_REG] =  0x38,
> -	[DSIM_RXFIFO_REG] =  0x3c,
> -	[DSIM_FIFOCTRL_REG] =  0x44,
> -	[DSIM_PLLCTRL_REG] =  0x4c,
> -	[DSIM_PHYCTRL_REG] =  0x5c,
> -	[DSIM_PHYTIMING_REG] =  0x64,
> -	[DSIM_PHYTIMING1_REG] =  0x68,
> -	[DSIM_PHYTIMING2_REG] =  0x6c,
> +static const unsigned int exynos5422_reg_values[] = {
> +	[RESET_TYPE] = DSIM_SWRST,
> +	[PLL_TIMER] = 500,
> +	[STOP_STATE_CNT] = 0xf,
> +	[PHYCTRL_ULPS_EXIT] = 0xaf,
> +	[PHYCTRL_VREG_LP] = 0,
> +	[PHYCTRL_SLEW_UP] = 0,
> +	[PHYTIMING_LPX] = 0x08,
> +	[PHYTIMING_HS_EXIT] = 0x0d,
> +	[PHYTIMING_CLK_PREPARE] = 0x09,
> +	[PHYTIMING_CLK_ZERO] = 0x30,
> +	[PHYTIMING_CLK_POST] = 0x0e,
> +	[PHYTIMING_CLK_TRAIL] = 0x0a,
> +	[PHYTIMING_HS_PREPARE] = 0x0c,
> +	[PHYTIMING_HS_ZERO] = 0x11,
> +	[PHYTIMING_HS_TRAIL] = 0x0d,
>  };
>  
> -static const unsigned int exynos5433_reg_ofs[] = {
> -	[DSIM_STATUS_REG] = 0x04,
> -	[DSIM_SWRST_REG] = 0x0C,
> -	[DSIM_CLKCTRL_REG] = 0x10,
> -	[DSIM_TIMEOUT_REG] = 0x14,
> -	[DSIM_CONFIG_REG] = 0x18,
> -	[DSIM_ESCMODE_REG] = 0x1C,
> -	[DSIM_MDRESOL_REG] = 0x20,
> -	[DSIM_MVPORCH_REG] = 0x24,
> -	[DSIM_MHPORCH_REG] = 0x28,
> -	[DSIM_MSYNC_REG] = 0x2C,
> -	[DSIM_INTSRC_REG] = 0x34,
> -	[DSIM_INTMSK_REG] = 0x38,
> -	[DSIM_PKTHDR_REG] = 0x3C,
> -	[DSIM_PAYLOAD_REG] = 0x40,
> -	[DSIM_RXFIFO_REG] = 0x44,
> -	[DSIM_FIFOCTRL_REG] = 0x4C,
> -	[DSIM_PLLCTRL_REG] = 0x94,
> -	[DSIM_PHYCTRL_REG] = 0xA4,
> -	[DSIM_PHYTIMING_REG] = 0xB4,
> -	[DSIM_PHYTIMING1_REG] = 0xB8,
> -	[DSIM_PHYTIMING2_REG] = 0xBC,
> +static const unsigned int exynos5433_reg_values[] = {
> +	[RESET_TYPE] = DSIM_FUNCRST,
> +	[PLL_TIMER] = 22200,
> +	[STOP_STATE_CNT] = 0xa,
> +	[PHYCTRL_ULPS_EXIT] = 0x190,
> +	[PHYCTRL_VREG_LP] = 1,
> +	[PHYCTRL_SLEW_UP] = 1,
> +	[PHYTIMING_LPX] = 0x07,
> +	[PHYTIMING_HS_EXIT] = 0x0c,
> +	[PHYTIMING_CLK_PREPARE] = 0x09,
> +	[PHYTIMING_CLK_ZERO] = 0x2d,
> +	[PHYTIMING_CLK_POST] = 0x0e,
> +	[PHYTIMING_CLK_TRAIL] = 0x09,
> +	[PHYTIMING_HS_PREPARE] = 0x0b,
> +	[PHYTIMING_HS_ZERO] = 0x10,
> +	[PHYTIMING_HS_TRAIL] = 0x0c,
>  };
>  
> -static inline void exynos_dsi_write(struct exynos_dsi *dsi, enum reg_idx idx,
> -				    u32 val)
> -{
> -	const unsigned int *reg_ofs;
> -
> -	if (dsi->driver_data->reg_ofs == EXYNOS5433_REG_OFS)
> -		reg_ofs = exynos5433_reg_ofs;
> -	else
> -		reg_ofs = exynos_reg_ofs;
> -
> -	writel(val, dsi->reg_base + reg_ofs[idx]);
> -}
> -
> -static inline u32 exynos_dsi_read(struct exynos_dsi *dsi, enum reg_idx idx)
> -{
> -	const unsigned int *reg_ofs;
> -
> -	if (dsi->driver_data->reg_ofs == EXYNOS5433_REG_OFS)
> -		reg_ofs = exynos5433_reg_ofs;
> -	else
> -		reg_ofs = exynos_reg_ofs;
> -
> -	return readl(dsi->reg_base + reg_ofs[idx]);
> -}
> -
> -static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi)
> -{
> -	if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300)))
> -		return;
> -
> -	dev_err(dsi->dev, "timeout waiting for reset\n");
> -}
> -
> -static void exynos_dsi_reset(struct exynos_dsi *dsi)
> -{
> -	u32 reset_val = dsi->driver_data->reg_values[RESET_TYPE];
> -
> -	reinit_completion(&dsi->completed);
> -	exynos_dsi_write(dsi, DSIM_SWRST_REG, reset_val);
> -}
> -
> -#ifndef MHZ
> -#define MHZ	(1000*1000)
> -#endif
> -
> -static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi,
> -		unsigned long fin, unsigned long fout, u8 *p, u16 *m, u8 *s)
> -{
> -	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
> -	unsigned long best_freq = 0;
> -	u32 min_delta = 0xffffffff;
> -	u8 p_min, p_max;
> -	u8 _p, best_p;
> -	u16 _m, best_m;
> -	u8 _s, best_s;
> -
> -	p_min = DIV_ROUND_UP(fin, (12 * MHZ));
> -	p_max = fin / (6 * MHZ);
> -
> -	for (_p = p_min; _p <= p_max; ++_p) {
> -		for (_s = 0; _s <= 5; ++_s) {
> -			u64 tmp;
> -			u32 delta;
> -
> -			tmp = (u64)fout * (_p << _s);
> -			do_div(tmp, fin);
> -			_m = tmp;
> -			if (_m < 41 || _m > 125)
> -				continue;
> -
> -			tmp = (u64)_m * fin;
> -			do_div(tmp, _p);
> -			if (tmp < 500 * MHZ ||
> -					tmp > driver_data->max_freq * MHZ)
> -				continue;
> -
> -			tmp = (u64)_m * fin;
> -			do_div(tmp, _p << _s);
> -
> -			delta = abs(fout - tmp);
> -			if (delta < min_delta) {
> -				best_p = _p;
> -				best_m = _m;
> -				best_s = _s;
> -				min_delta = delta;
> -				best_freq = tmp;
> -			}
> -		}
> -	}
> -
> -	if (best_freq) {
> -		*p = best_p;
> -		*m = best_m;
> -		*s = best_s;
> -	}
> -
> -	return best_freq;
> -}
> -
> -static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi,
> -					unsigned long freq)
> -{
> -	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
> -	unsigned long fin, fout;
> -	int timeout;
> -	u8 p, s;
> -	u16 m;
> -	u32 reg;
> -
> -	fin = dsi->pll_clk_rate;
> -	fout = exynos_dsi_pll_find_pms(dsi, fin, freq, &p, &m, &s);
> -	if (!fout) {
> -		dev_err(dsi->dev,
> -			"failed to find PLL PMS for requested frequency\n");
> -		return 0;
> -	}
> -	dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s);
> -
> -	writel(driver_data->reg_values[PLL_TIMER],
> -			dsi->reg_base + driver_data->plltmr_reg);
> -
> -	reg = DSIM_PLL_EN | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s);
> -
> -	if (driver_data->has_freqband) {
> -		static const unsigned long freq_bands[] = {
> -			100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ,
> -			270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ,
> -			510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ,
> -			770 * MHZ, 870 * MHZ, 950 * MHZ,
> -		};
> -		int band;
> -
> -		for (band = 0; band < ARRAY_SIZE(freq_bands); ++band)
> -			if (fout < freq_bands[band])
> -				break;
> -
> -		dev_dbg(dsi->dev, "band %d\n", band);
> -
> -		reg |= DSIM_FREQ_BAND(band);
> -	}
> -
> -	exynos_dsi_write(dsi, DSIM_PLLCTRL_REG, reg);
> -
> -	timeout = 1000;
> -	do {
> -		if (timeout-- == 0) {
> -			dev_err(dsi->dev, "PLL failed to stabilize\n");
> -			return 0;
> -		}
> -		reg = exynos_dsi_read(dsi, DSIM_STATUS_REG);
> -	} while ((reg & DSIM_PLL_STABLE) == 0);
> -
> -	return fout;
> -}
> -
> -static int exynos_dsi_enable_clock(struct exynos_dsi *dsi)
> -{
> -	unsigned long hs_clk, byte_clk, esc_clk;
> -	unsigned long esc_div;
> -	u32 reg;
> -
> -	hs_clk = exynos_dsi_set_pll(dsi, dsi->burst_clk_rate);
> -	if (!hs_clk) {
> -		dev_err(dsi->dev, "failed to configure DSI PLL\n");
> -		return -EFAULT;
> -	}
> -
> -	byte_clk = hs_clk / 8;
> -	esc_div = DIV_ROUND_UP(byte_clk, dsi->esc_clk_rate);
> -	esc_clk = byte_clk / esc_div;
> -
> -	if (esc_clk > 20 * MHZ) {
> -		++esc_div;
> -		esc_clk = byte_clk / esc_div;
> -	}
> -
> -	dev_dbg(dsi->dev, "hs_clk = %lu, byte_clk = %lu, esc_clk = %lu\n",
> -		hs_clk, byte_clk, esc_clk);
> -
> -	reg = exynos_dsi_read(dsi, DSIM_CLKCTRL_REG);
> -	reg &= ~(DSIM_ESC_PRESCALER_MASK | DSIM_LANE_ESC_CLK_EN_CLK
> -			| DSIM_LANE_ESC_CLK_EN_DATA_MASK | DSIM_PLL_BYPASS
> -			| DSIM_BYTE_CLK_SRC_MASK);
> -	reg |= DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN
> -			| DSIM_ESC_PRESCALER(esc_div)
> -			| DSIM_LANE_ESC_CLK_EN_CLK
> -			| DSIM_LANE_ESC_CLK_EN_DATA(BIT(dsi->lanes) - 1)
> -			| DSIM_BYTE_CLK_SRC(0)
> -			| DSIM_TX_REQUEST_HSCLK;
> -	exynos_dsi_write(dsi, DSIM_CLKCTRL_REG, reg);
> -
> -	return 0;
> -}
> -
> -static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi)
> -{
> -	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
> -	const unsigned int *reg_values = driver_data->reg_values;
> -	u32 reg;
> -
> -	if (driver_data->has_freqband)
> -		return;
> -
> -	/* B D-PHY: D-PHY Master & Slave Analog Block control */
> -	reg = DSIM_PHYCTRL_ULPS_EXIT(reg_values[PHYCTRL_ULPS_EXIT]);
> -	if (reg_values[PHYCTRL_VREG_LP])
> -		reg |= DSIM_PHYCTRL_B_DPHYCTL_VREG_LP;
> -	if (reg_values[PHYCTRL_SLEW_UP])
> -		reg |= DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP;
> -	exynos_dsi_write(dsi, DSIM_PHYCTRL_REG, reg);
> -
> -	/*
> -	 * T LPX: Transmitted length of any Low-Power state period
> -	 * T HS-EXIT: Time that the transmitter drives LP-11 following a HS
> -	 *	burst
> -	 */
> -	reg = DSIM_PHYTIMING_LPX(reg_values[PHYTIMING_LPX]) |
> -		DSIM_PHYTIMING_HS_EXIT(reg_values[PHYTIMING_HS_EXIT]);
> -	exynos_dsi_write(dsi, DSIM_PHYTIMING_REG, reg);
> -
> -	/*
> -	 * T CLK-PREPARE: Time that the transmitter drives the Clock Lane LP-00
> -	 *	Line state immediately before the HS-0 Line state starting the
> -	 *	HS transmission
> -	 * T CLK-ZERO: Time that the transmitter drives the HS-0 state prior to
> -	 *	transmitting the Clock.
> -	 * T CLK_POST: Time that the transmitter continues to send HS clock
> -	 *	after the last associated Data Lane has transitioned to LP Mode
> -	 *	Interval is defined as the period from the end of T HS-TRAIL to
> -	 *	the beginning of T CLK-TRAIL
> -	 * T CLK-TRAIL: Time that the transmitter drives the HS-0 state after
> -	 *	the last payload clock bit of a HS transmission burst
> -	 */
> -	reg = DSIM_PHYTIMING1_CLK_PREPARE(reg_values[PHYTIMING_CLK_PREPARE]) |
> -		DSIM_PHYTIMING1_CLK_ZERO(reg_values[PHYTIMING_CLK_ZERO]) |
> -		DSIM_PHYTIMING1_CLK_POST(reg_values[PHYTIMING_CLK_POST]) |
> -		DSIM_PHYTIMING1_CLK_TRAIL(reg_values[PHYTIMING_CLK_TRAIL]);
> -	exynos_dsi_write(dsi, DSIM_PHYTIMING1_REG, reg);
> -
> -	/*
> -	 * T HS-PREPARE: Time that the transmitter drives the Data Lane LP-00
> -	 *	Line state immediately before the HS-0 Line state starting the
> -	 *	HS transmission
> -	 * T HS-ZERO: Time that the transmitter drives the HS-0 state prior to
> -	 *	transmitting the Sync sequence.
> -	 * T HS-TRAIL: Time that the transmitter drives the flipped differential
> -	 *	state after last payload data bit of a HS transmission burst
> -	 */
> -	reg = DSIM_PHYTIMING2_HS_PREPARE(reg_values[PHYTIMING_HS_PREPARE]) |
> -		DSIM_PHYTIMING2_HS_ZERO(reg_values[PHYTIMING_HS_ZERO]) |
> -		DSIM_PHYTIMING2_HS_TRAIL(reg_values[PHYTIMING_HS_TRAIL]);
> -	exynos_dsi_write(dsi, DSIM_PHYTIMING2_REG, reg);
> -}
> -
> -static void exynos_dsi_disable_clock(struct exynos_dsi *dsi)
> -{
> -	u32 reg;
> -
> -	reg = exynos_dsi_read(dsi, DSIM_CLKCTRL_REG);
> -	reg &= ~(DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK
> -			| DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN);
> -	exynos_dsi_write(dsi, DSIM_CLKCTRL_REG, reg);
> -
> -	reg = exynos_dsi_read(dsi, DSIM_PLLCTRL_REG);
> -	reg &= ~DSIM_PLL_EN;
> -	exynos_dsi_write(dsi, DSIM_PLLCTRL_REG, reg);
> -}
> -
> -static void exynos_dsi_enable_lane(struct exynos_dsi *dsi, u32 lane)
> -{
> -	u32 reg = exynos_dsi_read(dsi, DSIM_CONFIG_REG);
> -	reg |= (DSIM_NUM_OF_DATA_LANE(dsi->lanes - 1) | DSIM_LANE_EN_CLK |
> -			DSIM_LANE_EN(lane));
> -	exynos_dsi_write(dsi, DSIM_CONFIG_REG, reg);
> -}
> -
> -static int exynos_dsi_init_link(struct exynos_dsi *dsi)
> +static int exynos_dsi_host_attach(struct device *dev,
> +				  struct mipi_dsi_device *device)
>  {
> -	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
> -	int timeout;
> -	u32 reg;
> -	u32 lanes_mask;
> -
> -	/* Initialize FIFO pointers */
> -	reg = exynos_dsi_read(dsi, DSIM_FIFOCTRL_REG);
> -	reg &= ~0x1f;
> -	exynos_dsi_write(dsi, DSIM_FIFOCTRL_REG, reg);
> -
> -	usleep_range(9000, 11000);
> -
> -	reg |= 0x1f;
> -	exynos_dsi_write(dsi, DSIM_FIFOCTRL_REG, reg);
> -	usleep_range(9000, 11000);
> +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> +	struct drm_device *drm = dsi->encoder.dev;
> +	struct exynos_drm_crtc *crtc;
>  
> -	/* DSI configuration */
> -	reg = 0;
> +	mutex_lock(&drm->mode_config.mutex);
> +	crtc = exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD);
> +	crtc->i80_mode = !(device->mode_flags & MIPI_DSI_MODE_VIDEO);
> +	mutex_unlock(&drm->mode_config.mutex);
>  
> -	/*
> -	 * The first bit of mode_flags specifies display configuration.
> -	 * If this bit is set[= MIPI_DSI_MODE_VIDEO], dsi will support video
> -	 * mode, otherwise it will support command mode.
> -	 */
> -	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> -		reg |= DSIM_VIDEO_MODE;
> -
> -		/*
> -		 * The user manual describes that following bits are ignored in
> -		 * command mode.
> -		 */
> -		if (!(dsi->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH))
> -			reg |= DSIM_MFLUSH_VS;
> -		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
> -			reg |= DSIM_SYNC_INFORM;
> -		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
> -			reg |= DSIM_BURST_MODE;
> -		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT)
> -			reg |= DSIM_AUTO_MODE;
> -		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSE)
> -			reg |= DSIM_HSE_MODE;
> -		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HFP))
> -			reg |= DSIM_HFP_MODE;
> -		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HBP))
> -			reg |= DSIM_HBP_MODE;
> -		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSA))
> -			reg |= DSIM_HSA_MODE;
> -	}
> -
> -	if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET))
> -		reg |= DSIM_EOT_DISABLE;
> -
> -	switch (dsi->format) {
> -	case MIPI_DSI_FMT_RGB888:
> -		reg |= DSIM_MAIN_PIX_FORMAT_RGB888;
> -		break;
> -	case MIPI_DSI_FMT_RGB666:
> -		reg |= DSIM_MAIN_PIX_FORMAT_RGB666;
> -		break;
> -	case MIPI_DSI_FMT_RGB666_PACKED:
> -		reg |= DSIM_MAIN_PIX_FORMAT_RGB666_P;
> -		break;
> -	case MIPI_DSI_FMT_RGB565:
> -		reg |= DSIM_MAIN_PIX_FORMAT_RGB565;
> -		break;
> -	default:
> -		dev_err(dsi->dev, "invalid pixel format\n");
> -		return -EINVAL;
> -	}
> -
> -	/*
> -	 * Use non-continuous clock mode if the periparal wants and
> -	 * host controller supports
> -	 *
> -	 * In non-continous clock mode, host controller will turn off
> -	 * the HS clock between high-speed transmissions to reduce
> -	 * power consumption.
> -	 */
> -	if (driver_data->has_clklane_stop &&
> -			dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
> -		reg |= DSIM_CLKLANE_STOP;
> -	}
> -	exynos_dsi_write(dsi, DSIM_CONFIG_REG, reg);
> -
> -	lanes_mask = BIT(dsi->lanes) - 1;
> -	exynos_dsi_enable_lane(dsi, lanes_mask);
> -
> -	/* Check clock and data lane state are stop state */
> -	timeout = 100;
> -	do {
> -		if (timeout-- == 0) {
> -			dev_err(dsi->dev, "waiting for bus lanes timed out\n");
> -			return -EFAULT;
> -		}
> -
> -		reg = exynos_dsi_read(dsi, DSIM_STATUS_REG);
> -		if ((reg & DSIM_STOP_STATE_DAT(lanes_mask))
> -		    != DSIM_STOP_STATE_DAT(lanes_mask))
> -			continue;
> -	} while (!(reg & (DSIM_STOP_STATE_CLK | DSIM_TX_READY_HS_CLK)));
> -
> -	reg = exynos_dsi_read(dsi, DSIM_ESCMODE_REG);
> -	reg &= ~DSIM_STOP_STATE_CNT_MASK;
> -	reg |= DSIM_STOP_STATE_CNT(driver_data->reg_values[STOP_STATE_CNT]);
> -	exynos_dsi_write(dsi, DSIM_ESCMODE_REG, reg);
> -
> -	reg = DSIM_BTA_TIMEOUT(0xff) | DSIM_LPDR_TIMEOUT(0xffff);
> -	exynos_dsi_write(dsi, DSIM_TIMEOUT_REG, reg);
> +	if (drm->mode_config.poll_enabled)
> +		drm_kms_helper_hotplug_event(drm);
>  
>  	return 0;
>  }
>  
> -static void exynos_dsi_set_display_mode(struct exynos_dsi *dsi)
> -{
> -	struct drm_display_mode *m = &dsi->mode;
> -	unsigned int num_bits_resol = dsi->driver_data->num_bits_resol;
> -	u32 reg;
> -
> -	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> -		reg = DSIM_CMD_ALLOW(0xf)
> -			| DSIM_STABLE_VFP(m->vsync_start - m->vdisplay)
> -			| DSIM_MAIN_VBP(m->vtotal - m->vsync_end);
> -		exynos_dsi_write(dsi, DSIM_MVPORCH_REG, reg);
> -
> -		reg = DSIM_MAIN_HFP(m->hsync_start - m->hdisplay)
> -			| DSIM_MAIN_HBP(m->htotal - m->hsync_end);
> -		exynos_dsi_write(dsi, DSIM_MHPORCH_REG, reg);
> -
> -		reg = DSIM_MAIN_VSA(m->vsync_end - m->vsync_start)
> -			| DSIM_MAIN_HSA(m->hsync_end - m->hsync_start);
> -		exynos_dsi_write(dsi, DSIM_MSYNC_REG, reg);
> -	}
> -	reg =  DSIM_MAIN_HRESOL(m->hdisplay, num_bits_resol) |
> -		DSIM_MAIN_VRESOL(m->vdisplay, num_bits_resol);
> -
> -	exynos_dsi_write(dsi, DSIM_MDRESOL_REG, reg);
> -
> -	dev_dbg(dsi->dev, "LCD size = %dx%d\n", m->hdisplay, m->vdisplay);
> -}
> -
> -static void exynos_dsi_set_display_enable(struct exynos_dsi *dsi, bool enable)
> -{
> -	u32 reg;
> -
> -	reg = exynos_dsi_read(dsi, DSIM_MDRESOL_REG);
> -	if (enable)
> -		reg |= DSIM_MAIN_STAND_BY;
> -	else
> -		reg &= ~DSIM_MAIN_STAND_BY;
> -	exynos_dsi_write(dsi, DSIM_MDRESOL_REG, reg);
> -}
> -
> -static int exynos_dsi_wait_for_hdr_fifo(struct exynos_dsi *dsi)
> -{
> -	int timeout = 2000;
> -
> -	do {
> -		u32 reg = exynos_dsi_read(dsi, DSIM_FIFOCTRL_REG);
> -
> -		if (!(reg & DSIM_SFR_HEADER_FULL))
> -			return 0;
> -
> -		if (!cond_resched())
> -			usleep_range(950, 1050);
> -	} while (--timeout);
> -
> -	return -ETIMEDOUT;
> -}
> -
> -static void exynos_dsi_set_cmd_lpm(struct exynos_dsi *dsi, bool lpm)
> -{
> -	u32 v = exynos_dsi_read(dsi, DSIM_ESCMODE_REG);
> -
> -	if (lpm)
> -		v |= DSIM_CMD_LPDT_LP;
> -	else
> -		v &= ~DSIM_CMD_LPDT_LP;
> -
> -	exynos_dsi_write(dsi, DSIM_ESCMODE_REG, v);
> -}
> -
> -static void exynos_dsi_force_bta(struct exynos_dsi *dsi)
> -{
> -	u32 v = exynos_dsi_read(dsi, DSIM_ESCMODE_REG);
> -	v |= DSIM_FORCE_BTA;
> -	exynos_dsi_write(dsi, DSIM_ESCMODE_REG, v);
> -}
> -
> -static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi,
> -					struct exynos_dsi_transfer *xfer)
> -{
> -	struct device *dev = dsi->dev;
> -	struct mipi_dsi_packet *pkt = &xfer->packet;
> -	const u8 *payload = pkt->payload + xfer->tx_done;
> -	u16 length = pkt->payload_length - xfer->tx_done;
> -	bool first = !xfer->tx_done;
> -	u32 reg;
> -
> -	dev_dbg(dev, "< xfer %pK: tx len %u, done %u, rx len %u, done %u\n",
> -		xfer, length, xfer->tx_done, xfer->rx_len, xfer->rx_done);
> -
> -	if (length > DSI_TX_FIFO_SIZE)
> -		length = DSI_TX_FIFO_SIZE;
> -
> -	xfer->tx_done += length;
> -
> -	/* Send payload */
> -	while (length >= 4) {
> -		reg = get_unaligned_le32(payload);
> -		exynos_dsi_write(dsi, DSIM_PAYLOAD_REG, reg);
> -		payload += 4;
> -		length -= 4;
> -	}
> -
> -	reg = 0;
> -	switch (length) {
> -	case 3:
> -		reg |= payload[2] << 16;
> -		fallthrough;
> -	case 2:
> -		reg |= payload[1] << 8;
> -		fallthrough;
> -	case 1:
> -		reg |= payload[0];
> -		exynos_dsi_write(dsi, DSIM_PAYLOAD_REG, reg);
> -		break;
> -	}
> -
> -	/* Send packet header */
> -	if (!first)
> -		return;
> -
> -	reg = get_unaligned_le32(pkt->header);
> -	if (exynos_dsi_wait_for_hdr_fifo(dsi)) {
> -		dev_err(dev, "waiting for header FIFO timed out\n");
> -		return;
> -	}
> -
> -	if (NEQV(xfer->flags & MIPI_DSI_MSG_USE_LPM,
> -		 dsi->state & DSIM_STATE_CMD_LPM)) {
> -		exynos_dsi_set_cmd_lpm(dsi, xfer->flags & MIPI_DSI_MSG_USE_LPM);
> -		dsi->state ^= DSIM_STATE_CMD_LPM;
> -	}
> -
> -	exynos_dsi_write(dsi, DSIM_PKTHDR_REG, reg);
> -
> -	if (xfer->flags & MIPI_DSI_MSG_REQ_ACK)
> -		exynos_dsi_force_bta(dsi);
> -}
> -
> -static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi,
> -					struct exynos_dsi_transfer *xfer)
> -{
> -	u8 *payload = xfer->rx_payload + xfer->rx_done;
> -	bool first = !xfer->rx_done;
> -	struct device *dev = dsi->dev;
> -	u16 length;
> -	u32 reg;
> -
> -	if (first) {
> -		reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG);
> -
> -		switch (reg & 0x3f) {
> -		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
> -		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
> -			if (xfer->rx_len >= 2) {
> -				payload[1] = reg >> 16;
> -				++xfer->rx_done;
> -			}
> -			fallthrough;
> -		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
> -		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
> -			payload[0] = reg >> 8;
> -			++xfer->rx_done;
> -			xfer->rx_len = xfer->rx_done;
> -			xfer->result = 0;
> -			goto clear_fifo;
> -		case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
> -			dev_err(dev, "DSI Error Report: 0x%04x\n",
> -				(reg >> 8) & 0xffff);
> -			xfer->result = 0;
> -			goto clear_fifo;
> -		}
> -
> -		length = (reg >> 8) & 0xffff;
> -		if (length > xfer->rx_len) {
> -			dev_err(dev,
> -				"response too long (%u > %u bytes), stripping\n",
> -				xfer->rx_len, length);
> -			length = xfer->rx_len;
> -		} else if (length < xfer->rx_len)
> -			xfer->rx_len = length;
> -	}
> -
> -	length = xfer->rx_len - xfer->rx_done;
> -	xfer->rx_done += length;
> -
> -	/* Receive payload */
> -	while (length >= 4) {
> -		reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG);
> -		payload[0] = (reg >>  0) & 0xff;
> -		payload[1] = (reg >>  8) & 0xff;
> -		payload[2] = (reg >> 16) & 0xff;
> -		payload[3] = (reg >> 24) & 0xff;
> -		payload += 4;
> -		length -= 4;
> -	}
> -
> -	if (length) {
> -		reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG);
> -		switch (length) {
> -		case 3:
> -			payload[2] = (reg >> 16) & 0xff;
> -			fallthrough;
> -		case 2:
> -			payload[1] = (reg >> 8) & 0xff;
> -			fallthrough;
> -		case 1:
> -			payload[0] = reg & 0xff;
> -		}
> -	}
> -
> -	if (xfer->rx_done == xfer->rx_len)
> -		xfer->result = 0;
> -
> -clear_fifo:
> -	length = DSI_RX_FIFO_SIZE / 4;
> -	do {
> -		reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG);
> -		if (reg == DSI_RX_FIFO_EMPTY)
> -			break;
> -	} while (--length);
> -}
> -
> -static void exynos_dsi_transfer_start(struct exynos_dsi *dsi)
> -{
> -	unsigned long flags;
> -	struct exynos_dsi_transfer *xfer;
> -	bool start = false;
> -
> -again:
> -	spin_lock_irqsave(&dsi->transfer_lock, flags);
> -
> -	if (list_empty(&dsi->transfer_list)) {
> -		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> -		return;
> -	}
> -
> -	xfer = list_first_entry(&dsi->transfer_list,
> -					struct exynos_dsi_transfer, list);
> -
> -	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> -
> -	if (xfer->packet.payload_length &&
> -	    xfer->tx_done == xfer->packet.payload_length)
> -		/* waiting for RX */
> -		return;
> -
> -	exynos_dsi_send_to_fifo(dsi, xfer);
> -
> -	if (xfer->packet.payload_length || xfer->rx_len)
> -		return;
> -
> -	xfer->result = 0;
> -	complete(&xfer->completed);
> -
> -	spin_lock_irqsave(&dsi->transfer_lock, flags);
> -
> -	list_del_init(&xfer->list);
> -	start = !list_empty(&dsi->transfer_list);
> -
> -	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> -
> -	if (start)
> -		goto again;
> -}
> -
> -static bool exynos_dsi_transfer_finish(struct exynos_dsi *dsi)
> -{
> -	struct exynos_dsi_transfer *xfer;
> -	unsigned long flags;
> -	bool start = true;
> -
> -	spin_lock_irqsave(&dsi->transfer_lock, flags);
> -
> -	if (list_empty(&dsi->transfer_list)) {
> -		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> -		return false;
> -	}
> -
> -	xfer = list_first_entry(&dsi->transfer_list,
> -					struct exynos_dsi_transfer, list);
> -
> -	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> -
> -	dev_dbg(dsi->dev,
> -		"> xfer %pK, tx_len %zu, tx_done %u, rx_len %u, rx_done %u\n",
> -		xfer, xfer->packet.payload_length, xfer->tx_done, xfer->rx_len,
> -		xfer->rx_done);
> -
> -	if (xfer->tx_done != xfer->packet.payload_length)
> -		return true;
> -
> -	if (xfer->rx_done != xfer->rx_len)
> -		exynos_dsi_read_from_fifo(dsi, xfer);
> -
> -	if (xfer->rx_done != xfer->rx_len)
> -		return true;
> -
> -	spin_lock_irqsave(&dsi->transfer_lock, flags);
> -
> -	list_del_init(&xfer->list);
> -	start = !list_empty(&dsi->transfer_list);
> -
> -	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> -
> -	if (!xfer->rx_len)
> -		xfer->result = 0;
> -	complete(&xfer->completed);
> -
> -	return start;
> -}
> -
> -static void exynos_dsi_remove_transfer(struct exynos_dsi *dsi,
> -					struct exynos_dsi_transfer *xfer)
> -{
> -	unsigned long flags;
> -	bool start;
> -
> -	spin_lock_irqsave(&dsi->transfer_lock, flags);
> -
> -	if (!list_empty(&dsi->transfer_list) &&
> -	    xfer == list_first_entry(&dsi->transfer_list,
> -				     struct exynos_dsi_transfer, list)) {
> -		list_del_init(&xfer->list);
> -		start = !list_empty(&dsi->transfer_list);
> -		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> -		if (start)
> -			exynos_dsi_transfer_start(dsi);
> -		return;
> -	}
> -
> -	list_del_init(&xfer->list);
> -
> -	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> -}
> -
> -static int exynos_dsi_transfer(struct exynos_dsi *dsi,
> -					struct exynos_dsi_transfer *xfer)
> -{
> -	unsigned long flags;
> -	bool stopped;
> -
> -	xfer->tx_done = 0;
> -	xfer->rx_done = 0;
> -	xfer->result = -ETIMEDOUT;
> -	init_completion(&xfer->completed);
> -
> -	spin_lock_irqsave(&dsi->transfer_lock, flags);
> -
> -	stopped = list_empty(&dsi->transfer_list);
> -	list_add_tail(&xfer->list, &dsi->transfer_list);
> -
> -	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> -
> -	if (stopped)
> -		exynos_dsi_transfer_start(dsi);
> -
> -	wait_for_completion_timeout(&xfer->completed,
> -				    msecs_to_jiffies(DSI_XFER_TIMEOUT_MS));
> -	if (xfer->result == -ETIMEDOUT) {
> -		struct mipi_dsi_packet *pkt = &xfer->packet;
> -		exynos_dsi_remove_transfer(dsi, xfer);
> -		dev_err(dsi->dev, "xfer timed out: %*ph %*ph\n", 4, pkt->header,
> -			(int)pkt->payload_length, pkt->payload);
> -		return -ETIMEDOUT;
> -	}
> -
> -	/* Also covers hardware timeout condition */
> -	return xfer->result;
> -}
> -
> -static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
> -{
> -	struct exynos_dsi *dsi = dev_id;
> -	u32 status;
> -
> -	status = exynos_dsi_read(dsi, DSIM_INTSRC_REG);
> -	if (!status) {
> -		static unsigned long int j;
> -		if (printk_timed_ratelimit(&j, 500))
> -			dev_warn(dsi->dev, "spurious interrupt\n");
> -		return IRQ_HANDLED;
> -	}
> -	exynos_dsi_write(dsi, DSIM_INTSRC_REG, status);
> -
> -	if (status & DSIM_INT_SW_RST_RELEASE) {
> -		u32 mask = ~(DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY |
> -			DSIM_INT_SFR_HDR_FIFO_EMPTY | DSIM_INT_RX_ECC_ERR |
> -			DSIM_INT_SW_RST_RELEASE);
> -		exynos_dsi_write(dsi, DSIM_INTMSK_REG, mask);
> -		complete(&dsi->completed);
> -		return IRQ_HANDLED;
> -	}
> -
> -	if (!(status & (DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY |
> -			DSIM_INT_PLL_STABLE)))
> -		return IRQ_HANDLED;
> -
> -	if (exynos_dsi_transfer_finish(dsi))
> -		exynos_dsi_transfer_start(dsi);
> -
> -	return IRQ_HANDLED;
> -}
> -
> -static irqreturn_t exynos_dsi_te_irq_handler(int irq, void *dev_id)
> -{
> -	struct exynos_dsi *dsi = dev_id;
> -	const struct exynos_dsi_host_ops *ops = dsi->driver_data->host_ops;
> -
> -	if (ops && ops->te_handler &&
> -	    (dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
> -		ops->te_handler(dsi->dsi_host.dev);
> -
> -	return IRQ_HANDLED;
> -}
> -
> -static void exynos_dsi_enable_irq(struct exynos_dsi *dsi)
> -{
> -	enable_irq(dsi->irq);
> -
> -	if (gpio_is_valid(dsi->te_gpio))
> -		enable_irq(gpio_to_irq(dsi->te_gpio));
> -}
> -
> -static void exynos_dsi_disable_irq(struct exynos_dsi *dsi)
> -{
> -	if (gpio_is_valid(dsi->te_gpio))
> -		disable_irq(gpio_to_irq(dsi->te_gpio));
> -
> -	disable_irq(dsi->irq);
> -}
> -
> -static int exynos_dsi_init(struct exynos_dsi *dsi)
> +static int exynos_dsi_host_detach(struct device *dev,
> +				  struct mipi_dsi_device *device)
>  {
> -	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
> -
> -	exynos_dsi_reset(dsi);
> -	exynos_dsi_enable_irq(dsi);
> +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> +	struct drm_device *drm = dsi->encoder.dev;
>  
> -	if (driver_data->reg_values[RESET_TYPE] == DSIM_FUNCRST)
> -		exynos_dsi_enable_lane(dsi, BIT(dsi->lanes) - 1);
> -
> -	exynos_dsi_enable_clock(dsi);
> -	if (driver_data->wait_for_reset)
> -		exynos_dsi_wait_for_reset(dsi);
> -	exynos_dsi_set_phy_ctrl(dsi);
> -	exynos_dsi_init_link(dsi);
> +	if (drm->mode_config.poll_enabled)
> +		drm_kms_helper_hotplug_event(drm);
>  
>  	return 0;
>  }
>  
> -static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi,
> -				      struct device *panel)
> +static void exynos_dsi_te_handler(struct device *dev)
>  {
> -	int ret;
> -	int te_gpio_irq;
> +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
>  
> -	dsi->te_gpio = of_get_named_gpio(panel->of_node, "te-gpios", 0);
> -	if (dsi->te_gpio == -ENOENT)
> -		return 0;
> -
> -	if (!gpio_is_valid(dsi->te_gpio)) {
> -		ret = dsi->te_gpio;
> -		dev_err(dsi->dev, "cannot get te-gpios, %d\n", ret);
> -		goto out;
> -	}
> -
> -	ret = gpio_request(dsi->te_gpio, "te_gpio");
> -	if (ret) {
> -		dev_err(dsi->dev, "gpio request failed with %d\n", ret);
> -		goto out;
> -	}
> -
> -	te_gpio_irq = gpio_to_irq(dsi->te_gpio);
> -	irq_set_status_flags(te_gpio_irq, IRQ_NOAUTOEN);
> -
> -	ret = request_threaded_irq(te_gpio_irq, exynos_dsi_te_irq_handler, NULL,
> -					IRQF_TRIGGER_RISING, "TE", dsi);
> -	if (ret) {
> -		dev_err(dsi->dev, "request interrupt failed with %d\n", ret);
> -		gpio_free(dsi->te_gpio);
> -		goto out;
> -	}
> -
> -out:
> -	return ret;
> +	exynos_drm_crtc_te_handler(dsi->encoder.crtc);
>  }
>  
> -static void exynos_dsi_unregister_te_irq(struct exynos_dsi *dsi)
> -{
> -	if (gpio_is_valid(dsi->te_gpio)) {
> -		free_irq(gpio_to_irq(dsi->te_gpio), dsi);
> -		gpio_free(dsi->te_gpio);
> -		dsi->te_gpio = -ENOENT;
> -	}
> -}
> -
> -static void exynos_dsi_enable(struct exynos_dsi *dsi)
> -{
> -	struct drm_bridge *iter;
> -	int ret;
> -
> -	if (dsi->state & DSIM_STATE_ENABLED)
> -		return;
> -
> -	pm_runtime_get_sync(dsi->dev);
> -	dsi->state |= DSIM_STATE_ENABLED;
> -
> -	if (dsi->panel) {
> -		ret = drm_panel_prepare(dsi->panel);
> -		if (ret < 0)
> -			goto err_put_sync;
> -	} else {
> -		list_for_each_entry_reverse(iter, &dsi->bridge_chain,
> -					    chain_node) {
> -			if (iter->funcs->pre_enable)
> -				iter->funcs->pre_enable(iter);
> -		}
> -	}
> -
> -	exynos_dsi_set_display_mode(dsi);
> -	exynos_dsi_set_display_enable(dsi, true);
> -
> -	if (dsi->panel) {
> -		ret = drm_panel_enable(dsi->panel);
> -		if (ret < 0)
> -			goto err_display_disable;
> -	} else {
> -		list_for_each_entry(iter, &dsi->bridge_chain, chain_node) {
> -			if (iter->funcs->enable)
> -				iter->funcs->enable(iter);
> -		}
> -	}
> -
> -	dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
> -	return;
> -
> -err_display_disable:
> -	exynos_dsi_set_display_enable(dsi, false);
> -	drm_panel_unprepare(dsi->panel);
> -
> -err_put_sync:
> -	dsi->state &= ~DSIM_STATE_ENABLED;
> -	pm_runtime_put(dsi->dev);
> -}
> -
> -static void exynos_dsi_disable(struct exynos_dsi *dsi)
> -{
> -	struct drm_bridge *iter;
> -
> -	if (!(dsi->state & DSIM_STATE_ENABLED))
> -		return;
> -
> -	dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
> -
> -	drm_panel_disable(dsi->panel);
> -
> -	list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) {
> -		if (iter->funcs->disable)
> -			iter->funcs->disable(iter);
> -	}
> -
> -	exynos_dsi_set_display_enable(dsi, false);
> -	drm_panel_unprepare(dsi->panel);
> -
> -	list_for_each_entry(iter, &dsi->bridge_chain, chain_node) {
> -		if (iter->funcs->post_disable)
> -			iter->funcs->post_disable(iter);
> -	}
> -
> -	dsi->state &= ~DSIM_STATE_ENABLED;
> -	pm_runtime_put_sync(dsi->dev);
> -}
> -
> -static enum drm_connector_status
> -exynos_dsi_detect(struct drm_connector *connector, bool force)
> -{
> -	return connector->status;
> -}
> +static const struct samsung_dsim_host_ops exynos_dsi_host_ops = {
> +	.attach = exynos_dsi_host_attach,
> +	.detach = exynos_dsi_host_detach,
> +	.te_handler = exynos_dsi_te_handler,
> +};
>  
> -static void exynos_dsi_connector_destroy(struct drm_connector *connector)
> -{
> -	drm_connector_unregister(connector);
> -	drm_connector_cleanup(connector);
> -	connector->dev = NULL;
> -}
> +static const struct samsung_dsim_driver_data exynos3_dsi_driver_data = {
> +	.reg_ofs = EXYNOS_REG_OFS,
> +	.plltmr_reg = 0x50,
> +	.has_freqband = 1,
> +	.has_clklane_stop = 1,
> +	.num_clks = 2,
> +	.max_freq = 1000,
> +	.wait_for_reset = 1,
> +	.num_bits_resol = 11,
> +	.reg_values = reg_values,
> +	.host_ops = &exynos_dsi_host_ops,
> +};
>  
> -static const struct drm_connector_funcs exynos_dsi_connector_funcs = {
> -	.detect = exynos_dsi_detect,
> -	.fill_modes = drm_helper_probe_single_connector_modes,
> -	.destroy = exynos_dsi_connector_destroy,
> -	.reset = drm_atomic_helper_connector_reset,
> -	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> -	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +static const struct samsung_dsim_driver_data exynos4_dsi_driver_data = {
> +	.reg_ofs = EXYNOS_REG_OFS,
> +	.plltmr_reg = 0x50,
> +	.has_freqband = 1,
> +	.has_clklane_stop = 1,
> +	.num_clks = 2,
> +	.max_freq = 1000,
> +	.wait_for_reset = 1,
> +	.num_bits_resol = 11,
> +	.reg_values = reg_values,
> +	.host_ops = &exynos_dsi_host_ops,
>  };
>  
> -static int exynos_dsi_get_modes(struct drm_connector *connector)
> -{
> -	struct exynos_dsi *dsi = connector_to_dsi(connector);
> +static const struct samsung_dsim_driver_data exynos5_dsi_driver_data = {
> +	.reg_ofs = EXYNOS_REG_OFS,
> +	.plltmr_reg = 0x58,
> +	.num_clks = 2,
> +	.max_freq = 1000,
> +	.wait_for_reset = 1,
> +	.num_bits_resol = 11,
> +	.reg_values = reg_values,
> +	.host_ops = &exynos_dsi_host_ops,
> +};
>  
> -	if (dsi->panel)
> -		return drm_panel_get_modes(dsi->panel, connector);
> +static const struct samsung_dsim_driver_data exynos5433_dsi_driver_data = {
> +	.reg_ofs = EXYNOS5433_REG_OFS,
> +	.plltmr_reg = 0xa0,
> +	.has_clklane_stop = 1,
> +	.num_clks = 5,
> +	.max_freq = 1500,
> +	.wait_for_reset = 0,
> +	.num_bits_resol = 12,
> +	.reg_values = exynos5433_reg_values,
> +	.host_ops = &exynos_dsi_host_ops,
> +};
>  
> -	return 0;
> -}
> +static const struct samsung_dsim_driver_data exynos5422_dsi_driver_data = {
> +	.reg_ofs = EXYNOS5433_REG_OFS,
> +	.plltmr_reg = 0xa0,
> +	.has_clklane_stop = 1,
> +	.num_clks = 2,
> +	.max_freq = 1500,
> +	.wait_for_reset = 1,
> +	.num_bits_resol = 12,
> +	.reg_values = exynos5422_reg_values,
> +	.host_ops = &exynos_dsi_host_ops,
> +};
>  
> -static const struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = {
> -	.get_modes = exynos_dsi_get_modes,
> +static const struct of_device_id exynos_dsi_of_match[] = {
> +	{ .compatible = "samsung,exynos3250-mipi-dsi",
> +	  .data = &exynos3_dsi_driver_data },
> +	{ .compatible = "samsung,exynos4210-mipi-dsi",
> +	  .data = &exynos4_dsi_driver_data },
> +	{ .compatible = "samsung,exynos5410-mipi-dsi",
> +	  .data = &exynos5_dsi_driver_data },
> +	{ .compatible = "samsung,exynos5422-mipi-dsi",
> +	  .data = &exynos5422_dsi_driver_data },
> +	{ .compatible = "samsung,exynos5433-mipi-dsi",
> +	  .data = &exynos5433_dsi_driver_data },
> +	{ }
>  };
>  
> -static int exynos_dsi_create_connector(struct exynos_dsi *dsi)
> +static int exynos_dsi_bind(struct device *dev,
> +			   struct device *master, void *data)
>  {
> -	struct drm_connector *connector = &dsi->connector;
> -	struct drm_device *drm = dsi->bridge.dev;
> +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> +	struct drm_encoder *encoder = &dsi->encoder;
> +	struct drm_device *drm_dev = data;
> +	struct device_node *in_bridge_node;
> +	struct drm_bridge *in_bridge;
>  	int ret;
>  
> -	connector->polled = DRM_CONNECTOR_POLL_HPD;
> +	drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_TMDS);
>  
> -	ret = drm_connector_init(drm, connector, &exynos_dsi_connector_funcs,
> -				 DRM_MODE_CONNECTOR_DSI);
> -	if (ret) {
> -		DRM_DEV_ERROR(dsi->dev,
> -			      "Failed to initialize connector with drm\n");
> +	ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
> +	if (ret < 0)
>  		return ret;
> -	}
> -
> -	connector->status = connector_status_disconnected;
> -	drm_connector_helper_add(connector, &exynos_dsi_connector_helper_funcs);
> -	drm_connector_attach_encoder(connector, dsi->bridge.encoder);
> -	if (!drm->registered)
> -		return 0;
> -
> -	connector->funcs->reset(connector);
> -	drm_connector_register(connector);
> -	return 0;
> -}
> -
> -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(dsi);
> -		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);
> -		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);
> +	in_bridge_node = of_graph_get_remote_node(dev->of_node, DSI_PORT_IN, 0);
> +	if (in_bridge_node) {
> +		in_bridge = of_drm_find_bridge(in_bridge_node);
> +		if (in_bridge)
> +			drm_bridge_attach(encoder, in_bridge, NULL, 0);
> +		of_node_put(in_bridge_node);
>  	}
> -}
> -
> -static void exynos_dsi_bridge_enable(struct drm_bridge *bridge)
> -{
> -	struct exynos_dsi *dsi = bridge->driver_private;
> -
> -	exynos_dsi_enable(dsi);
> -}
> -
> -static void exynos_dsi_bridge_disable(struct drm_bridge *bridge)
> -{
> -	struct exynos_dsi *dsi = bridge->driver_private;
> -
> -	exynos_dsi_disable(dsi);
> -}
>  
> -static void exynos_dsi_bridge_mode_set(struct drm_bridge *bridge,
> -				       const struct drm_display_mode *mode,
> -				       const struct drm_display_mode *adjusted_mode)
> -{
> -	struct exynos_dsi *dsi = bridge->driver_private;
> -
> -	/* The mode is set when actually enabling the device. */
> -	drm_mode_copy(&dsi->mode, adjusted_mode);
> -}
> -
> -static const struct drm_bridge_funcs exynos_dsi_bridge_funcs = {
> -	.attach = exynos_dsi_bridge_attach,
> -	.detach = exynos_dsi_bridge_detach,
> -	.enable = exynos_dsi_bridge_enable,
> -	.disable = exynos_dsi_bridge_disable,
> -	.mode_set = exynos_dsi_bridge_mode_set,
> -};
> -
> -static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
> -				  struct mipi_dsi_device *device)
> -{
> -	struct exynos_dsi *dsi = host_to_dsi(host);
> -	const struct exynos_dsi_host_ops *ops = dsi->driver_data->host_ops;
> -	struct drm_bridge *out_bridge;
> -
> -	out_bridge = of_drm_find_bridge(device->dev.of_node);
> -	if (out_bridge) {
> -		dsi->out_bridge = out_bridge;
> -	} else {
> -		dsi->panel = of_drm_find_panel(device->dev.of_node);
> -		if (IS_ERR(dsi->panel))
> -			dsi->panel = NULL;
> -		else
> -			dsi->connector.status = connector_status_connected;
> -	}
> -
> -	/*
> -	 * This is a temporary solution and should be made by more generic way.
> -	 *
> -	 * If attached panel device is for command mode one, dsi should register
> -	 * TE interrupt handler.
> -	 */
> -	if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO)) {
> -		int ret = exynos_dsi_register_te_irq(dsi, &device->dev);
> -		if (ret)
> -			return ret;
> -	}
> -
> -	dsi->lanes = device->lanes;
> -	dsi->format = device->format;
> -	dsi->mode_flags = device->mode_flags;
> -
> -	if (ops && ops->attach)
> -		ops->attach(dsi->dsi_host.dev, device);
> +	ret = samsung_dsim_bind(dsi->dsi, encoder);
> +	if (ret)
> +		goto err;
>  
>  	return 0;
> -}
> -
> -static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
> -				  struct mipi_dsi_device *device)
> -{
> -	struct exynos_dsi *dsi = host_to_dsi(host);
> -	const struct exynos_dsi_host_ops *ops = dsi->driver_data->host_ops;
>  
> -	if (ops && ops->detach)
> -		ops->detach(dsi->dsi_host.dev, device);
> -
> -	exynos_dsi_unregister_te_irq(dsi);
> -
> -	return 0;
> +err:
> +	drm_encoder_cleanup(encoder);
> +	return ret;
>  }
>  
> -static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host,
> -					 const struct mipi_dsi_msg *msg)
> +static void exynos_dsi_unbind(struct device *dev,
> +			      struct device *master, void *data)
>  {
> -	struct exynos_dsi *dsi = host_to_dsi(host);
> -	struct exynos_dsi_transfer xfer;
> -	int ret;
> +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> +	struct drm_encoder *encoder = &dsi->encoder;
>  
> -	if (!(dsi->state & DSIM_STATE_ENABLED))
> -		return -EINVAL;
> -
> -	if (!(dsi->state & DSIM_STATE_INITIALIZED)) {
> -		ret = exynos_dsi_init(dsi);
> -		if (ret)
> -			return ret;
> -		dsi->state |= DSIM_STATE_INITIALIZED;
> -	}
> -
> -	ret = mipi_dsi_create_packet(&xfer.packet, msg);
> -	if (ret < 0)
> -		return ret;
> +	samsung_dsim_unbind(dsi->dsi);
>  
> -	xfer.rx_len = msg->rx_len;
> -	xfer.rx_payload = msg->rx_buf;
> -	xfer.flags = msg->flags;
> -
> -	ret = exynos_dsi_transfer(dsi, &xfer);
> -	return (ret < 0) ? ret : xfer.rx_done;
> +	drm_encoder_cleanup(encoder);
>  }
>  
> -static const struct mipi_dsi_host_ops exynos_dsi_ops = {
> -	.attach = exynos_dsi_host_attach,
> -	.detach = exynos_dsi_host_detach,
> -	.transfer = exynos_dsi_host_transfer,
> +static const struct component_ops exynos_dsi_component_ops = {
> +	.bind	= exynos_dsi_bind,
> +	.unbind	= exynos_dsi_unbind,
>  };
>  
> -static int exynos_dsi_of_read_u32(const struct device_node *np,
> -				  const char *propname, u32 *out_value)
> -{
> -	int ret = of_property_read_u32(np, propname, out_value);
> -
> -	if (ret < 0)
> -		pr_err("%pOF: failed to get '%s' property\n", np, propname);
> -
> -	return ret;
> -}
> -
> -static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
> -{
> -	struct device *dev = dsi->dev;
> -	struct device_node *node = dev->of_node;
> -	int ret;
> -
> -	ret = exynos_dsi_of_read_u32(node, "samsung,pll-clock-frequency",
> -				     &dsi->pll_clk_rate);
> -	if (ret < 0)
> -		return ret;
> -
> -	ret = exynos_dsi_of_read_u32(node, "samsung,burst-clock-frequency",
> -				     &dsi->burst_clk_rate);
> -	if (ret < 0)
> -		return ret;
> -
> -	ret = exynos_dsi_of_read_u32(node, "samsung,esc-clock-frequency",
> -				     &dsi->esc_clk_rate);
> -	if (ret < 0)
> -		return ret;
> -
> -	return 0;
> -}
> -
> -static struct exynos_dsi *__exynos_dsi_probe(struct platform_device *pdev)
> +static int 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;
> +	struct device *dev = &pdev->dev;
> +	int ret;
>  
>  	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
>  	if (!dsi)
> -		return ERR_PTR(-ENOMEM);
> +		return -ENOMEM;
> +	platform_set_drvdata(pdev, dsi);
>  
> -	/* To be checked as invalid one */
> -	dsi->te_gpio = -ENOENT;
> +	dsi->dsi = samsung_dsim_probe(pdev);
> +	if (IS_ERR(dsi->dsi))
> +		return PTR_ERR(dsi->dsi);
>  
> -	init_completion(&dsi->completed);
> -	spin_lock_init(&dsi->transfer_lock);
> -	INIT_LIST_HEAD(&dsi->transfer_list);
> -	INIT_LIST_HEAD(&dsi->bridge_chain);
> +	pm_runtime_enable(dev);
>  
> -	dsi->dsi_host.ops = &exynos_dsi_ops;
> -	dsi->dsi_host.dev = dev;
> -
> -	dsi->dev = dev;
> -	dsi->driver_data = of_device_get_match_data(dev);
> -
> -	dsi->supplies[0].supply = "vddcore";
> -	dsi->supplies[1].supply = "vddio";
> -	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(dsi->supplies),
> -				      dsi->supplies);
> -	if (ret) {
> -		if (ret != -EPROBE_DEFER)
> -			dev_info(dev, "failed to get regulators: %d\n", ret);
> -		return ERR_PTR(ret);
> -	}
> -
> -	dsi->clks = devm_kcalloc(dev,
> -			dsi->driver_data->num_clks, sizeof(*dsi->clks),
> -			GFP_KERNEL);
> -	if (!dsi->clks)
> -		return ERR_PTR(-ENOMEM);
> -
> -	for (i = 0; i < dsi->driver_data->num_clks; i++) {
> -		dsi->clks[i] = devm_clk_get(dev, clk_names[i]);
> -		if (IS_ERR(dsi->clks[i])) {
> -			if (strcmp(clk_names[i], "sclk_mipi") == 0) {
> -				dsi->clks[i] = devm_clk_get(dev,
> -							OLD_SCLK_MIPI_CLK_NAME);
> -				if (!IS_ERR(dsi->clks[i]))
> -					continue;
> -			}
> -
> -			dev_info(dev, "failed to get the clock: %s\n",
> -					clk_names[i]);
> -			return ERR_PTR(PTR_ERR(dsi->clks[i]));
> -		}
> -	}
> -
> -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> -	dsi->reg_base = devm_ioremap_resource(dev, res);
> -	if (IS_ERR(dsi->reg_base)) {
> -		dev_err(dev, "failed to remap io region\n");
> -		return dsi->reg_base;
> -	}
> -
> -	dsi->phy = devm_phy_get(dev, "dsim");
> -	if (IS_ERR(dsi->phy)) {
> -		dev_info(dev, "failed to get dsim phy\n");
> -		return ERR_PTR(PTR_ERR(dsi->phy));
> -	}
> -
> -	dsi->irq = platform_get_irq(pdev, 0);
> -	if (dsi->irq < 0)
> -		return ERR_PTR(dsi->irq);
> -
> -	irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
> -	ret = devm_request_threaded_irq(dev, dsi->irq, NULL,
> -					exynos_dsi_irq, IRQF_ONESHOT,
> -					dev_name(dev), dsi);
> -	if (ret) {
> -		dev_err(dev, "failed to request dsi irq\n");
> -		return ERR_PTR(ret);
> -	}
> -
> -	ret = exynos_dsi_parse_dt(dsi);
> +	ret = component_add(dev, &exynos_dsi_component_ops);
>  	if (ret)
> -		return ERR_PTR(ret);
> +		goto err_disable_runtime;
>  
> -	ret = mipi_dsi_host_register(&dsi->dsi_host);
> -	if (ret)
> -		return ERR_PTR(ret);
> +	return 0;
>  
> -	bridge = &dsi->bridge;
> -	bridge->driver_private = dsi;
> -	bridge->funcs = &exynos_dsi_bridge_funcs;
> -	bridge->of_node = dev->of_node;
> -	drm_bridge_add(bridge);
> +err_disable_runtime:
> +	pm_runtime_disable(dev);
>  
> -	return dsi;
> +	return ret;
>  }
>  
> -static void __exynos_dsi_remove(struct exynos_dsi *dsi)
> +static int exynos_dsi_remove(struct platform_device *pdev)
>  {
> -	drm_bridge_remove(&dsi->bridge);
> +	struct exynos_dsi *dsi = platform_get_drvdata(pdev);
>  
> -	mipi_dsi_host_unregister(&dsi->dsi_host);
> -}
> -
> -/*
> - * Probe/remove API, used from platforms based on the DRM bridge API.
> - */
> -struct exynos_dsi *exynos_dsi_probe(struct platform_device *pdev)
> -{
> -	return __exynos_dsi_probe(pdev);
> -}
> +	pm_runtime_disable(&pdev->dev);
>  
> -void exynos_dsi_remove(struct exynos_dsi *dsi)
> -{
> -	return __exynos_dsi_remove(dsi);
> -}
> +	samsung_dsim_remove(dsi->dsi);
>  
> -/*
> - * Bind/unbind API, used from platforms based on the component framework.
> - */
> -int exynos_dsi_bind(struct exynos_dsi *dsi, struct drm_encoder *encoder)
> -{
> -	struct drm_bridge *previous = drm_bridge_chain_get_first_bridge(encoder);
> +	component_del(&pdev->dev, &exynos_dsi_component_ops);
>  
> -	return drm_bridge_attach(encoder, &dsi->bridge, previous, 0);
> -}
> -
> -void exynos_dsi_unbind(struct exynos_dsi *dsi)
> -{
> -	exynos_dsi_disable(dsi);
> +	return 0;
>  }
>  
> -int exynos_dsi_suspend(struct exynos_dsi *dsi)
> +static int __maybe_unused exynos_dsi_suspend(struct device *dev)
>  {
> -	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
> -	int ret, i;
> -
> -	usleep_range(10000, 20000);
> -
> -	if (dsi->state & DSIM_STATE_INITIALIZED) {
> -		dsi->state &= ~DSIM_STATE_INITIALIZED;
> -
> -		exynos_dsi_disable_clock(dsi);
> -
> -		exynos_dsi_disable_irq(dsi);
> -	}
> -
> -	dsi->state &= ~DSIM_STATE_CMD_LPM;
> -
> -	phy_power_off(dsi->phy);
> -
> -	for (i = driver_data->num_clks - 1; i > -1; i--)
> -		clk_disable_unprepare(dsi->clks[i]);
> -
> -	ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
> -	if (ret < 0)
> -		dev_err(dsi->dev, "cannot disable regulators %d\n", ret);
> +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
>  
> -	return 0;
> +	return samsung_dsim_suspend(dsi->dsi);
>  }
>  
> -int exynos_dsi_resume(struct exynos_dsi *dsi)
> +static int __maybe_unused exynos_dsi_resume(struct device *dev)
>  {
> -	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
> -	int ret, i;
> -
> -	ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
> -	if (ret < 0) {
> -		dev_err(dsi->dev, "cannot enable regulators %d\n", ret);
> -		return ret;
> -	}
> -
> -	for (i = 0; i < driver_data->num_clks; i++) {
> -		ret = clk_prepare_enable(dsi->clks[i]);
> -		if (ret < 0)
> -			goto err_clk;
> -	}
> -
> -	ret = phy_power_on(dsi->phy);
> -	if (ret < 0) {
> -		dev_err(dsi->dev, "cannot enable phy %d\n", ret);
> -		goto err_clk;
> -	}
> +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
>  
> -	return 0;
> +	return samsung_dsim_resume(dsi->dsi);
> +}
>  
> -err_clk:
> -	while (--i > -1)
> -		clk_disable_unprepare(dsi->clks[i]);
> -	regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
> +static const struct dev_pm_ops exynos_dsi_pm_ops = {
> +	SET_RUNTIME_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume, NULL)
> +	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> +				pm_runtime_force_resume)
> +};
>  
> -	return ret;
> -}
> +struct platform_driver dsi_driver = {
> +	.probe = exynos_dsi_probe,
> +	.remove = exynos_dsi_remove,
> +	.driver = {
> +		   .name = "exynos-dsi",
> +		   .owner = THIS_MODULE,
> +		   .pm = &exynos_dsi_pm_ops,
> +		   .of_match_table = exynos_dsi_of_match,
> +	},
> +};
>  
>  MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
>  MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c b/drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c
> deleted file mode 100644
> index 79d9ec6ade45..000000000000
> --- a/drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c
> +++ /dev/null
> @@ -1,333 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0-only
> -/*
> - * Samsung SoC MIPI DSI Master driver.
> - *
> - * Copyright (c) 2014 Samsung Electronics Co., Ltd
> - *
> - * Contacts: Tomasz Figa <t.figa@samsung.com>
> - */
> -
> -#include <linux/component.h>
> -#include <linux/of_device.h>
> -#include <linux/of_graph.h>
> -#include <linux/pm_runtime.h>
> -
> -#include <drm/drm_bridge.h>
> -#include <drm/drm_encoder.h>
> -#include <drm/drm_mipi_dsi.h>
> -#include <drm/drm_probe_helper.h>
> -#include <drm/drm_simple_kms_helper.h>
> -
> -#include "exynos_drm_crtc.h"
> -#include "exynos_drm_drv.h"
> -#include "exynos_drm_dsi.h"
> -
> -enum {
> -	DSI_PORT_IN,
> -	DSI_PORT_OUT
> -};
> -
> -struct exynos_dsi_pltfm {
> -	struct exynos_dsi *dsi;
> -	struct drm_encoder encoder;
> -};
> -
> -static const unsigned int reg_values[] = {
> -	[RESET_TYPE] = DSIM_SWRST,
> -	[PLL_TIMER] = 500,
> -	[STOP_STATE_CNT] = 0xf,
> -	[PHYCTRL_ULPS_EXIT] = 0x0af,
> -	[PHYCTRL_VREG_LP] = 0,
> -	[PHYCTRL_SLEW_UP] = 0,
> -	[PHYTIMING_LPX] = 0x06,
> -	[PHYTIMING_HS_EXIT] = 0x0b,
> -	[PHYTIMING_CLK_PREPARE] = 0x07,
> -	[PHYTIMING_CLK_ZERO] = 0x27,
> -	[PHYTIMING_CLK_POST] = 0x0d,
> -	[PHYTIMING_CLK_TRAIL] = 0x08,
> -	[PHYTIMING_HS_PREPARE] = 0x09,
> -	[PHYTIMING_HS_ZERO] = 0x0d,
> -	[PHYTIMING_HS_TRAIL] = 0x0b,
> -};
> -
> -static const unsigned int exynos5422_reg_values[] = {
> -	[RESET_TYPE] = DSIM_SWRST,
> -	[PLL_TIMER] = 500,
> -	[STOP_STATE_CNT] = 0xf,
> -	[PHYCTRL_ULPS_EXIT] = 0xaf,
> -	[PHYCTRL_VREG_LP] = 0,
> -	[PHYCTRL_SLEW_UP] = 0,
> -	[PHYTIMING_LPX] = 0x08,
> -	[PHYTIMING_HS_EXIT] = 0x0d,
> -	[PHYTIMING_CLK_PREPARE] = 0x09,
> -	[PHYTIMING_CLK_ZERO] = 0x30,
> -	[PHYTIMING_CLK_POST] = 0x0e,
> -	[PHYTIMING_CLK_TRAIL] = 0x0a,
> -	[PHYTIMING_HS_PREPARE] = 0x0c,
> -	[PHYTIMING_HS_ZERO] = 0x11,
> -	[PHYTIMING_HS_TRAIL] = 0x0d,
> -};
> -
> -static const unsigned int exynos5433_reg_values[] = {
> -	[RESET_TYPE] = DSIM_FUNCRST,
> -	[PLL_TIMER] = 22200,
> -	[STOP_STATE_CNT] = 0xa,
> -	[PHYCTRL_ULPS_EXIT] = 0x190,
> -	[PHYCTRL_VREG_LP] = 1,
> -	[PHYCTRL_SLEW_UP] = 1,
> -	[PHYTIMING_LPX] = 0x07,
> -	[PHYTIMING_HS_EXIT] = 0x0c,
> -	[PHYTIMING_CLK_PREPARE] = 0x09,
> -	[PHYTIMING_CLK_ZERO] = 0x2d,
> -	[PHYTIMING_CLK_POST] = 0x0e,
> -	[PHYTIMING_CLK_TRAIL] = 0x09,
> -	[PHYTIMING_HS_PREPARE] = 0x0b,
> -	[PHYTIMING_HS_ZERO] = 0x10,
> -	[PHYTIMING_HS_TRAIL] = 0x0c,
> -};
> -
> -static int __exynos_dsi_host_attach(struct device *dev,
> -				    struct mipi_dsi_device *device)
> -{
> -	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
> -	struct drm_device *drm = dsi->encoder.dev;
> -	struct exynos_drm_crtc *crtc;
> -
> -	mutex_lock(&drm->mode_config.mutex);
> -	crtc = exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD);
> -	crtc->i80_mode = !(device->mode_flags & MIPI_DSI_MODE_VIDEO);
> -	mutex_unlock(&drm->mode_config.mutex);
> -
> -	if (drm->mode_config.poll_enabled)
> -		drm_kms_helper_hotplug_event(drm);
> -
> -	return 0;
> -}
> -
> -static int __exynos_dsi_host_detach(struct device *dev,
> -				    struct mipi_dsi_device *device)
> -{
> -	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
> -	struct drm_device *drm = dsi->encoder.dev;
> -
> -	if (drm->mode_config.poll_enabled)
> -		drm_kms_helper_hotplug_event(drm);
> -
> -	return 0;
> -}
> -
> -static void __exynos_dsi_te_handler(struct device *dev)
> -{
> -	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
> -
> -	exynos_drm_crtc_te_handler(dsi->encoder.crtc);
> -}
> -
> -static const struct exynos_dsi_host_ops exynos_dsi_host_ops = {
> -	.attach = __exynos_dsi_host_attach,
> -	.detach = __exynos_dsi_host_detach,
> -	.te_handler = __exynos_dsi_te_handler,
> -};
> -
> -static const struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
> -	.reg_ofs = EXYNOS_REG_OFS,
> -	.plltmr_reg = 0x50,
> -	.has_freqband = 1,
> -	.has_clklane_stop = 1,
> -	.num_clks = 2,
> -	.max_freq = 1000,
> -	.wait_for_reset = 1,
> -	.num_bits_resol = 11,
> -	.reg_values = reg_values,
> -	.host_ops = &exynos_dsi_host_ops,
> -};
> -
> -static const struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
> -	.reg_ofs = EXYNOS_REG_OFS,
> -	.plltmr_reg = 0x50,
> -	.has_freqband = 1,
> -	.has_clklane_stop = 1,
> -	.num_clks = 2,
> -	.max_freq = 1000,
> -	.wait_for_reset = 1,
> -	.num_bits_resol = 11,
> -	.reg_values = reg_values,
> -	.host_ops = &exynos_dsi_host_ops,
> -};
> -
> -static const struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
> -	.reg_ofs = EXYNOS_REG_OFS,
> -	.plltmr_reg = 0x58,
> -	.num_clks = 2,
> -	.max_freq = 1000,
> -	.wait_for_reset = 1,
> -	.num_bits_resol = 11,
> -	.reg_values = reg_values,
> -	.host_ops = &exynos_dsi_host_ops,
> -};
> -
> -static const struct exynos_dsi_driver_data exynos5433_dsi_driver_data = {
> -	.reg_ofs = EXYNOS5433_REG_OFS,
> -	.plltmr_reg = 0xa0,
> -	.has_clklane_stop = 1,
> -	.num_clks = 5,
> -	.max_freq = 1500,
> -	.wait_for_reset = 0,
> -	.num_bits_resol = 12,
> -	.reg_values = exynos5433_reg_values,
> -	.host_ops = &exynos_dsi_host_ops,
> -};
> -
> -static const struct exynos_dsi_driver_data exynos5422_dsi_driver_data = {
> -	.reg_ofs = EXYNOS5433_REG_OFS,
> -	.plltmr_reg = 0xa0,
> -	.has_clklane_stop = 1,
> -	.num_clks = 2,
> -	.max_freq = 1500,
> -	.wait_for_reset = 1,
> -	.num_bits_resol = 12,
> -	.reg_values = exynos5422_reg_values,
> -	.host_ops = &exynos_dsi_host_ops,
> -};
> -
> -static const struct of_device_id exynos_dsi_of_match[] = {
> -	{ .compatible = "samsung,exynos3250-mipi-dsi",
> -	  .data = &exynos3_dsi_driver_data },
> -	{ .compatible = "samsung,exynos4210-mipi-dsi",
> -	  .data = &exynos4_dsi_driver_data },
> -	{ .compatible = "samsung,exynos5410-mipi-dsi",
> -	  .data = &exynos5_dsi_driver_data },
> -	{ .compatible = "samsung,exynos5422-mipi-dsi",
> -	  .data = &exynos5422_dsi_driver_data },
> -	{ .compatible = "samsung,exynos5433-mipi-dsi",
> -	  .data = &exynos5433_dsi_driver_data },
> -	{ }
> -};
> -
> -static int exynos_dsi_pltfm_bind(struct device *dev, struct device *master, void *data)
> -{
> -	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
> -	struct drm_encoder *encoder = &dsi->encoder;
> -	struct drm_device *drm_dev = data;
> -	struct device_node *in_bridge_node;
> -	struct drm_bridge *in_bridge;
> -	int ret;
> -
> -	drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_TMDS);
> -
> -	ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
> -	if (ret < 0)
> -		return ret;
> -
> -	in_bridge_node = of_graph_get_remote_node(dev->of_node, DSI_PORT_IN, 0);
> -	if (in_bridge_node) {
> -		in_bridge = of_drm_find_bridge(in_bridge_node);
> -		if (in_bridge)
> -			drm_bridge_attach(encoder, in_bridge, NULL, 0);
> -		of_node_put(in_bridge_node);
> -	}
> -
> -	ret = exynos_dsi_bind(dsi->dsi, encoder);
> -	if (ret)
> -		goto err;
> -
> -	return 0;
> -
> -err:
> -	drm_encoder_cleanup(encoder);
> -	return ret;
> -}
> -
> -static void exynos_dsi_pltfm_unbind(struct device *dev, struct device *master,
> -				    void *data)
> -{
> -	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
> -	struct drm_encoder *encoder = &dsi->encoder;
> -
> -	exynos_dsi_unbind(dsi->dsi);
> -
> -	drm_encoder_cleanup(encoder);
> -}
> -
> -static const struct component_ops exynos_dsi_pltfm_component_ops = {
> -	.bind	= exynos_dsi_pltfm_bind,
> -	.unbind	= exynos_dsi_pltfm_unbind,
> -};
> -
> -static int exynos_dsi_pltfm_probe(struct platform_device *pdev)
> -{
> -	struct exynos_dsi_pltfm *dsi;
> -	struct device *dev = &pdev->dev;
> -	int ret;
> -
> -	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
> -	if (!dsi)
> -		return -ENOMEM;
> -	platform_set_drvdata(pdev, dsi);
> -
> -	dsi->dsi = exynos_dsi_probe(pdev);
> -	if (IS_ERR(dsi->dsi))
> -		return PTR_ERR(dsi->dsi);
> -
> -	pm_runtime_enable(dev);
> -
> -	ret = component_add(dev, &exynos_dsi_pltfm_component_ops);
> -	if (ret)
> -		goto err_disable_runtime;
> -
> -	return 0;
> -
> -err_disable_runtime:
> -	pm_runtime_disable(dev);
> -
> -	return ret;
> -}
> -
> -static int exynos_dsi_pltfm_remove(struct platform_device *pdev)
> -{
> -	struct exynos_dsi_pltfm *dsi = platform_get_drvdata(pdev);
> -
> -	pm_runtime_disable(&pdev->dev);
> -
> -	exynos_dsi_remove(dsi->dsi);
> -
> -	component_del(&pdev->dev, &exynos_dsi_pltfm_component_ops);
> -
> -	return 0;
> -}
> -
> -static int __maybe_unused exynos_dsi_pltfm_suspend(struct device *dev)
> -{
> -	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
> -
> -	return exynos_dsi_suspend(dsi->dsi);
> -}
> -
> -static int __maybe_unused exynos_dsi_pltfm_resume(struct device *dev)
> -{
> -	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
> -
> -	return exynos_dsi_resume(dsi->dsi);
> -}
> -
> -static const struct dev_pm_ops exynos_dsi_pm_ops = {
> -	SET_RUNTIME_PM_OPS(exynos_dsi_pltfm_suspend, exynos_dsi_pltfm_resume, NULL)
> -	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> -				pm_runtime_force_resume)
> -};
> -
> -struct platform_driver dsi_driver = {
> -	.probe = exynos_dsi_pltfm_probe,
> -	.remove = exynos_dsi_pltfm_remove,
> -	.driver = {
> -		   .name = "exynos-dsi",
> -		   .owner = THIS_MODULE,
> -		   .pm = &exynos_dsi_pm_ops,
> -		   .of_match_table = exynos_dsi_of_match,
> -	},
> -};
> -
> -MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
> -MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
> -MODULE_DESCRIPTION("Samsung SoC MIPI DSI Master");
> -MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.h b/include/drm/bridge/samsung-dsim.h
> similarity index 69%
> rename from drivers/gpu/drm/exynos/exynos_drm_dsi.h
> rename to include/drm/bridge/samsung-dsim.h
> index 8fa3276889de..be8b4913aa9c 100644
> --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.h
> +++ b/include/drm/bridge/samsung-dsim.h
> @@ -3,7 +3,7 @@
>  #define __EXYNOS_DRM_DSI__
>  
>  struct drm_encoder;
> -struct exynos_dsi;
> +struct samsung_dsim;
>  struct platform_device;
>  struct mipi_dsi_device;
>  
> @@ -12,13 +12,13 @@ enum exynos_reg_offset {
>  	EXYNOS5433_REG_OFS
>  };
>  
> -struct exynos_dsi *exynos_dsi_probe(struct platform_device *pdev);
> -void exynos_dsi_remove(struct exynos_dsi *dsi);
> -int exynos_dsi_bind(struct exynos_dsi *dsi, struct drm_encoder *encoder);
> -void exynos_dsi_unbind(struct exynos_dsi *dsi);
> +struct samsung_dsim *samsung_dsim_probe(struct platform_device *pdev);
> +void samsung_dsim_remove(struct samsung_dsim *dsi);
> +int samsung_dsim_bind(struct samsung_dsim *dsi, struct drm_encoder *encoder);
> +void samsung_dsim_unbind(struct samsung_dsim *dsi);
>  
> -int exynos_dsi_suspend(struct exynos_dsi *dsi);
> -int exynos_dsi_resume(struct exynos_dsi *dsi);
> +int samsung_dsim_suspend(struct samsung_dsim *dsi);
> +int samsung_dsim_resume(struct samsung_dsim *dsi);
>  
>  enum reg_value_idx {
>  	RESET_TYPE,
> @@ -42,13 +42,13 @@ enum reg_value_idx {
>  #define DSIM_FUNCRST			(1 << 16)
>  #define DSIM_SWRST			(1 << 0)
>  
> -struct exynos_dsi_host_ops {
> +struct samsung_dsim_host_ops {
>  	int (*attach)(struct device *dev, struct mipi_dsi_device *device);
>  	int (*detach)(struct device *dev, struct mipi_dsi_device *device);
>  	void (*te_handler)(struct device *dev);
>  };
>  
> -struct exynos_dsi_driver_data {
> +struct samsung_dsim_driver_data {
>  	enum exynos_reg_offset reg_ofs;
>  	unsigned int plltmr_reg;
>  	unsigned int has_freqband:1;
> @@ -58,7 +58,7 @@ struct exynos_dsi_driver_data {
>  	unsigned int wait_for_reset;
>  	unsigned int num_bits_resol;
>  	const unsigned int *reg_values;
> -	const struct exynos_dsi_host_ops *host_ops;
> +	const struct samsung_dsim_host_ops *host_ops;
>  };
>  
>  #endif /* __EXYNOS_DRM_DSI__ */
> -- 
> 2.20.1
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 16/16] drm/exynos: move bridge driver to bridges
  2020-09-16  7:58     ` Daniel Vetter
@ 2020-09-16  8:58       ` Michael Tretter
  2020-09-16  9:03         ` Daniel Vetter
  0 siblings, 1 reply; 61+ messages in thread
From: Michael Tretter @ 2020-09-16  8:58 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: linux-samsung-soc, jy0922.shim, narmstrong, b.zolnierkie,
	sw0312.kim, dri-devel, a.hajda, krzk, kernel, sylvester.nawrocki,
	Laurent.pinchart

On Wed, 16 Sep 2020 09:58:39 +0200, Daniel Vetter wrote:
> On Fri, Sep 11, 2020 at 03:54:13PM +0200, Michael Tretter wrote:
> > As the driver is not platform dependent anymore, move it to the drm
> > bridge driver directory.
> > 
> > Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
> 
> So new drm_bridge drivers that still use the component stuff is a bit
> uncool. We're trying to get away from that everywhere, bridges should be
> abstracted enough that just going through the of lookup functions to get
> at your bridge should work.
> 
> Is there anything here that prevents this, or could this be included?

The Exynos drm driver uses the component framework. Maybe I can avoid exposing
the bind/unbind API in the drm_bridge driver by implementing the bind/unbind
in the platform specific part. However, completely getting rid of the
component framework for this drm_bridge is not possible without changing the
entire Exynos drm driver.

Michael

> -Daniel
> 
> > ---
> > v2:
> > - select DRM_SAMSUNG_DSIM from DRM_EXYNOS_DSI
> > - add removal of depends on !FB_S3C
> > ---
> >  drivers/gpu/drm/bridge/Kconfig                |    9 +
> >  drivers/gpu/drm/bridge/Makefile               |    1 +
> >  drivers/gpu/drm/bridge/samsung-dsim.c         | 1790 ++++++++++++++++
> >  drivers/gpu/drm/exynos/Kconfig                |    5 +-
> >  drivers/gpu/drm/exynos/Makefile               |    2 +-
> >  drivers/gpu/drm/exynos/exynos_drm_dsi.c       | 1896 ++---------------
> >  drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c |  333 ---
> >  .../drm/bridge/samsung-dsim.h                 |   20 +-
> >  8 files changed, 2037 insertions(+), 2019 deletions(-)
> >  create mode 100644 drivers/gpu/drm/bridge/samsung-dsim.c
> >  delete mode 100644 drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c
> >  rename drivers/gpu/drm/exynos/exynos_drm_dsi.h => include/drm/bridge/samsung-dsim.h (69%)
> > 
> > diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> > index 3e11af4e9f63..55ab5030c6cf 100644
> > --- a/drivers/gpu/drm/bridge/Kconfig
> > +++ b/drivers/gpu/drm/bridge/Kconfig
> > @@ -125,6 +125,15 @@ config DRM_PARADE_PS8640
> >  	  The PS8640 is a high-performance and low-power
> >  	  MIPI DSI to eDP converter
> >  
> > +config DRM_SAMSUNG_DSIM
> > +	tristate "Samsung MIPI DSI bridge"
> > +	depends on OF
> > +	select DRM_KMS_HELPER
> > +	select DRM_MIPI_DSI
> > +	select DRM_PANEL
> > +	help
> > +	  Samsung MIPI DSI bridge driver.
> > +
> >  config DRM_SIL_SII8620
> >  	tristate "Silicon Image SII8620 HDMI/MHL bridge"
> >  	depends on OF
> > diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> > index c589a6a7cbe1..5ac7a5c413dc 100644
> > --- a/drivers/gpu/drm/bridge/Makefile
> > +++ b/drivers/gpu/drm/bridge/Makefile
> > @@ -8,6 +8,7 @@ obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v
> >  obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
> >  obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
> >  obj-$(CONFIG_DRM_PARADE_PS8640) += parade-ps8640.o
> > +obj-$(CONFIG_DRM_SAMSUNG_DSIM) += samsung-dsim.o
> >  obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o
> >  obj-$(CONFIG_DRM_SII902X) += sii902x.o
> >  obj-$(CONFIG_DRM_SII9234) += sii9234.o
> > diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
> > new file mode 100644
> > index 000000000000..6d2d8dc027de
> > --- /dev/null
> > +++ b/drivers/gpu/drm/bridge/samsung-dsim.c
> > @@ -0,0 +1,1790 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Samsung SoC MIPI DSI Master driver.
> > + *
> > + * Copyright (c) 2014 Samsung Electronics Co., Ltd
> > + *
> > + * Contacts: Tomasz Figa <t.figa@samsung.com>
> > +*/
> > +
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/component.h>
> > +#include <linux/gpio/consumer.h>
> > +#include <linux/irq.h>
> > +#include <linux/of_device.h>
> > +#include <linux/of_gpio.h>
> > +#include <linux/of_graph.h>
> > +#include <linux/phy/phy.h>
> > +#include <linux/regulator/consumer.h>
> > +
> > +#include <asm/unaligned.h>
> > +
> > +#include <video/mipi_display.h>
> > +#include <video/videomode.h>
> > +
> > +#include <drm/bridge/samsung-dsim.h>
> > +#include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_bridge.h>
> > +#include <drm/drm_fb_helper.h>
> > +#include <drm/drm_mipi_dsi.h>
> > +#include <drm/drm_panel.h>
> > +#include <drm/drm_print.h>
> > +#include <drm/drm_probe_helper.h>
> > +#include <drm/drm_simple_kms_helper.h>
> > +
> > +/* returns true iff both arguments logically differs */
> > +#define NEQV(a, b) (!(a) ^ !(b))
> > +
> > +/* DSIM_STATUS */
> > +#define DSIM_STOP_STATE_DAT(x)		(((x) & 0xf) << 0)
> > +#define DSIM_STOP_STATE_CLK		(1 << 8)
> > +#define DSIM_TX_READY_HS_CLK		(1 << 10)
> > +#define DSIM_PLL_STABLE			(1 << 31)
> > +
> > +/* DSIM_TIMEOUT */
> > +#define DSIM_LPDR_TIMEOUT(x)		((x) << 0)
> > +#define DSIM_BTA_TIMEOUT(x)		((x) << 16)
> > +
> > +/* DSIM_CLKCTRL */
> > +#define DSIM_ESC_PRESCALER(x)		(((x) & 0xffff) << 0)
> > +#define DSIM_ESC_PRESCALER_MASK		(0xffff << 0)
> > +#define DSIM_LANE_ESC_CLK_EN_CLK	(1 << 19)
> > +#define DSIM_LANE_ESC_CLK_EN_DATA(x)	(((x) & 0xf) << 20)
> > +#define DSIM_LANE_ESC_CLK_EN_DATA_MASK	(0xf << 20)
> > +#define DSIM_BYTE_CLKEN			(1 << 24)
> > +#define DSIM_BYTE_CLK_SRC(x)		(((x) & 0x3) << 25)
> > +#define DSIM_BYTE_CLK_SRC_MASK		(0x3 << 25)
> > +#define DSIM_PLL_BYPASS			(1 << 27)
> > +#define DSIM_ESC_CLKEN			(1 << 28)
> > +#define DSIM_TX_REQUEST_HSCLK		(1 << 31)
> > +
> > +/* DSIM_CONFIG */
> > +#define DSIM_LANE_EN_CLK		(1 << 0)
> > +#define DSIM_LANE_EN(x)			(((x) & 0xf) << 1)
> > +#define DSIM_NUM_OF_DATA_LANE(x)	(((x) & 0x3) << 5)
> > +#define DSIM_SUB_PIX_FORMAT(x)		(((x) & 0x7) << 8)
> > +#define DSIM_MAIN_PIX_FORMAT_MASK	(0x7 << 12)
> > +#define DSIM_MAIN_PIX_FORMAT_RGB888	(0x7 << 12)
> > +#define DSIM_MAIN_PIX_FORMAT_RGB666	(0x6 << 12)
> > +#define DSIM_MAIN_PIX_FORMAT_RGB666_P	(0x5 << 12)
> > +#define DSIM_MAIN_PIX_FORMAT_RGB565	(0x4 << 12)
> > +#define DSIM_SUB_VC			(((x) & 0x3) << 16)
> > +#define DSIM_MAIN_VC			(((x) & 0x3) << 18)
> > +#define DSIM_HSA_MODE			(1 << 20)
> > +#define DSIM_HBP_MODE			(1 << 21)
> > +#define DSIM_HFP_MODE			(1 << 22)
> > +#define DSIM_HSE_MODE			(1 << 23)
> > +#define DSIM_AUTO_MODE			(1 << 24)
> > +#define DSIM_VIDEO_MODE			(1 << 25)
> > +#define DSIM_BURST_MODE			(1 << 26)
> > +#define DSIM_SYNC_INFORM		(1 << 27)
> > +#define DSIM_EOT_DISABLE		(1 << 28)
> > +#define DSIM_MFLUSH_VS			(1 << 29)
> > +/* This flag is valid only for exynos3250/3472/5260/5430 */
> > +#define DSIM_CLKLANE_STOP		(1 << 30)
> > +
> > +/* DSIM_ESCMODE */
> > +#define DSIM_TX_TRIGGER_RST		(1 << 4)
> > +#define DSIM_TX_LPDT_LP			(1 << 6)
> > +#define DSIM_CMD_LPDT_LP		(1 << 7)
> > +#define DSIM_FORCE_BTA			(1 << 16)
> > +#define DSIM_FORCE_STOP_STATE		(1 << 20)
> > +#define DSIM_STOP_STATE_CNT(x)		(((x) & 0x7ff) << 21)
> > +#define DSIM_STOP_STATE_CNT_MASK	(0x7ff << 21)
> > +
> > +/* DSIM_MDRESOL */
> > +#define DSIM_MAIN_STAND_BY		(1 << 31)
> > +#define DSIM_MAIN_VRESOL(x, num_bits)	(((x) & ((1 << (num_bits)) - 1)) << 16)
> > +#define DSIM_MAIN_HRESOL(x, num_bits)	(((x) & ((1 << (num_bits)) - 1)) << 0)
> > +
> > +/* DSIM_MVPORCH */
> > +#define DSIM_CMD_ALLOW(x)		((x) << 28)
> > +#define DSIM_STABLE_VFP(x)		((x) << 16)
> > +#define DSIM_MAIN_VBP(x)		((x) << 0)
> > +#define DSIM_CMD_ALLOW_MASK		(0xf << 28)
> > +#define DSIM_STABLE_VFP_MASK		(0x7ff << 16)
> > +#define DSIM_MAIN_VBP_MASK		(0x7ff << 0)
> > +
> > +/* DSIM_MHPORCH */
> > +#define DSIM_MAIN_HFP(x)		((x) << 16)
> > +#define DSIM_MAIN_HBP(x)		((x) << 0)
> > +#define DSIM_MAIN_HFP_MASK		((0xffff) << 16)
> > +#define DSIM_MAIN_HBP_MASK		((0xffff) << 0)
> > +
> > +/* DSIM_MSYNC */
> > +#define DSIM_MAIN_VSA(x)		((x) << 22)
> > +#define DSIM_MAIN_HSA(x)		((x) << 0)
> > +#define DSIM_MAIN_VSA_MASK		((0x3ff) << 22)
> > +#define DSIM_MAIN_HSA_MASK		((0xffff) << 0)
> > +
> > +/* DSIM_SDRESOL */
> > +#define DSIM_SUB_STANDY(x)		((x) << 31)
> > +#define DSIM_SUB_VRESOL(x)		((x) << 16)
> > +#define DSIM_SUB_HRESOL(x)		((x) << 0)
> > +#define DSIM_SUB_STANDY_MASK		((0x1) << 31)
> > +#define DSIM_SUB_VRESOL_MASK		((0x7ff) << 16)
> > +#define DSIM_SUB_HRESOL_MASK		((0x7ff) << 0)
> > +
> > +/* DSIM_INTSRC */
> > +#define DSIM_INT_PLL_STABLE		(1 << 31)
> > +#define DSIM_INT_SW_RST_RELEASE		(1 << 30)
> > +#define DSIM_INT_SFR_FIFO_EMPTY		(1 << 29)
> > +#define DSIM_INT_SFR_HDR_FIFO_EMPTY	(1 << 28)
> > +#define DSIM_INT_BTA			(1 << 25)
> > +#define DSIM_INT_FRAME_DONE		(1 << 24)
> > +#define DSIM_INT_RX_TIMEOUT		(1 << 21)
> > +#define DSIM_INT_BTA_TIMEOUT		(1 << 20)
> > +#define DSIM_INT_RX_DONE		(1 << 18)
> > +#define DSIM_INT_RX_TE			(1 << 17)
> > +#define DSIM_INT_RX_ACK			(1 << 16)
> > +#define DSIM_INT_RX_ECC_ERR		(1 << 15)
> > +#define DSIM_INT_RX_CRC_ERR		(1 << 14)
> > +
> > +/* DSIM_FIFOCTRL */
> > +#define DSIM_RX_DATA_FULL		(1 << 25)
> > +#define DSIM_RX_DATA_EMPTY		(1 << 24)
> > +#define DSIM_SFR_HEADER_FULL		(1 << 23)
> > +#define DSIM_SFR_HEADER_EMPTY		(1 << 22)
> > +#define DSIM_SFR_PAYLOAD_FULL		(1 << 21)
> > +#define DSIM_SFR_PAYLOAD_EMPTY		(1 << 20)
> > +#define DSIM_I80_HEADER_FULL		(1 << 19)
> > +#define DSIM_I80_HEADER_EMPTY		(1 << 18)
> > +#define DSIM_I80_PAYLOAD_FULL		(1 << 17)
> > +#define DSIM_I80_PAYLOAD_EMPTY		(1 << 16)
> > +#define DSIM_SD_HEADER_FULL		(1 << 15)
> > +#define DSIM_SD_HEADER_EMPTY		(1 << 14)
> > +#define DSIM_SD_PAYLOAD_FULL		(1 << 13)
> > +#define DSIM_SD_PAYLOAD_EMPTY		(1 << 12)
> > +#define DSIM_MD_HEADER_FULL		(1 << 11)
> > +#define DSIM_MD_HEADER_EMPTY		(1 << 10)
> > +#define DSIM_MD_PAYLOAD_FULL		(1 << 9)
> > +#define DSIM_MD_PAYLOAD_EMPTY		(1 << 8)
> > +#define DSIM_RX_FIFO			(1 << 4)
> > +#define DSIM_SFR_FIFO			(1 << 3)
> > +#define DSIM_I80_FIFO			(1 << 2)
> > +#define DSIM_SD_FIFO			(1 << 1)
> > +#define DSIM_MD_FIFO			(1 << 0)
> > +
> > +/* DSIM_PHYACCHR */
> > +#define DSIM_AFC_EN			(1 << 14)
> > +#define DSIM_AFC_CTL(x)			(((x) & 0x7) << 5)
> > +
> > +/* DSIM_PLLCTRL */
> > +#define DSIM_FREQ_BAND(x)		((x) << 24)
> > +#define DSIM_PLL_EN			(1 << 23)
> > +#define DSIM_PLL_P(x)			((x) << 13)
> > +#define DSIM_PLL_M(x)			((x) << 4)
> > +#define DSIM_PLL_S(x)			((x) << 1)
> > +
> > +/* DSIM_PHYCTRL */
> > +#define DSIM_PHYCTRL_ULPS_EXIT(x)	(((x) & 0x1ff) << 0)
> > +#define DSIM_PHYCTRL_B_DPHYCTL_VREG_LP	(1 << 30)
> > +#define DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP	(1 << 14)
> > +
> > +/* DSIM_PHYTIMING */
> > +#define DSIM_PHYTIMING_LPX(x)		((x) << 8)
> > +#define DSIM_PHYTIMING_HS_EXIT(x)	((x) << 0)
> > +
> > +/* DSIM_PHYTIMING1 */
> > +#define DSIM_PHYTIMING1_CLK_PREPARE(x)	((x) << 24)
> > +#define DSIM_PHYTIMING1_CLK_ZERO(x)	((x) << 16)
> > +#define DSIM_PHYTIMING1_CLK_POST(x)	((x) << 8)
> > +#define DSIM_PHYTIMING1_CLK_TRAIL(x)	((x) << 0)
> > +
> > +/* DSIM_PHYTIMING2 */
> > +#define DSIM_PHYTIMING2_HS_PREPARE(x)	((x) << 16)
> > +#define DSIM_PHYTIMING2_HS_ZERO(x)	((x) << 8)
> > +#define DSIM_PHYTIMING2_HS_TRAIL(x)	((x) << 0)
> > +
> > +#define DSI_MAX_BUS_WIDTH		4
> > +#define DSI_NUM_VIRTUAL_CHANNELS	4
> > +#define DSI_TX_FIFO_SIZE		2048
> > +#define DSI_RX_FIFO_SIZE		256
> > +#define DSI_XFER_TIMEOUT_MS		100
> > +#define DSI_RX_FIFO_EMPTY		0x30800002
> > +
> > +#define OLD_SCLK_MIPI_CLK_NAME "pll_clk"
> > +
> > +static const char *const clk_names[5] = { "bus_clk", "sclk_mipi",
> > +	"phyclk_mipidphy0_bitclkdiv8", "phyclk_mipidphy0_rxclkesc0",
> > +	"sclk_rgb_vclk_to_dsim0" };
> > +
> > +enum samsung_dsim_transfer_type {
> > +	EXYNOS_DSI_TX,
> > +	EXYNOS_DSI_RX,
> > +};
> > +
> > +struct samsung_dsim_transfer {
> > +	struct list_head list;
> > +	struct completion completed;
> > +	int result;
> > +	struct mipi_dsi_packet packet;
> > +	u16 flags;
> > +	u16 tx_done;
> > +
> > +	u8 *rx_payload;
> > +	u16 rx_len;
> > +	u16 rx_done;
> > +};
> > +
> > +#define DSIM_STATE_ENABLED		BIT(0)
> > +#define DSIM_STATE_INITIALIZED		BIT(1)
> > +#define DSIM_STATE_CMD_LPM		BIT(2)
> > +#define DSIM_STATE_VIDOUT_AVAILABLE	BIT(3)
> > +
> > +struct samsung_dsim {
> > +	struct drm_bridge bridge;
> > +	struct mipi_dsi_host dsi_host;
> > +	struct drm_connector connector;
> > +	struct drm_panel *panel;
> > +	struct list_head bridge_chain;
> > +	struct drm_bridge *out_bridge;
> > +	struct device *dev;
> > +
> > +	void __iomem *reg_base;
> > +	struct phy *phy;
> > +	struct clk **clks;
> > +	struct regulator_bulk_data supplies[2];
> > +	int irq;
> > +	int te_gpio;
> > +
> > +	u32 pll_clk_rate;
> > +	u32 burst_clk_rate;
> > +	u32 esc_clk_rate;
> > +	u32 lanes;
> > +	u32 mode_flags;
> > +	u32 format;
> > +
> > +	struct drm_display_mode mode;
> > +
> > +	int state;
> > +	struct drm_property *brightness;
> > +	struct completion completed;
> > +
> > +	spinlock_t transfer_lock; /* protects transfer_list */
> > +	struct list_head transfer_list;
> > +
> > +	const struct samsung_dsim_driver_data *driver_data;
> > +};
> > +
> > +#define host_to_dsi(host) container_of(host, struct samsung_dsim, dsi_host)
> > +#define connector_to_dsi(c) container_of(c, struct samsung_dsim, connector)
> > +
> > +enum reg_idx {
> > +	DSIM_STATUS_REG,	/* Status register */
> > +	DSIM_SWRST_REG,		/* Software reset register */
> > +	DSIM_CLKCTRL_REG,	/* Clock control register */
> > +	DSIM_TIMEOUT_REG,	/* Time out register */
> > +	DSIM_CONFIG_REG,	/* Configuration register */
> > +	DSIM_ESCMODE_REG,	/* Escape mode register */
> > +	DSIM_MDRESOL_REG,
> > +	DSIM_MVPORCH_REG,	/* Main display Vporch register */
> > +	DSIM_MHPORCH_REG,	/* Main display Hporch register */
> > +	DSIM_MSYNC_REG,		/* Main display sync area register */
> > +	DSIM_INTSRC_REG,	/* Interrupt source register */
> > +	DSIM_INTMSK_REG,	/* Interrupt mask register */
> > +	DSIM_PKTHDR_REG,	/* Packet Header FIFO register */
> > +	DSIM_PAYLOAD_REG,	/* Payload FIFO register */
> > +	DSIM_RXFIFO_REG,	/* Read FIFO register */
> > +	DSIM_FIFOCTRL_REG,	/* FIFO status and control register */
> > +	DSIM_PLLCTRL_REG,	/* PLL control register */
> > +	DSIM_PHYCTRL_REG,
> > +	DSIM_PHYTIMING_REG,
> > +	DSIM_PHYTIMING1_REG,
> > +	DSIM_PHYTIMING2_REG,
> > +	NUM_REGS
> > +};
> > +
> > +static const unsigned int exynos_reg_ofs[] = {
> > +	[DSIM_STATUS_REG] =  0x00,
> > +	[DSIM_SWRST_REG] =  0x04,
> > +	[DSIM_CLKCTRL_REG] =  0x08,
> > +	[DSIM_TIMEOUT_REG] =  0x0c,
> > +	[DSIM_CONFIG_REG] =  0x10,
> > +	[DSIM_ESCMODE_REG] =  0x14,
> > +	[DSIM_MDRESOL_REG] =  0x18,
> > +	[DSIM_MVPORCH_REG] =  0x1c,
> > +	[DSIM_MHPORCH_REG] =  0x20,
> > +	[DSIM_MSYNC_REG] =  0x24,
> > +	[DSIM_INTSRC_REG] =  0x2c,
> > +	[DSIM_INTMSK_REG] =  0x30,
> > +	[DSIM_PKTHDR_REG] =  0x34,
> > +	[DSIM_PAYLOAD_REG] =  0x38,
> > +	[DSIM_RXFIFO_REG] =  0x3c,
> > +	[DSIM_FIFOCTRL_REG] =  0x44,
> > +	[DSIM_PLLCTRL_REG] =  0x4c,
> > +	[DSIM_PHYCTRL_REG] =  0x5c,
> > +	[DSIM_PHYTIMING_REG] =  0x64,
> > +	[DSIM_PHYTIMING1_REG] =  0x68,
> > +	[DSIM_PHYTIMING2_REG] =  0x6c,
> > +};
> > +
> > +static const unsigned int exynos5433_reg_ofs[] = {
> > +	[DSIM_STATUS_REG] = 0x04,
> > +	[DSIM_SWRST_REG] = 0x0C,
> > +	[DSIM_CLKCTRL_REG] = 0x10,
> > +	[DSIM_TIMEOUT_REG] = 0x14,
> > +	[DSIM_CONFIG_REG] = 0x18,
> > +	[DSIM_ESCMODE_REG] = 0x1C,
> > +	[DSIM_MDRESOL_REG] = 0x20,
> > +	[DSIM_MVPORCH_REG] = 0x24,
> > +	[DSIM_MHPORCH_REG] = 0x28,
> > +	[DSIM_MSYNC_REG] = 0x2C,
> > +	[DSIM_INTSRC_REG] = 0x34,
> > +	[DSIM_INTMSK_REG] = 0x38,
> > +	[DSIM_PKTHDR_REG] = 0x3C,
> > +	[DSIM_PAYLOAD_REG] = 0x40,
> > +	[DSIM_RXFIFO_REG] = 0x44,
> > +	[DSIM_FIFOCTRL_REG] = 0x4C,
> > +	[DSIM_PLLCTRL_REG] = 0x94,
> > +	[DSIM_PHYCTRL_REG] = 0xA4,
> > +	[DSIM_PHYTIMING_REG] = 0xB4,
> > +	[DSIM_PHYTIMING1_REG] = 0xB8,
> > +	[DSIM_PHYTIMING2_REG] = 0xBC,
> > +};
> > +
> > +static inline void samsung_dsim_write(struct samsung_dsim *dsi,
> > +				      enum reg_idx idx, u32 val)
> > +{
> > +	const unsigned int *reg_ofs;
> > +
> > +	if (dsi->driver_data->reg_ofs == EXYNOS5433_REG_OFS)
> > +		reg_ofs = exynos5433_reg_ofs;
> > +	else
> > +		reg_ofs = exynos_reg_ofs;
> > +
> > +	writel(val, dsi->reg_base + reg_ofs[idx]);
> > +}
> > +
> > +static inline u32 samsung_dsim_read(struct samsung_dsim *dsi, enum reg_idx idx)
> > +{
> > +	const unsigned int *reg_ofs;
> > +
> > +	if (dsi->driver_data->reg_ofs == EXYNOS5433_REG_OFS)
> > +		reg_ofs = exynos5433_reg_ofs;
> > +	else
> > +		reg_ofs = exynos_reg_ofs;
> > +
> > +	return readl(dsi->reg_base + reg_ofs[idx]);
> > +}
> > +
> > +static void samsung_dsim_wait_for_reset(struct samsung_dsim *dsi)
> > +{
> > +	if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300)))
> > +		return;
> > +
> > +	dev_err(dsi->dev, "timeout waiting for reset\n");
> > +}
> > +
> > +static void samsung_dsim_reset(struct samsung_dsim *dsi)
> > +{
> > +	u32 reset_val = dsi->driver_data->reg_values[RESET_TYPE];
> > +
> > +	reinit_completion(&dsi->completed);
> > +	samsung_dsim_write(dsi, DSIM_SWRST_REG, reset_val);
> > +}
> > +
> > +#ifndef MHZ
> > +#define MHZ	(1000*1000)
> > +#endif
> > +
> > +static unsigned long samsung_dsim_pll_find_pms(struct samsung_dsim *dsi,
> > +					       unsigned long fin,
> > +					       unsigned long fout,
> > +					       u8 *p, u16 *m, u8 *s)
> > +{
> > +	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
> > +	unsigned long best_freq = 0;
> > +	u32 min_delta = 0xffffffff;
> > +	u8 p_min, p_max;
> > +	u8 _p, best_p;
> > +	u16 _m, best_m;
> > +	u8 _s, best_s;
> > +
> > +	p_min = DIV_ROUND_UP(fin, (12 * MHZ));
> > +	p_max = fin / (6 * MHZ);
> > +
> > +	for (_p = p_min; _p <= p_max; ++_p) {
> > +		for (_s = 0; _s <= 5; ++_s) {
> > +			u64 tmp;
> > +			u32 delta;
> > +
> > +			tmp = (u64)fout * (_p << _s);
> > +			do_div(tmp, fin);
> > +			_m = tmp;
> > +			if (_m < 41 || _m > 125)
> > +				continue;
> > +
> > +			tmp = (u64)_m * fin;
> > +			do_div(tmp, _p);
> > +			if (tmp < 500 * MHZ ||
> > +					tmp > driver_data->max_freq * MHZ)
> > +				continue;
> > +
> > +			tmp = (u64)_m * fin;
> > +			do_div(tmp, _p << _s);
> > +
> > +			delta = abs(fout - tmp);
> > +			if (delta < min_delta) {
> > +				best_p = _p;
> > +				best_m = _m;
> > +				best_s = _s;
> > +				min_delta = delta;
> > +				best_freq = tmp;
> > +			}
> > +		}
> > +	}
> > +
> > +	if (best_freq) {
> > +		*p = best_p;
> > +		*m = best_m;
> > +		*s = best_s;
> > +	}
> > +
> > +	return best_freq;
> > +}
> > +
> > +static unsigned long samsung_dsim_set_pll(struct samsung_dsim *dsi,
> > +					  unsigned long freq)
> > +{
> > +	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
> > +	unsigned long fin, fout;
> > +	int timeout;
> > +	u8 p, s;
> > +	u16 m;
> > +	u32 reg;
> > +
> > +	fin = dsi->pll_clk_rate;
> > +	fout = samsung_dsim_pll_find_pms(dsi, fin, freq, &p, &m, &s);
> > +	if (!fout) {
> > +		dev_err(dsi->dev,
> > +			"failed to find PLL PMS for requested frequency\n");
> > +		return 0;
> > +	}
> > +	dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s);
> > +
> > +	writel(driver_data->reg_values[PLL_TIMER],
> > +			dsi->reg_base + driver_data->plltmr_reg);
> > +
> > +	reg = DSIM_PLL_EN | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s);
> > +
> > +	if (driver_data->has_freqband) {
> > +		static const unsigned long freq_bands[] = {
> > +			100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ,
> > +			270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ,
> > +			510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ,
> > +			770 * MHZ, 870 * MHZ, 950 * MHZ,
> > +		};
> > +		int band;
> > +
> > +		for (band = 0; band < ARRAY_SIZE(freq_bands); ++band)
> > +			if (fout < freq_bands[band])
> > +				break;
> > +
> > +		dev_dbg(dsi->dev, "band %d\n", band);
> > +
> > +		reg |= DSIM_FREQ_BAND(band);
> > +	}
> > +
> > +	samsung_dsim_write(dsi, DSIM_PLLCTRL_REG, reg);
> > +
> > +	timeout = 1000;
> > +	do {
> > +		if (timeout-- == 0) {
> > +			dev_err(dsi->dev, "PLL failed to stabilize\n");
> > +			return 0;
> > +		}
> > +		reg = samsung_dsim_read(dsi, DSIM_STATUS_REG);
> > +	} while ((reg & DSIM_PLL_STABLE) == 0);
> > +
> > +	return fout;
> > +}
> > +
> > +static int samsung_dsim_enable_clock(struct samsung_dsim *dsi)
> > +{
> > +	unsigned long hs_clk, byte_clk, esc_clk;
> > +	unsigned long esc_div;
> > +	u32 reg;
> > +
> > +	hs_clk = samsung_dsim_set_pll(dsi, dsi->burst_clk_rate);
> > +	if (!hs_clk) {
> > +		dev_err(dsi->dev, "failed to configure DSI PLL\n");
> > +		return -EFAULT;
> > +	}
> > +
> > +	byte_clk = hs_clk / 8;
> > +	esc_div = DIV_ROUND_UP(byte_clk, dsi->esc_clk_rate);
> > +	esc_clk = byte_clk / esc_div;
> > +
> > +	if (esc_clk > 20 * MHZ) {
> > +		++esc_div;
> > +		esc_clk = byte_clk / esc_div;
> > +	}
> > +
> > +	dev_dbg(dsi->dev, "hs_clk = %lu, byte_clk = %lu, esc_clk = %lu\n",
> > +		hs_clk, byte_clk, esc_clk);
> > +
> > +	reg = samsung_dsim_read(dsi, DSIM_CLKCTRL_REG);
> > +	reg &= ~(DSIM_ESC_PRESCALER_MASK | DSIM_LANE_ESC_CLK_EN_CLK
> > +			| DSIM_LANE_ESC_CLK_EN_DATA_MASK | DSIM_PLL_BYPASS
> > +			| DSIM_BYTE_CLK_SRC_MASK);
> > +	reg |= DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN
> > +			| DSIM_ESC_PRESCALER(esc_div)
> > +			| DSIM_LANE_ESC_CLK_EN_CLK
> > +			| DSIM_LANE_ESC_CLK_EN_DATA(BIT(dsi->lanes) - 1)
> > +			| DSIM_BYTE_CLK_SRC(0)
> > +			| DSIM_TX_REQUEST_HSCLK;
> > +	samsung_dsim_write(dsi, DSIM_CLKCTRL_REG, reg);
> > +
> > +	return 0;
> > +}
> > +
> > +static void samsung_dsim_set_phy_ctrl(struct samsung_dsim *dsi)
> > +{
> > +	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
> > +	const unsigned int *reg_values = driver_data->reg_values;
> > +	u32 reg;
> > +
> > +	if (driver_data->has_freqband)
> > +		return;
> > +
> > +	/* B D-PHY: D-PHY Master & Slave Analog Block control */
> > +	reg = DSIM_PHYCTRL_ULPS_EXIT(reg_values[PHYCTRL_ULPS_EXIT]);
> > +	if (reg_values[PHYCTRL_VREG_LP])
> > +		reg |= DSIM_PHYCTRL_B_DPHYCTL_VREG_LP;
> > +	if (reg_values[PHYCTRL_SLEW_UP])
> > +		reg |= DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP;
> > +	samsung_dsim_write(dsi, DSIM_PHYCTRL_REG, reg);
> > +
> > +	/*
> > +	 * T LPX: Transmitted length of any Low-Power state period
> > +	 * T HS-EXIT: Time that the transmitter drives LP-11 following a HS
> > +	 *	burst
> > +	 */
> > +	reg = DSIM_PHYTIMING_LPX(reg_values[PHYTIMING_LPX]) |
> > +		DSIM_PHYTIMING_HS_EXIT(reg_values[PHYTIMING_HS_EXIT]);
> > +	samsung_dsim_write(dsi, DSIM_PHYTIMING_REG, reg);
> > +
> > +	/*
> > +	 * T CLK-PREPARE: Time that the transmitter drives the Clock Lane LP-00
> > +	 *	Line state immediately before the HS-0 Line state starting the
> > +	 *	HS transmission
> > +	 * T CLK-ZERO: Time that the transmitter drives the HS-0 state prior to
> > +	 *	transmitting the Clock.
> > +	 * T CLK_POST: Time that the transmitter continues to send HS clock
> > +	 *	after the last associated Data Lane has transitioned to LP Mode
> > +	 *	Interval is defined as the period from the end of T HS-TRAIL to
> > +	 *	the beginning of T CLK-TRAIL
> > +	 * T CLK-TRAIL: Time that the transmitter drives the HS-0 state after
> > +	 *	the last payload clock bit of a HS transmission burst
> > +	 */
> > +	reg = DSIM_PHYTIMING1_CLK_PREPARE(reg_values[PHYTIMING_CLK_PREPARE]) |
> > +		DSIM_PHYTIMING1_CLK_ZERO(reg_values[PHYTIMING_CLK_ZERO]) |
> > +		DSIM_PHYTIMING1_CLK_POST(reg_values[PHYTIMING_CLK_POST]) |
> > +		DSIM_PHYTIMING1_CLK_TRAIL(reg_values[PHYTIMING_CLK_TRAIL]);
> > +	samsung_dsim_write(dsi, DSIM_PHYTIMING1_REG, reg);
> > +
> > +	/*
> > +	 * T HS-PREPARE: Time that the transmitter drives the Data Lane LP-00
> > +	 *	Line state immediately before the HS-0 Line state starting the
> > +	 *	HS transmission
> > +	 * T HS-ZERO: Time that the transmitter drives the HS-0 state prior to
> > +	 *	transmitting the Sync sequence.
> > +	 * T HS-TRAIL: Time that the transmitter drives the flipped differential
> > +	 *	state after last payload data bit of a HS transmission burst
> > +	 */
> > +	reg = DSIM_PHYTIMING2_HS_PREPARE(reg_values[PHYTIMING_HS_PREPARE]) |
> > +		DSIM_PHYTIMING2_HS_ZERO(reg_values[PHYTIMING_HS_ZERO]) |
> > +		DSIM_PHYTIMING2_HS_TRAIL(reg_values[PHYTIMING_HS_TRAIL]);
> > +	samsung_dsim_write(dsi, DSIM_PHYTIMING2_REG, reg);
> > +}
> > +
> > +static void samsung_dsim_disable_clock(struct samsung_dsim *dsi)
> > +{
> > +	u32 reg;
> > +
> > +	reg = samsung_dsim_read(dsi, DSIM_CLKCTRL_REG);
> > +	reg &= ~(DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK
> > +			| DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN);
> > +	samsung_dsim_write(dsi, DSIM_CLKCTRL_REG, reg);
> > +
> > +	reg = samsung_dsim_read(dsi, DSIM_PLLCTRL_REG);
> > +	reg &= ~DSIM_PLL_EN;
> > +	samsung_dsim_write(dsi, DSIM_PLLCTRL_REG, reg);
> > +}
> > +
> > +static void samsung_dsim_enable_lane(struct samsung_dsim *dsi, u32 lane)
> > +{
> > +	u32 reg = samsung_dsim_read(dsi, DSIM_CONFIG_REG);
> > +	reg |= (DSIM_NUM_OF_DATA_LANE(dsi->lanes - 1) | DSIM_LANE_EN_CLK |
> > +			DSIM_LANE_EN(lane));
> > +	samsung_dsim_write(dsi, DSIM_CONFIG_REG, reg);
> > +}
> > +
> > +static int samsung_dsim_init_link(struct samsung_dsim *dsi)
> > +{
> > +	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
> > +	int timeout;
> > +	u32 reg;
> > +	u32 lanes_mask;
> > +
> > +	/* Initialize FIFO pointers */
> > +	reg = samsung_dsim_read(dsi, DSIM_FIFOCTRL_REG);
> > +	reg &= ~0x1f;
> > +	samsung_dsim_write(dsi, DSIM_FIFOCTRL_REG, reg);
> > +
> > +	usleep_range(9000, 11000);
> > +
> > +	reg |= 0x1f;
> > +	samsung_dsim_write(dsi, DSIM_FIFOCTRL_REG, reg);
> > +	usleep_range(9000, 11000);
> > +
> > +	/* DSI configuration */
> > +	reg = 0;
> > +
> > +	/*
> > +	 * The first bit of mode_flags specifies display configuration.
> > +	 * If this bit is set[= MIPI_DSI_MODE_VIDEO], dsi will support video
> > +	 * mode, otherwise it will support command mode.
> > +	 */
> > +	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> > +		reg |= DSIM_VIDEO_MODE;
> > +
> > +		/*
> > +		 * The user manual describes that following bits are ignored in
> > +		 * command mode.
> > +		 */
> > +		if (!(dsi->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH))
> > +			reg |= DSIM_MFLUSH_VS;
> > +		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
> > +			reg |= DSIM_SYNC_INFORM;
> > +		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
> > +			reg |= DSIM_BURST_MODE;
> > +		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT)
> > +			reg |= DSIM_AUTO_MODE;
> > +		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSE)
> > +			reg |= DSIM_HSE_MODE;
> > +		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HFP))
> > +			reg |= DSIM_HFP_MODE;
> > +		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HBP))
> > +			reg |= DSIM_HBP_MODE;
> > +		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSA))
> > +			reg |= DSIM_HSA_MODE;
> > +	}
> > +
> > +	if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET))
> > +		reg |= DSIM_EOT_DISABLE;
> > +
> > +	switch (dsi->format) {
> > +	case MIPI_DSI_FMT_RGB888:
> > +		reg |= DSIM_MAIN_PIX_FORMAT_RGB888;
> > +		break;
> > +	case MIPI_DSI_FMT_RGB666:
> > +		reg |= DSIM_MAIN_PIX_FORMAT_RGB666;
> > +		break;
> > +	case MIPI_DSI_FMT_RGB666_PACKED:
> > +		reg |= DSIM_MAIN_PIX_FORMAT_RGB666_P;
> > +		break;
> > +	case MIPI_DSI_FMT_RGB565:
> > +		reg |= DSIM_MAIN_PIX_FORMAT_RGB565;
> > +		break;
> > +	default:
> > +		dev_err(dsi->dev, "invalid pixel format\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	/*
> > +	 * Use non-continuous clock mode if the periparal wants and
> > +	 * host controller supports
> > +	 *
> > +	 * In non-continous clock mode, host controller will turn off
> > +	 * the HS clock between high-speed transmissions to reduce
> > +	 * power consumption.
> > +	 */
> > +	if (driver_data->has_clklane_stop &&
> > +			dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
> > +		reg |= DSIM_CLKLANE_STOP;
> > +	}
> > +	samsung_dsim_write(dsi, DSIM_CONFIG_REG, reg);
> > +
> > +	lanes_mask = BIT(dsi->lanes) - 1;
> > +	samsung_dsim_enable_lane(dsi, lanes_mask);
> > +
> > +	/* Check clock and data lane state are stop state */
> > +	timeout = 100;
> > +	do {
> > +		if (timeout-- == 0) {
> > +			dev_err(dsi->dev, "waiting for bus lanes timed out\n");
> > +			return -EFAULT;
> > +		}
> > +
> > +		reg = samsung_dsim_read(dsi, DSIM_STATUS_REG);
> > +		if ((reg & DSIM_STOP_STATE_DAT(lanes_mask))
> > +		    != DSIM_STOP_STATE_DAT(lanes_mask))
> > +			continue;
> > +	} while (!(reg & (DSIM_STOP_STATE_CLK | DSIM_TX_READY_HS_CLK)));
> > +
> > +	reg = samsung_dsim_read(dsi, DSIM_ESCMODE_REG);
> > +	reg &= ~DSIM_STOP_STATE_CNT_MASK;
> > +	reg |= DSIM_STOP_STATE_CNT(driver_data->reg_values[STOP_STATE_CNT]);
> > +	samsung_dsim_write(dsi, DSIM_ESCMODE_REG, reg);
> > +
> > +	reg = DSIM_BTA_TIMEOUT(0xff) | DSIM_LPDR_TIMEOUT(0xffff);
> > +	samsung_dsim_write(dsi, DSIM_TIMEOUT_REG, reg);
> > +
> > +	return 0;
> > +}
> > +
> > +static void samsung_dsim_set_display_mode(struct samsung_dsim *dsi)
> > +{
> > +	struct drm_display_mode *m = &dsi->mode;
> > +	unsigned int num_bits_resol = dsi->driver_data->num_bits_resol;
> > +	u32 reg;
> > +
> > +	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> > +		reg = DSIM_CMD_ALLOW(0xf)
> > +			| DSIM_STABLE_VFP(m->vsync_start - m->vdisplay)
> > +			| DSIM_MAIN_VBP(m->vtotal - m->vsync_end);
> > +		samsung_dsim_write(dsi, DSIM_MVPORCH_REG, reg);
> > +
> > +		reg = DSIM_MAIN_HFP(m->hsync_start - m->hdisplay)
> > +			| DSIM_MAIN_HBP(m->htotal - m->hsync_end);
> > +		samsung_dsim_write(dsi, DSIM_MHPORCH_REG, reg);
> > +
> > +		reg = DSIM_MAIN_VSA(m->vsync_end - m->vsync_start)
> > +			| DSIM_MAIN_HSA(m->hsync_end - m->hsync_start);
> > +		samsung_dsim_write(dsi, DSIM_MSYNC_REG, reg);
> > +	}
> > +	reg =  DSIM_MAIN_HRESOL(m->hdisplay, num_bits_resol) |
> > +		DSIM_MAIN_VRESOL(m->vdisplay, num_bits_resol);
> > +
> > +	samsung_dsim_write(dsi, DSIM_MDRESOL_REG, reg);
> > +
> > +	dev_dbg(dsi->dev, "LCD size = %dx%d\n", m->hdisplay, m->vdisplay);
> > +}
> > +
> > +static void samsung_dsim_set_display_enable(struct samsung_dsim *dsi,
> > +					    bool enable)
> > +{
> > +	u32 reg;
> > +
> > +	reg = samsung_dsim_read(dsi, DSIM_MDRESOL_REG);
> > +	if (enable)
> > +		reg |= DSIM_MAIN_STAND_BY;
> > +	else
> > +		reg &= ~DSIM_MAIN_STAND_BY;
> > +	samsung_dsim_write(dsi, DSIM_MDRESOL_REG, reg);
> > +}
> > +
> > +static int samsung_dsim_wait_for_hdr_fifo(struct samsung_dsim *dsi)
> > +{
> > +	int timeout = 2000;
> > +
> > +	do {
> > +		u32 reg = samsung_dsim_read(dsi, DSIM_FIFOCTRL_REG);
> > +
> > +		if (!(reg & DSIM_SFR_HEADER_FULL))
> > +			return 0;
> > +
> > +		if (!cond_resched())
> > +			usleep_range(950, 1050);
> > +	} while (--timeout);
> > +
> > +	return -ETIMEDOUT;
> > +}
> > +
> > +static void samsung_dsim_set_cmd_lpm(struct samsung_dsim *dsi, bool lpm)
> > +{
> > +	u32 v = samsung_dsim_read(dsi, DSIM_ESCMODE_REG);
> > +
> > +	if (lpm)
> > +		v |= DSIM_CMD_LPDT_LP;
> > +	else
> > +		v &= ~DSIM_CMD_LPDT_LP;
> > +
> > +	samsung_dsim_write(dsi, DSIM_ESCMODE_REG, v);
> > +}
> > +
> > +static void samsung_dsim_force_bta(struct samsung_dsim *dsi)
> > +{
> > +	u32 v = samsung_dsim_read(dsi, DSIM_ESCMODE_REG);
> > +	v |= DSIM_FORCE_BTA;
> > +	samsung_dsim_write(dsi, DSIM_ESCMODE_REG, v);
> > +}
> > +
> > +static void samsung_dsim_send_to_fifo(struct samsung_dsim *dsi,
> > +				      struct samsung_dsim_transfer *xfer)
> > +{
> > +	struct device *dev = dsi->dev;
> > +	struct mipi_dsi_packet *pkt = &xfer->packet;
> > +	const u8 *payload = pkt->payload + xfer->tx_done;
> > +	u16 length = pkt->payload_length - xfer->tx_done;
> > +	bool first = !xfer->tx_done;
> > +	u32 reg;
> > +
> > +	dev_dbg(dev, "< xfer %pK: tx len %u, done %u, rx len %u, done %u\n",
> > +		xfer, length, xfer->tx_done, xfer->rx_len, xfer->rx_done);
> > +
> > +	if (length > DSI_TX_FIFO_SIZE)
> > +		length = DSI_TX_FIFO_SIZE;
> > +
> > +	xfer->tx_done += length;
> > +
> > +	/* Send payload */
> > +	while (length >= 4) {
> > +		reg = get_unaligned_le32(payload);
> > +		samsung_dsim_write(dsi, DSIM_PAYLOAD_REG, reg);
> > +		payload += 4;
> > +		length -= 4;
> > +	}
> > +
> > +	reg = 0;
> > +	switch (length) {
> > +	case 3:
> > +		reg |= payload[2] << 16;
> > +		fallthrough;
> > +	case 2:
> > +		reg |= payload[1] << 8;
> > +		fallthrough;
> > +	case 1:
> > +		reg |= payload[0];
> > +		samsung_dsim_write(dsi, DSIM_PAYLOAD_REG, reg);
> > +		break;
> > +	}
> > +
> > +	/* Send packet header */
> > +	if (!first)
> > +		return;
> > +
> > +	reg = get_unaligned_le32(pkt->header);
> > +	if (samsung_dsim_wait_for_hdr_fifo(dsi)) {
> > +		dev_err(dev, "waiting for header FIFO timed out\n");
> > +		return;
> > +	}
> > +
> > +	if (NEQV(xfer->flags & MIPI_DSI_MSG_USE_LPM,
> > +		 dsi->state & DSIM_STATE_CMD_LPM)) {
> > +		samsung_dsim_set_cmd_lpm(dsi, xfer->flags & MIPI_DSI_MSG_USE_LPM);
> > +		dsi->state ^= DSIM_STATE_CMD_LPM;
> > +	}
> > +
> > +	samsung_dsim_write(dsi, DSIM_PKTHDR_REG, reg);
> > +
> > +	if (xfer->flags & MIPI_DSI_MSG_REQ_ACK)
> > +		samsung_dsim_force_bta(dsi);
> > +}
> > +
> > +static void samsung_dsim_read_from_fifo(struct samsung_dsim *dsi,
> > +					struct samsung_dsim_transfer *xfer)
> > +{
> > +	u8 *payload = xfer->rx_payload + xfer->rx_done;
> > +	bool first = !xfer->rx_done;
> > +	struct device *dev = dsi->dev;
> > +	u16 length;
> > +	u32 reg;
> > +
> > +	if (first) {
> > +		reg = samsung_dsim_read(dsi, DSIM_RXFIFO_REG);
> > +
> > +		switch (reg & 0x3f) {
> > +		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
> > +		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
> > +			if (xfer->rx_len >= 2) {
> > +				payload[1] = reg >> 16;
> > +				++xfer->rx_done;
> > +			}
> > +			fallthrough;
> > +		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
> > +		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
> > +			payload[0] = reg >> 8;
> > +			++xfer->rx_done;
> > +			xfer->rx_len = xfer->rx_done;
> > +			xfer->result = 0;
> > +			goto clear_fifo;
> > +		case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
> > +			dev_err(dev, "DSI Error Report: 0x%04x\n",
> > +				(reg >> 8) & 0xffff);
> > +			xfer->result = 0;
> > +			goto clear_fifo;
> > +		}
> > +
> > +		length = (reg >> 8) & 0xffff;
> > +		if (length > xfer->rx_len) {
> > +			dev_err(dev,
> > +				"response too long (%u > %u bytes), stripping\n",
> > +				xfer->rx_len, length);
> > +			length = xfer->rx_len;
> > +		} else if (length < xfer->rx_len)
> > +			xfer->rx_len = length;
> > +	}
> > +
> > +	length = xfer->rx_len - xfer->rx_done;
> > +	xfer->rx_done += length;
> > +
> > +	/* Receive payload */
> > +	while (length >= 4) {
> > +		reg = samsung_dsim_read(dsi, DSIM_RXFIFO_REG);
> > +		payload[0] = (reg >>  0) & 0xff;
> > +		payload[1] = (reg >>  8) & 0xff;
> > +		payload[2] = (reg >> 16) & 0xff;
> > +		payload[3] = (reg >> 24) & 0xff;
> > +		payload += 4;
> > +		length -= 4;
> > +	}
> > +
> > +	if (length) {
> > +		reg = samsung_dsim_read(dsi, DSIM_RXFIFO_REG);
> > +		switch (length) {
> > +		case 3:
> > +			payload[2] = (reg >> 16) & 0xff;
> > +			fallthrough;
> > +		case 2:
> > +			payload[1] = (reg >> 8) & 0xff;
> > +			fallthrough;
> > +		case 1:
> > +			payload[0] = reg & 0xff;
> > +		}
> > +	}
> > +
> > +	if (xfer->rx_done == xfer->rx_len)
> > +		xfer->result = 0;
> > +
> > +clear_fifo:
> > +	length = DSI_RX_FIFO_SIZE / 4;
> > +	do {
> > +		reg = samsung_dsim_read(dsi, DSIM_RXFIFO_REG);
> > +		if (reg == DSI_RX_FIFO_EMPTY)
> > +			break;
> > +	} while (--length);
> > +}
> > +
> > +static void samsung_dsim_transfer_start(struct samsung_dsim *dsi)
> > +{
> > +	unsigned long flags;
> > +	struct samsung_dsim_transfer *xfer;
> > +	bool start = false;
> > +
> > +again:
> > +	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > +
> > +	if (list_empty(&dsi->transfer_list)) {
> > +		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > +		return;
> > +	}
> > +
> > +	xfer = list_first_entry(&dsi->transfer_list,
> > +					struct samsung_dsim_transfer, list);
> > +
> > +	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > +
> > +	if (xfer->packet.payload_length &&
> > +	    xfer->tx_done == xfer->packet.payload_length)
> > +		/* waiting for RX */
> > +		return;
> > +
> > +	samsung_dsim_send_to_fifo(dsi, xfer);
> > +
> > +	if (xfer->packet.payload_length || xfer->rx_len)
> > +		return;
> > +
> > +	xfer->result = 0;
> > +	complete(&xfer->completed);
> > +
> > +	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > +
> > +	list_del_init(&xfer->list);
> > +	start = !list_empty(&dsi->transfer_list);
> > +
> > +	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > +
> > +	if (start)
> > +		goto again;
> > +}
> > +
> > +static bool samsung_dsim_transfer_finish(struct samsung_dsim *dsi)
> > +{
> > +	struct samsung_dsim_transfer *xfer;
> > +	unsigned long flags;
> > +	bool start = true;
> > +
> > +	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > +
> > +	if (list_empty(&dsi->transfer_list)) {
> > +		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > +		return false;
> > +	}
> > +
> > +	xfer = list_first_entry(&dsi->transfer_list,
> > +					struct samsung_dsim_transfer, list);
> > +
> > +	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > +
> > +	dev_dbg(dsi->dev,
> > +		"> xfer %pK, tx_len %zu, tx_done %u, rx_len %u, rx_done %u\n",
> > +		xfer, xfer->packet.payload_length, xfer->tx_done, xfer->rx_len,
> > +		xfer->rx_done);
> > +
> > +	if (xfer->tx_done != xfer->packet.payload_length)
> > +		return true;
> > +
> > +	if (xfer->rx_done != xfer->rx_len)
> > +		samsung_dsim_read_from_fifo(dsi, xfer);
> > +
> > +	if (xfer->rx_done != xfer->rx_len)
> > +		return true;
> > +
> > +	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > +
> > +	list_del_init(&xfer->list);
> > +	start = !list_empty(&dsi->transfer_list);
> > +
> > +	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > +
> > +	if (!xfer->rx_len)
> > +		xfer->result = 0;
> > +	complete(&xfer->completed);
> > +
> > +	return start;
> > +}
> > +
> > +static void samsung_dsim_remove_transfer(struct samsung_dsim *dsi,
> > +					 struct samsung_dsim_transfer *xfer)
> > +{
> > +	unsigned long flags;
> > +	bool start;
> > +
> > +	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > +
> > +	if (!list_empty(&dsi->transfer_list) &&
> > +	    xfer == list_first_entry(&dsi->transfer_list,
> > +				     struct samsung_dsim_transfer, list)) {
> > +		list_del_init(&xfer->list);
> > +		start = !list_empty(&dsi->transfer_list);
> > +		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > +		if (start)
> > +			samsung_dsim_transfer_start(dsi);
> > +		return;
> > +	}
> > +
> > +	list_del_init(&xfer->list);
> > +
> > +	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > +}
> > +
> > +static int samsung_dsim_transfer(struct samsung_dsim *dsi,
> > +				 struct samsung_dsim_transfer *xfer)
> > +{
> > +	unsigned long flags;
> > +	bool stopped;
> > +
> > +	xfer->tx_done = 0;
> > +	xfer->rx_done = 0;
> > +	xfer->result = -ETIMEDOUT;
> > +	init_completion(&xfer->completed);
> > +
> > +	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > +
> > +	stopped = list_empty(&dsi->transfer_list);
> > +	list_add_tail(&xfer->list, &dsi->transfer_list);
> > +
> > +	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > +
> > +	if (stopped)
> > +		samsung_dsim_transfer_start(dsi);
> > +
> > +	wait_for_completion_timeout(&xfer->completed,
> > +				    msecs_to_jiffies(DSI_XFER_TIMEOUT_MS));
> > +	if (xfer->result == -ETIMEDOUT) {
> > +		struct mipi_dsi_packet *pkt = &xfer->packet;
> > +		samsung_dsim_remove_transfer(dsi, xfer);
> > +		dev_err(dsi->dev, "xfer timed out: %*ph %*ph\n", 4, pkt->header,
> > +			(int)pkt->payload_length, pkt->payload);
> > +		return -ETIMEDOUT;
> > +	}
> > +
> > +	/* Also covers hardware timeout condition */
> > +	return xfer->result;
> > +}
> > +
> > +static irqreturn_t samsung_dsim_irq(int irq, void *dev_id)
> > +{
> > +	struct samsung_dsim *dsi = dev_id;
> > +	u32 status;
> > +
> > +	status = samsung_dsim_read(dsi, DSIM_INTSRC_REG);
> > +	if (!status) {
> > +		static unsigned long int j;
> > +		if (printk_timed_ratelimit(&j, 500))
> > +			dev_warn(dsi->dev, "spurious interrupt\n");
> > +		return IRQ_HANDLED;
> > +	}
> > +	samsung_dsim_write(dsi, DSIM_INTSRC_REG, status);
> > +
> > +	if (status & DSIM_INT_SW_RST_RELEASE) {
> > +		u32 mask = ~(DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY |
> > +			DSIM_INT_SFR_HDR_FIFO_EMPTY | DSIM_INT_RX_ECC_ERR |
> > +			DSIM_INT_SW_RST_RELEASE);
> > +		samsung_dsim_write(dsi, DSIM_INTMSK_REG, mask);
> > +		complete(&dsi->completed);
> > +		return IRQ_HANDLED;
> > +	}
> > +
> > +	if (!(status & (DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY |
> > +			DSIM_INT_PLL_STABLE)))
> > +		return IRQ_HANDLED;
> > +
> > +	if (samsung_dsim_transfer_finish(dsi))
> > +		samsung_dsim_transfer_start(dsi);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static irqreturn_t samsung_dsim_te_irq_handler(int irq, void *dev_id)
> > +{
> > +	struct samsung_dsim *dsi = dev_id;
> > +	const struct samsung_dsim_host_ops *ops = dsi->driver_data->host_ops;
> > +
> > +	if (ops && ops->te_handler &&
> > +	    (dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
> > +		ops->te_handler(dsi->dsi_host.dev);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static void samsung_dsim_enable_irq(struct samsung_dsim *dsi)
> > +{
> > +	enable_irq(dsi->irq);
> > +
> > +	if (gpio_is_valid(dsi->te_gpio))
> > +		enable_irq(gpio_to_irq(dsi->te_gpio));
> > +}
> > +
> > +static void samsung_dsim_disable_irq(struct samsung_dsim *dsi)
> > +{
> > +	if (gpio_is_valid(dsi->te_gpio))
> > +		disable_irq(gpio_to_irq(dsi->te_gpio));
> > +
> > +	disable_irq(dsi->irq);
> > +}
> > +
> > +static int samsung_dsim_init(struct samsung_dsim *dsi)
> > +{
> > +	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
> > +
> > +	samsung_dsim_reset(dsi);
> > +	samsung_dsim_enable_irq(dsi);
> > +
> > +	if (driver_data->reg_values[RESET_TYPE] == DSIM_FUNCRST)
> > +		samsung_dsim_enable_lane(dsi, BIT(dsi->lanes) - 1);
> > +
> > +	samsung_dsim_enable_clock(dsi);
> > +	if (driver_data->wait_for_reset)
> > +		samsung_dsim_wait_for_reset(dsi);
> > +	samsung_dsim_set_phy_ctrl(dsi);
> > +	samsung_dsim_init_link(dsi);
> > +
> > +	return 0;
> > +}
> > +
> > +static int samsung_dsim_register_te_irq(struct samsung_dsim *dsi,
> > +					struct device *panel)
> > +{
> > +	int ret;
> > +	int te_gpio_irq;
> > +
> > +	dsi->te_gpio = of_get_named_gpio(panel->of_node, "te-gpios", 0);
> > +	if (dsi->te_gpio == -ENOENT)
> > +		return 0;
> > +
> > +	if (!gpio_is_valid(dsi->te_gpio)) {
> > +		ret = dsi->te_gpio;
> > +		dev_err(dsi->dev, "cannot get te-gpios, %d\n", ret);
> > +		goto out;
> > +	}
> > +
> > +	ret = gpio_request(dsi->te_gpio, "te_gpio");
> > +	if (ret) {
> > +		dev_err(dsi->dev, "gpio request failed with %d\n", ret);
> > +		goto out;
> > +	}
> > +
> > +	te_gpio_irq = gpio_to_irq(dsi->te_gpio);
> > +	irq_set_status_flags(te_gpio_irq, IRQ_NOAUTOEN);
> > +
> > +	ret = request_threaded_irq(te_gpio_irq, samsung_dsim_te_irq_handler,
> > +				   NULL, IRQF_TRIGGER_RISING, "TE", dsi);
> > +	if (ret) {
> > +		dev_err(dsi->dev, "request interrupt failed with %d\n", ret);
> > +		gpio_free(dsi->te_gpio);
> > +		goto out;
> > +	}
> > +
> > +out:
> > +	return ret;
> > +}
> > +
> > +static void samsung_dsim_unregister_te_irq(struct samsung_dsim *dsi)
> > +{
> > +	if (gpio_is_valid(dsi->te_gpio)) {
> > +		free_irq(gpio_to_irq(dsi->te_gpio), dsi);
> > +		gpio_free(dsi->te_gpio);
> > +		dsi->te_gpio = -ENOENT;
> > +	}
> > +}
> > +
> > +static void samsung_dsim_enable(struct samsung_dsim *dsi)
> > +{
> > +	struct drm_bridge *iter;
> > +	int ret;
> > +
> > +	if (dsi->state & DSIM_STATE_ENABLED)
> > +		return;
> > +
> > +	pm_runtime_get_sync(dsi->dev);
> > +	dsi->state |= DSIM_STATE_ENABLED;
> > +
> > +	if (dsi->panel) {
> > +		ret = drm_panel_prepare(dsi->panel);
> > +		if (ret < 0)
> > +			goto err_put_sync;
> > +	} else {
> > +		list_for_each_entry_reverse(iter, &dsi->bridge_chain,
> > +					    chain_node) {
> > +			if (iter->funcs->pre_enable)
> > +				iter->funcs->pre_enable(iter);
> > +		}
> > +	}
> > +
> > +	samsung_dsim_set_display_mode(dsi);
> > +	samsung_dsim_set_display_enable(dsi, true);
> > +
> > +	if (dsi->panel) {
> > +		ret = drm_panel_enable(dsi->panel);
> > +		if (ret < 0)
> > +			goto err_display_disable;
> > +	} else {
> > +		list_for_each_entry(iter, &dsi->bridge_chain, chain_node) {
> > +			if (iter->funcs->enable)
> > +				iter->funcs->enable(iter);
> > +		}
> > +	}
> > +
> > +	dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
> > +	return;
> > +
> > +err_display_disable:
> > +	samsung_dsim_set_display_enable(dsi, false);
> > +	drm_panel_unprepare(dsi->panel);
> > +
> > +err_put_sync:
> > +	dsi->state &= ~DSIM_STATE_ENABLED;
> > +	pm_runtime_put(dsi->dev);
> > +}
> > +
> > +static void samsung_dsim_disable(struct samsung_dsim *dsi)
> > +{
> > +	struct drm_bridge *iter;
> > +
> > +	if (!(dsi->state & DSIM_STATE_ENABLED))
> > +		return;
> > +
> > +	dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
> > +
> > +	drm_panel_disable(dsi->panel);
> > +
> > +	list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) {
> > +		if (iter->funcs->disable)
> > +			iter->funcs->disable(iter);
> > +	}
> > +
> > +	samsung_dsim_set_display_enable(dsi, false);
> > +	drm_panel_unprepare(dsi->panel);
> > +
> > +	list_for_each_entry(iter, &dsi->bridge_chain, chain_node) {
> > +		if (iter->funcs->post_disable)
> > +			iter->funcs->post_disable(iter);
> > +	}
> > +
> > +	dsi->state &= ~DSIM_STATE_ENABLED;
> > +	pm_runtime_put_sync(dsi->dev);
> > +}
> > +
> > +static enum drm_connector_status
> > +samsung_dsim_detect(struct drm_connector *connector, bool force)
> > +{
> > +	return connector->status;
> > +}
> > +
> > +static void samsung_dsim_connector_destroy(struct drm_connector *connector)
> > +{
> > +	drm_connector_unregister(connector);
> > +	drm_connector_cleanup(connector);
> > +	connector->dev = NULL;
> > +}
> > +
> > +static const struct drm_connector_funcs samsung_dsim_connector_funcs = {
> > +	.detect = samsung_dsim_detect,
> > +	.fill_modes = drm_helper_probe_single_connector_modes,
> > +	.destroy = samsung_dsim_connector_destroy,
> > +	.reset = drm_atomic_helper_connector_reset,
> > +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> > +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> > +};
> > +
> > +static int samsung_dsim_get_modes(struct drm_connector *connector)
> > +{
> > +	struct samsung_dsim *dsi = connector_to_dsi(connector);
> > +
> > +	if (dsi->panel)
> > +		return drm_panel_get_modes(dsi->panel, connector);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct drm_connector_helper_funcs samsung_dsim_connector_helper_funcs = {
> > +	.get_modes = samsung_dsim_get_modes,
> > +};
> > +
> > +static int samsung_dsim_create_connector(struct samsung_dsim *dsi)
> > +{
> > +	struct drm_connector *connector = &dsi->connector;
> > +	struct drm_device *drm = dsi->bridge.dev;
> > +	int ret;
> > +
> > +	connector->polled = DRM_CONNECTOR_POLL_HPD;
> > +
> > +	ret = drm_connector_init(drm, connector, &samsung_dsim_connector_funcs,
> > +				 DRM_MODE_CONNECTOR_DSI);
> > +	if (ret) {
> > +		DRM_DEV_ERROR(dsi->dev,
> > +			      "Failed to initialize connector with drm\n");
> > +		return ret;
> > +	}
> > +
> > +	connector->status = connector_status_disconnected;
> > +	drm_connector_helper_add(connector, &samsung_dsim_connector_helper_funcs);
> > +	drm_connector_attach_encoder(connector, dsi->bridge.encoder);
> > +	if (!drm->registered)
> > +		return 0;
> > +
> > +	connector->funcs->reset(connector);
> > +	drm_connector_register(connector);
> > +	return 0;
> > +}
> > +
> > +static int samsung_dsim_bridge_attach(struct drm_bridge *bridge,
> > +				      enum drm_bridge_attach_flags flags)
> > +{
> > +	struct samsung_dsim *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 = samsung_dsim_create_connector(dsi);
> > +		if (ret)
> > +			return ret;
> > +
> > +		if (dsi->panel) {
> > +			dsi->connector.status = connector_status_connected;
> > +		}
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void samsung_dsim_bridge_detach(struct drm_bridge *bridge)
> > +{
> > +	struct samsung_dsim *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);
> > +		samsung_dsim_disable(dsi);
> > +		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 void samsung_dsim_bridge_enable(struct drm_bridge *bridge)
> > +{
> > +	struct samsung_dsim *dsi = bridge->driver_private;
> > +
> > +	samsung_dsim_enable(dsi);
> > +}
> > +
> > +static void samsung_dsim_bridge_disable(struct drm_bridge *bridge)
> > +{
> > +	struct samsung_dsim *dsi = bridge->driver_private;
> > +
> > +	samsung_dsim_disable(dsi);
> > +}
> > +
> > +static void samsung_dsim_bridge_mode_set(struct drm_bridge *bridge,
> > +					 const struct drm_display_mode *mode,
> > +					 const struct drm_display_mode *adjusted_mode)
> > +{
> > +	struct samsung_dsim *dsi = bridge->driver_private;
> > +
> > +	/* The mode is set when actually enabling the device. */
> > +	drm_mode_copy(&dsi->mode, adjusted_mode);
> > +}
> > +
> > +static const struct drm_bridge_funcs samsung_dsim_bridge_funcs = {
> > +	.attach = samsung_dsim_bridge_attach,
> > +	.detach = samsung_dsim_bridge_detach,
> > +	.enable = samsung_dsim_bridge_enable,
> > +	.disable = samsung_dsim_bridge_disable,
> > +	.mode_set = samsung_dsim_bridge_mode_set,
> > +};
> > +
> > +static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
> > +				    struct mipi_dsi_device *device)
> > +{
> > +	struct samsung_dsim *dsi = host_to_dsi(host);
> > +	const struct samsung_dsim_host_ops *ops = dsi->driver_data->host_ops;
> > +	struct drm_bridge *out_bridge;
> > +
> > +	out_bridge = of_drm_find_bridge(device->dev.of_node);
> > +	if (out_bridge) {
> > +		dsi->out_bridge = out_bridge;
> > +	} else {
> > +		dsi->panel = of_drm_find_panel(device->dev.of_node);
> > +		if (IS_ERR(dsi->panel))
> > +			dsi->panel = NULL;
> > +		else
> > +			dsi->connector.status = connector_status_connected;
> > +	}
> > +
> > +	/*
> > +	 * This is a temporary solution and should be made by more generic way.
> > +	 *
> > +	 * If attached panel device is for command mode one, dsi should register
> > +	 * TE interrupt handler.
> > +	 */
> > +	if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO)) {
> > +		int ret = samsung_dsim_register_te_irq(dsi, &device->dev);
> > +		if (ret)
> > +			return ret;
> > +	}
> > +
> > +	dsi->lanes = device->lanes;
> > +	dsi->format = device->format;
> > +	dsi->mode_flags = device->mode_flags;
> > +
> > +	if (ops && ops->attach)
> > +		ops->attach(dsi->dsi_host.dev, device);
> > +
> > +	return 0;
> > +}
> > +
> > +static int samsung_dsim_host_detach(struct mipi_dsi_host *host,
> > +				    struct mipi_dsi_device *device)
> > +{
> > +	struct samsung_dsim *dsi = host_to_dsi(host);
> > +	const struct samsung_dsim_host_ops *ops = dsi->driver_data->host_ops;
> > +
> > +	samsung_dsim_unregister_te_irq(dsi);
> > +
> > +	if (ops && ops->detach)
> > +		ops->detach(dsi->dsi_host.dev, device);
> > +
> > +	samsung_dsim_unregister_te_irq(dsi);
> > +
> > +	return 0;
> > +}
> > +
> > +static ssize_t samsung_dsim_host_transfer(struct mipi_dsi_host *host,
> > +					  const struct mipi_dsi_msg *msg)
> > +{
> > +	struct samsung_dsim *dsi = host_to_dsi(host);
> > +	struct samsung_dsim_transfer xfer;
> > +	int ret;
> > +
> > +	if (!(dsi->state & DSIM_STATE_ENABLED))
> > +		return -EINVAL;
> > +
> > +	if (!(dsi->state & DSIM_STATE_INITIALIZED)) {
> > +		ret = samsung_dsim_init(dsi);
> > +		if (ret)
> > +			return ret;
> > +		dsi->state |= DSIM_STATE_INITIALIZED;
> > +	}
> > +
> > +	ret = mipi_dsi_create_packet(&xfer.packet, msg);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	xfer.rx_len = msg->rx_len;
> > +	xfer.rx_payload = msg->rx_buf;
> > +	xfer.flags = msg->flags;
> > +
> > +	ret = samsung_dsim_transfer(dsi, &xfer);
> > +	return (ret < 0) ? ret : xfer.rx_done;
> > +}
> > +
> > +static const struct mipi_dsi_host_ops samsung_dsim_ops = {
> > +	.attach = samsung_dsim_host_attach,
> > +	.detach = samsung_dsim_host_detach,
> > +	.transfer = samsung_dsim_host_transfer,
> > +};
> > +
> > +static int samsung_dsim_of_read_u32(const struct device_node *np,
> > +				    const char *propname, u32 *out_value)
> > +{
> > +	int ret = of_property_read_u32(np, propname, out_value);
> > +
> > +	if (ret < 0)
> > +		pr_err("%pOF: failed to get '%s' property\n", np, propname);
> > +
> > +	return ret;
> > +}
> > +
> > +static int samsung_dsim_parse_dt(struct samsung_dsim *dsi)
> > +{
> > +	struct device *dev = dsi->dev;
> > +	struct device_node *node = dev->of_node;
> > +	int ret;
> > +
> > +	ret = samsung_dsim_of_read_u32(node, "samsung,pll-clock-frequency",
> > +				       &dsi->pll_clk_rate);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	ret = samsung_dsim_of_read_u32(node, "samsung,burst-clock-frequency",
> > +				       &dsi->burst_clk_rate);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	ret = samsung_dsim_of_read_u32(node, "samsung,esc-clock-frequency",
> > +				       &dsi->esc_clk_rate);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	return 0;
> > +}
> > +
> > +static struct samsung_dsim *__samsung_dsim_probe(struct platform_device *pdev)
> > +{
> > +	struct device *dev = &pdev->dev;
> > +	struct drm_bridge *bridge;
> > +	struct resource *res;
> > +	struct samsung_dsim *dsi;
> > +	int ret, i;
> > +
> > +	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
> > +	if (!dsi)
> > +		return ERR_PTR(-ENOMEM);
> > +
> > +	/* To be checked as invalid one */
> > +	dsi->te_gpio = -ENOENT;
> > +
> > +	init_completion(&dsi->completed);
> > +	spin_lock_init(&dsi->transfer_lock);
> > +	INIT_LIST_HEAD(&dsi->transfer_list);
> > +	INIT_LIST_HEAD(&dsi->bridge_chain);
> > +
> > +	dsi->dsi_host.ops = &samsung_dsim_ops;
> > +	dsi->dsi_host.dev = dev;
> > +
> > +	dsi->dev = dev;
> > +	dsi->driver_data = of_device_get_match_data(dev);
> > +
> > +	dsi->supplies[0].supply = "vddcore";
> > +	dsi->supplies[1].supply = "vddio";
> > +	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(dsi->supplies),
> > +				      dsi->supplies);
> > +	if (ret) {
> > +		if (ret != -EPROBE_DEFER)
> > +			dev_info(dev, "failed to get regulators: %d\n", ret);
> > +		return ERR_PTR(ret);
> > +	}
> > +
> > +	dsi->clks = devm_kcalloc(dev,
> > +			dsi->driver_data->num_clks, sizeof(*dsi->clks),
> > +			GFP_KERNEL);
> > +	if (!dsi->clks)
> > +		return ERR_PTR(-ENOMEM);
> > +
> > +	for (i = 0; i < dsi->driver_data->num_clks; i++) {
> > +		dsi->clks[i] = devm_clk_get(dev, clk_names[i]);
> > +		if (IS_ERR(dsi->clks[i])) {
> > +			if (strcmp(clk_names[i], "sclk_mipi") == 0) {
> > +				dsi->clks[i] = devm_clk_get(dev,
> > +							OLD_SCLK_MIPI_CLK_NAME);
> > +				if (!IS_ERR(dsi->clks[i]))
> > +					continue;
> > +			}
> > +
> > +			dev_info(dev, "failed to get the clock: %s\n",
> > +					clk_names[i]);
> > +			return ERR_PTR(PTR_ERR(dsi->clks[i]));
> > +		}
> > +	}
> > +
> > +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +	dsi->reg_base = devm_ioremap_resource(dev, res);
> > +	if (IS_ERR(dsi->reg_base)) {
> > +		dev_err(dev, "failed to remap io region\n");
> > +		return dsi->reg_base;
> > +	}
> > +
> > +	dsi->phy = devm_phy_get(dev, "dsim");
> > +	if (IS_ERR(dsi->phy)) {
> > +		dev_info(dev, "failed to get dsim phy\n");
> > +		return ERR_PTR(PTR_ERR(dsi->phy));
> > +	}
> > +
> > +	dsi->irq = platform_get_irq(pdev, 0);
> > +	if (dsi->irq < 0)
> > +		return ERR_PTR(dsi->irq);
> > +
> > +	irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
> > +	ret = devm_request_threaded_irq(dev, dsi->irq, NULL,
> > +					samsung_dsim_irq, IRQF_ONESHOT,
> > +					dev_name(dev), dsi);
> > +	if (ret) {
> > +		dev_err(dev, "failed to request dsi irq\n");
> > +		return ERR_PTR(ret);
> > +	}
> > +
> > +	ret = samsung_dsim_parse_dt(dsi);
> > +	if (ret)
> > +		return ERR_PTR(ret);
> > +
> > +	ret = mipi_dsi_host_register(&dsi->dsi_host);
> > +	if (ret)
> > +		return ERR_PTR(ret);
> > +
> > +	bridge = &dsi->bridge;
> > +	bridge->driver_private = dsi;
> > +	bridge->funcs = &samsung_dsim_bridge_funcs;
> > +	bridge->of_node = dev->of_node;
> > +	drm_bridge_add(bridge);
> > +
> > +	return dsi;
> > +}
> > +
> > +static void __samsung_dsim_remove(struct samsung_dsim *dsi)
> > +{
> > +	drm_bridge_remove(&dsi->bridge);
> > +
> > +	mipi_dsi_host_unregister(&dsi->dsi_host);
> > +}
> > +
> > +/*
> > + * Probe/remove API, used from platforms based on the DRM bridge API.
> > + */
> > +struct samsung_dsim *samsung_dsim_probe(struct platform_device *pdev)
> > +{
> > +	return __samsung_dsim_probe(pdev);
> > +}
> > +EXPORT_SYMBOL_GPL(samsung_dsim_probe);
> > +
> > +void samsung_dsim_remove(struct samsung_dsim *dsi)
> > +{
> > +	return __samsung_dsim_remove(dsi);
> > +}
> > +EXPORT_SYMBOL_GPL(samsung_dsim_remove);
> > +
> > +/*
> > + * Bind/unbind API, used from platforms based on the component framework.
> > + */
> > +int samsung_dsim_bind(struct samsung_dsim *dsi, struct drm_encoder *encoder)
> > +{
> > +	struct drm_bridge *previous = drm_bridge_chain_get_first_bridge(encoder);
> > +
> > +	return drm_bridge_attach(encoder, &dsi->bridge, previous, 0);
> > +}
> > +EXPORT_SYMBOL_GPL(samsung_dsim_bind);
> > +
> > +void samsung_dsim_unbind(struct samsung_dsim *dsi)
> > +{
> > +	samsung_dsim_disable(dsi);
> > +}
> > +EXPORT_SYMBOL_GPL(samsung_dsim_unbind);
> > +
> > +int samsung_dsim_suspend(struct samsung_dsim *dsi)
> > +{
> > +	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
> > +	int ret, i;
> > +
> > +	usleep_range(10000, 20000);
> > +
> > +	if (dsi->state & DSIM_STATE_INITIALIZED) {
> > +		dsi->state &= ~DSIM_STATE_INITIALIZED;
> > +
> > +		samsung_dsim_disable_clock(dsi);
> > +
> > +		samsung_dsim_disable_irq(dsi);
> > +	}
> > +
> > +	dsi->state &= ~DSIM_STATE_CMD_LPM;
> > +
> > +	phy_power_off(dsi->phy);
> > +
> > +	for (i = driver_data->num_clks - 1; i > -1; i--)
> > +		clk_disable_unprepare(dsi->clks[i]);
> > +
> > +	ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
> > +	if (ret < 0)
> > +		dev_err(dsi->dev, "cannot disable regulators %d\n", ret);
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(samsung_dsim_suspend);
> > +
> > +int samsung_dsim_resume(struct samsung_dsim *dsi)
> > +{
> > +	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
> > +	int ret, i;
> > +
> > +	ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
> > +	if (ret < 0) {
> > +		dev_err(dsi->dev, "cannot enable regulators %d\n", ret);
> > +		return ret;
> > +	}
> > +
> > +	for (i = 0; i < driver_data->num_clks; i++) {
> > +		ret = clk_prepare_enable(dsi->clks[i]);
> > +		if (ret < 0)
> > +			goto err_clk;
> > +	}
> > +
> > +	ret = phy_power_on(dsi->phy);
> > +	if (ret < 0) {
> > +		dev_err(dsi->dev, "cannot enable phy %d\n", ret);
> > +		goto err_clk;
> > +	}
> > +
> > +	return 0;
> > +
> > +err_clk:
> > +	while (--i > -1)
> > +		clk_disable_unprepare(dsi->clks[i]);
> > +	regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
> > +
> > +	return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(samsung_dsim_resume);
> > +
> > +MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
> > +MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
> > +MODULE_DESCRIPTION("Samsung SoC MIPI DSI Master");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
> > index 6417f374b923..3bc321ab5bc8 100644
> > --- a/drivers/gpu/drm/exynos/Kconfig
> > +++ b/drivers/gpu/drm/exynos/Kconfig
> > @@ -16,7 +16,6 @@ comment "CRTCs"
> >  
> >  config DRM_EXYNOS_FIMD
> >  	bool "FIMD"
> > -	depends on !FB_S3C
> >  	select MFD_SYSCON
> >  	help
> >  	  Choose this option if you want to use Exynos FIMD for DRM.
> > @@ -28,7 +27,6 @@ config DRM_EXYNOS5433_DECON
> >  
> >  config DRM_EXYNOS7_DECON
> >  	bool "DECON on Exynos7"
> > -	depends on !FB_S3C
> >  	help
> >  	  Choose this option if you want to use Exynos DECON for DRM.
> >  
> > @@ -55,8 +53,7 @@ config DRM_EXYNOS_DPI
> >  config DRM_EXYNOS_DSI
> >  	bool "MIPI-DSI host"
> >  	depends on DRM_EXYNOS_FIMD || DRM_EXYNOS5433_DECON || DRM_EXYNOS7_DECON
> > -	select DRM_MIPI_DSI
> > -	select DRM_PANEL
> > +	select DRM_SAMSUNG_DSIM
> >  	default n
> >  	help
> >  	  This enables support for Exynos MIPI-DSI device.
> > diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile
> > index add70b336935..2fd2f3ee4fcf 100644
> > --- a/drivers/gpu/drm/exynos/Makefile
> > +++ b/drivers/gpu/drm/exynos/Makefile
> > @@ -11,7 +11,7 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD)	+= exynos_drm_fimd.o
> >  exynosdrm-$(CONFIG_DRM_EXYNOS5433_DECON)	+= exynos5433_drm_decon.o
> >  exynosdrm-$(CONFIG_DRM_EXYNOS7_DECON)	+= exynos7_drm_decon.o
> >  exynosdrm-$(CONFIG_DRM_EXYNOS_DPI)	+= exynos_drm_dpi.o
> > -exynosdrm-$(CONFIG_DRM_EXYNOS_DSI)	+= exynos_drm_dsi.o exynos_drm_dsi_pltfm.o
> > +exynosdrm-$(CONFIG_DRM_EXYNOS_DSI)	+= exynos_drm_dsi.o
> >  exynosdrm-$(CONFIG_DRM_EXYNOS_DP)	+= exynos_dp.o
> >  exynosdrm-$(CONFIG_DRM_EXYNOS_MIXER)	+= exynos_mixer.o
> >  exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI)	+= exynos_hdmi.o
> > diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > index e8aea9d90c34..17f37fa74718 100644
> > --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > @@ -5,1774 +5,328 @@
> >   * Copyright (c) 2014 Samsung Electronics Co., Ltd
> >   *
> >   * Contacts: Tomasz Figa <t.figa@samsung.com>
> > -*/
> > + */
> >  
> > -#include <linux/clk.h>
> > -#include <linux/delay.h>
> >  #include <linux/component.h>
> > -#include <linux/gpio/consumer.h>
> > -#include <linux/irq.h>
> >  #include <linux/of_device.h>
> > -#include <linux/of_gpio.h>
> >  #include <linux/of_graph.h>
> > -#include <linux/phy/phy.h>
> > -#include <linux/regulator/consumer.h>
> > -
> > -#include <asm/unaligned.h>
> > +#include <linux/pm_runtime.h>
> >  
> > -#include <video/mipi_display.h>
> > -#include <video/videomode.h>
> > -
> > -#include <drm/drm_atomic_helper.h>
> > +#include <drm/bridge/samsung-dsim.h>
> >  #include <drm/drm_bridge.h>
> > -#include <drm/drm_fb_helper.h>
> > +#include <drm/drm_encoder.h>
> >  #include <drm/drm_mipi_dsi.h>
> > -#include <drm/drm_panel.h>
> > -#include <drm/drm_print.h>
> >  #include <drm/drm_probe_helper.h>
> >  #include <drm/drm_simple_kms_helper.h>
> >  
> > -#include "exynos_drm_dsi.h"
> > -
> > -/* returns true iff both arguments logically differs */
> > -#define NEQV(a, b) (!(a) ^ !(b))
> > -
> > -/* DSIM_STATUS */
> > -#define DSIM_STOP_STATE_DAT(x)		(((x) & 0xf) << 0)
> > -#define DSIM_STOP_STATE_CLK		(1 << 8)
> > -#define DSIM_TX_READY_HS_CLK		(1 << 10)
> > -#define DSIM_PLL_STABLE			(1 << 31)
> > -
> > -/* DSIM_TIMEOUT */
> > -#define DSIM_LPDR_TIMEOUT(x)		((x) << 0)
> > -#define DSIM_BTA_TIMEOUT(x)		((x) << 16)
> > -
> > -/* DSIM_CLKCTRL */
> > -#define DSIM_ESC_PRESCALER(x)		(((x) & 0xffff) << 0)
> > -#define DSIM_ESC_PRESCALER_MASK		(0xffff << 0)
> > -#define DSIM_LANE_ESC_CLK_EN_CLK	(1 << 19)
> > -#define DSIM_LANE_ESC_CLK_EN_DATA(x)	(((x) & 0xf) << 20)
> > -#define DSIM_LANE_ESC_CLK_EN_DATA_MASK	(0xf << 20)
> > -#define DSIM_BYTE_CLKEN			(1 << 24)
> > -#define DSIM_BYTE_CLK_SRC(x)		(((x) & 0x3) << 25)
> > -#define DSIM_BYTE_CLK_SRC_MASK		(0x3 << 25)
> > -#define DSIM_PLL_BYPASS			(1 << 27)
> > -#define DSIM_ESC_CLKEN			(1 << 28)
> > -#define DSIM_TX_REQUEST_HSCLK		(1 << 31)
> > -
> > -/* DSIM_CONFIG */
> > -#define DSIM_LANE_EN_CLK		(1 << 0)
> > -#define DSIM_LANE_EN(x)			(((x) & 0xf) << 1)
> > -#define DSIM_NUM_OF_DATA_LANE(x)	(((x) & 0x3) << 5)
> > -#define DSIM_SUB_PIX_FORMAT(x)		(((x) & 0x7) << 8)
> > -#define DSIM_MAIN_PIX_FORMAT_MASK	(0x7 << 12)
> > -#define DSIM_MAIN_PIX_FORMAT_RGB888	(0x7 << 12)
> > -#define DSIM_MAIN_PIX_FORMAT_RGB666	(0x6 << 12)
> > -#define DSIM_MAIN_PIX_FORMAT_RGB666_P	(0x5 << 12)
> > -#define DSIM_MAIN_PIX_FORMAT_RGB565	(0x4 << 12)
> > -#define DSIM_SUB_VC			(((x) & 0x3) << 16)
> > -#define DSIM_MAIN_VC			(((x) & 0x3) << 18)
> > -#define DSIM_HSA_MODE			(1 << 20)
> > -#define DSIM_HBP_MODE			(1 << 21)
> > -#define DSIM_HFP_MODE			(1 << 22)
> > -#define DSIM_HSE_MODE			(1 << 23)
> > -#define DSIM_AUTO_MODE			(1 << 24)
> > -#define DSIM_VIDEO_MODE			(1 << 25)
> > -#define DSIM_BURST_MODE			(1 << 26)
> > -#define DSIM_SYNC_INFORM		(1 << 27)
> > -#define DSIM_EOT_DISABLE		(1 << 28)
> > -#define DSIM_MFLUSH_VS			(1 << 29)
> > -/* This flag is valid only for exynos3250/3472/5260/5430 */
> > -#define DSIM_CLKLANE_STOP		(1 << 30)
> > -
> > -/* DSIM_ESCMODE */
> > -#define DSIM_TX_TRIGGER_RST		(1 << 4)
> > -#define DSIM_TX_LPDT_LP			(1 << 6)
> > -#define DSIM_CMD_LPDT_LP		(1 << 7)
> > -#define DSIM_FORCE_BTA			(1 << 16)
> > -#define DSIM_FORCE_STOP_STATE		(1 << 20)
> > -#define DSIM_STOP_STATE_CNT(x)		(((x) & 0x7ff) << 21)
> > -#define DSIM_STOP_STATE_CNT_MASK	(0x7ff << 21)
> > -
> > -/* DSIM_MDRESOL */
> > -#define DSIM_MAIN_STAND_BY		(1 << 31)
> > -#define DSIM_MAIN_VRESOL(x, num_bits)	(((x) & ((1 << (num_bits)) - 1)) << 16)
> > -#define DSIM_MAIN_HRESOL(x, num_bits)	(((x) & ((1 << (num_bits)) - 1)) << 0)
> > -
> > -/* DSIM_MVPORCH */
> > -#define DSIM_CMD_ALLOW(x)		((x) << 28)
> > -#define DSIM_STABLE_VFP(x)		((x) << 16)
> > -#define DSIM_MAIN_VBP(x)		((x) << 0)
> > -#define DSIM_CMD_ALLOW_MASK		(0xf << 28)
> > -#define DSIM_STABLE_VFP_MASK		(0x7ff << 16)
> > -#define DSIM_MAIN_VBP_MASK		(0x7ff << 0)
> > -
> > -/* DSIM_MHPORCH */
> > -#define DSIM_MAIN_HFP(x)		((x) << 16)
> > -#define DSIM_MAIN_HBP(x)		((x) << 0)
> > -#define DSIM_MAIN_HFP_MASK		((0xffff) << 16)
> > -#define DSIM_MAIN_HBP_MASK		((0xffff) << 0)
> > -
> > -/* DSIM_MSYNC */
> > -#define DSIM_MAIN_VSA(x)		((x) << 22)
> > -#define DSIM_MAIN_HSA(x)		((x) << 0)
> > -#define DSIM_MAIN_VSA_MASK		((0x3ff) << 22)
> > -#define DSIM_MAIN_HSA_MASK		((0xffff) << 0)
> > -
> > -/* DSIM_SDRESOL */
> > -#define DSIM_SUB_STANDY(x)		((x) << 31)
> > -#define DSIM_SUB_VRESOL(x)		((x) << 16)
> > -#define DSIM_SUB_HRESOL(x)		((x) << 0)
> > -#define DSIM_SUB_STANDY_MASK		((0x1) << 31)
> > -#define DSIM_SUB_VRESOL_MASK		((0x7ff) << 16)
> > -#define DSIM_SUB_HRESOL_MASK		((0x7ff) << 0)
> > -
> > -/* DSIM_INTSRC */
> > -#define DSIM_INT_PLL_STABLE		(1 << 31)
> > -#define DSIM_INT_SW_RST_RELEASE		(1 << 30)
> > -#define DSIM_INT_SFR_FIFO_EMPTY		(1 << 29)
> > -#define DSIM_INT_SFR_HDR_FIFO_EMPTY	(1 << 28)
> > -#define DSIM_INT_BTA			(1 << 25)
> > -#define DSIM_INT_FRAME_DONE		(1 << 24)
> > -#define DSIM_INT_RX_TIMEOUT		(1 << 21)
> > -#define DSIM_INT_BTA_TIMEOUT		(1 << 20)
> > -#define DSIM_INT_RX_DONE		(1 << 18)
> > -#define DSIM_INT_RX_TE			(1 << 17)
> > -#define DSIM_INT_RX_ACK			(1 << 16)
> > -#define DSIM_INT_RX_ECC_ERR		(1 << 15)
> > -#define DSIM_INT_RX_CRC_ERR		(1 << 14)
> > +#include "exynos_drm_crtc.h"
> > +#include "exynos_drm_drv.h"
> >  
> > -/* DSIM_FIFOCTRL */
> > -#define DSIM_RX_DATA_FULL		(1 << 25)
> > -#define DSIM_RX_DATA_EMPTY		(1 << 24)
> > -#define DSIM_SFR_HEADER_FULL		(1 << 23)
> > -#define DSIM_SFR_HEADER_EMPTY		(1 << 22)
> > -#define DSIM_SFR_PAYLOAD_FULL		(1 << 21)
> > -#define DSIM_SFR_PAYLOAD_EMPTY		(1 << 20)
> > -#define DSIM_I80_HEADER_FULL		(1 << 19)
> > -#define DSIM_I80_HEADER_EMPTY		(1 << 18)
> > -#define DSIM_I80_PAYLOAD_FULL		(1 << 17)
> > -#define DSIM_I80_PAYLOAD_EMPTY		(1 << 16)
> > -#define DSIM_SD_HEADER_FULL		(1 << 15)
> > -#define DSIM_SD_HEADER_EMPTY		(1 << 14)
> > -#define DSIM_SD_PAYLOAD_FULL		(1 << 13)
> > -#define DSIM_SD_PAYLOAD_EMPTY		(1 << 12)
> > -#define DSIM_MD_HEADER_FULL		(1 << 11)
> > -#define DSIM_MD_HEADER_EMPTY		(1 << 10)
> > -#define DSIM_MD_PAYLOAD_FULL		(1 << 9)
> > -#define DSIM_MD_PAYLOAD_EMPTY		(1 << 8)
> > -#define DSIM_RX_FIFO			(1 << 4)
> > -#define DSIM_SFR_FIFO			(1 << 3)
> > -#define DSIM_I80_FIFO			(1 << 2)
> > -#define DSIM_SD_FIFO			(1 << 1)
> > -#define DSIM_MD_FIFO			(1 << 0)
> > -
> > -/* DSIM_PHYACCHR */
> > -#define DSIM_AFC_EN			(1 << 14)
> > -#define DSIM_AFC_CTL(x)			(((x) & 0x7) << 5)
> > -
> > -/* DSIM_PLLCTRL */
> > -#define DSIM_FREQ_BAND(x)		((x) << 24)
> > -#define DSIM_PLL_EN			(1 << 23)
> > -#define DSIM_PLL_P(x)			((x) << 13)
> > -#define DSIM_PLL_M(x)			((x) << 4)
> > -#define DSIM_PLL_S(x)			((x) << 1)
> > -
> > -/* DSIM_PHYCTRL */
> > -#define DSIM_PHYCTRL_ULPS_EXIT(x)	(((x) & 0x1ff) << 0)
> > -#define DSIM_PHYCTRL_B_DPHYCTL_VREG_LP	(1 << 30)
> > -#define DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP	(1 << 14)
> > -
> > -/* DSIM_PHYTIMING */
> > -#define DSIM_PHYTIMING_LPX(x)		((x) << 8)
> > -#define DSIM_PHYTIMING_HS_EXIT(x)	((x) << 0)
> > -
> > -/* DSIM_PHYTIMING1 */
> > -#define DSIM_PHYTIMING1_CLK_PREPARE(x)	((x) << 24)
> > -#define DSIM_PHYTIMING1_CLK_ZERO(x)	((x) << 16)
> > -#define DSIM_PHYTIMING1_CLK_POST(x)	((x) << 8)
> > -#define DSIM_PHYTIMING1_CLK_TRAIL(x)	((x) << 0)
> > -
> > -/* DSIM_PHYTIMING2 */
> > -#define DSIM_PHYTIMING2_HS_PREPARE(x)	((x) << 16)
> > -#define DSIM_PHYTIMING2_HS_ZERO(x)	((x) << 8)
> > -#define DSIM_PHYTIMING2_HS_TRAIL(x)	((x) << 0)
> > -
> > -#define DSI_MAX_BUS_WIDTH		4
> > -#define DSI_NUM_VIRTUAL_CHANNELS	4
> > -#define DSI_TX_FIFO_SIZE		2048
> > -#define DSI_RX_FIFO_SIZE		256
> > -#define DSI_XFER_TIMEOUT_MS		100
> > -#define DSI_RX_FIFO_EMPTY		0x30800002
> > -
> > -#define OLD_SCLK_MIPI_CLK_NAME "pll_clk"
> > -
> > -static const char *const clk_names[5] = { "bus_clk", "sclk_mipi",
> > -	"phyclk_mipidphy0_bitclkdiv8", "phyclk_mipidphy0_rxclkesc0",
> > -	"sclk_rgb_vclk_to_dsim0" };
> > -
> > -enum exynos_dsi_transfer_type {
> > -	EXYNOS_DSI_TX,
> > -	EXYNOS_DSI_RX,
> > +enum {
> > +	DSI_PORT_IN,
> > +	DSI_PORT_OUT
> >  };
> >  
> > -struct exynos_dsi_transfer {
> > -	struct list_head list;
> > -	struct completion completed;
> > -	int result;
> > -	struct mipi_dsi_packet packet;
> > -	u16 flags;
> > -	u16 tx_done;
> > -
> > -	u8 *rx_payload;
> > -	u16 rx_len;
> > -	u16 rx_done;
> > -};
> > -
> > -#define DSIM_STATE_ENABLED		BIT(0)
> > -#define DSIM_STATE_INITIALIZED		BIT(1)
> > -#define DSIM_STATE_CMD_LPM		BIT(2)
> > -#define DSIM_STATE_VIDOUT_AVAILABLE	BIT(3)
> > -
> >  struct exynos_dsi {
> > -	struct drm_bridge bridge;
> > -	struct mipi_dsi_host dsi_host;
> > -	struct drm_connector connector;
> > -	struct drm_panel *panel;
> > -	struct list_head bridge_chain;
> > -	struct drm_bridge *out_bridge;
> > -	struct device *dev;
> > -
> > -	void __iomem *reg_base;
> > -	struct phy *phy;
> > -	struct clk **clks;
> > -	struct regulator_bulk_data supplies[2];
> > -	int irq;
> > -	int te_gpio;
> > -
> > -	u32 pll_clk_rate;
> > -	u32 burst_clk_rate;
> > -	u32 esc_clk_rate;
> > -	u32 lanes;
> > -	u32 mode_flags;
> > -	u32 format;
> > -
> > -	struct drm_display_mode mode;
> > -
> > -	int state;
> > -	struct drm_property *brightness;
> > -	struct completion completed;
> > -
> > -	spinlock_t transfer_lock; /* protects transfer_list */
> > -	struct list_head transfer_list;
> > -
> > -	const struct exynos_dsi_driver_data *driver_data;
> > +	struct samsung_dsim *dsi;
> > +	struct drm_encoder encoder;
> >  };
> >  
> > -#define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host)
> > -#define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector)
> > -
> > -enum reg_idx {
> > -	DSIM_STATUS_REG,	/* Status register */
> > -	DSIM_SWRST_REG,		/* Software reset register */
> > -	DSIM_CLKCTRL_REG,	/* Clock control register */
> > -	DSIM_TIMEOUT_REG,	/* Time out register */
> > -	DSIM_CONFIG_REG,	/* Configuration register */
> > -	DSIM_ESCMODE_REG,	/* Escape mode register */
> > -	DSIM_MDRESOL_REG,
> > -	DSIM_MVPORCH_REG,	/* Main display Vporch register */
> > -	DSIM_MHPORCH_REG,	/* Main display Hporch register */
> > -	DSIM_MSYNC_REG,		/* Main display sync area register */
> > -	DSIM_INTSRC_REG,	/* Interrupt source register */
> > -	DSIM_INTMSK_REG,	/* Interrupt mask register */
> > -	DSIM_PKTHDR_REG,	/* Packet Header FIFO register */
> > -	DSIM_PAYLOAD_REG,	/* Payload FIFO register */
> > -	DSIM_RXFIFO_REG,	/* Read FIFO register */
> > -	DSIM_FIFOCTRL_REG,	/* FIFO status and control register */
> > -	DSIM_PLLCTRL_REG,	/* PLL control register */
> > -	DSIM_PHYCTRL_REG,
> > -	DSIM_PHYTIMING_REG,
> > -	DSIM_PHYTIMING1_REG,
> > -	DSIM_PHYTIMING2_REG,
> > -	NUM_REGS
> > +static const unsigned int reg_values[] = {
> > +	[RESET_TYPE] = DSIM_SWRST,
> > +	[PLL_TIMER] = 500,
> > +	[STOP_STATE_CNT] = 0xf,
> > +	[PHYCTRL_ULPS_EXIT] = 0x0af,
> > +	[PHYCTRL_VREG_LP] = 0,
> > +	[PHYCTRL_SLEW_UP] = 0,
> > +	[PHYTIMING_LPX] = 0x06,
> > +	[PHYTIMING_HS_EXIT] = 0x0b,
> > +	[PHYTIMING_CLK_PREPARE] = 0x07,
> > +	[PHYTIMING_CLK_ZERO] = 0x27,
> > +	[PHYTIMING_CLK_POST] = 0x0d,
> > +	[PHYTIMING_CLK_TRAIL] = 0x08,
> > +	[PHYTIMING_HS_PREPARE] = 0x09,
> > +	[PHYTIMING_HS_ZERO] = 0x0d,
> > +	[PHYTIMING_HS_TRAIL] = 0x0b,
> >  };
> >  
> > -static const unsigned int exynos_reg_ofs[] = {
> > -	[DSIM_STATUS_REG] =  0x00,
> > -	[DSIM_SWRST_REG] =  0x04,
> > -	[DSIM_CLKCTRL_REG] =  0x08,
> > -	[DSIM_TIMEOUT_REG] =  0x0c,
> > -	[DSIM_CONFIG_REG] =  0x10,
> > -	[DSIM_ESCMODE_REG] =  0x14,
> > -	[DSIM_MDRESOL_REG] =  0x18,
> > -	[DSIM_MVPORCH_REG] =  0x1c,
> > -	[DSIM_MHPORCH_REG] =  0x20,
> > -	[DSIM_MSYNC_REG] =  0x24,
> > -	[DSIM_INTSRC_REG] =  0x2c,
> > -	[DSIM_INTMSK_REG] =  0x30,
> > -	[DSIM_PKTHDR_REG] =  0x34,
> > -	[DSIM_PAYLOAD_REG] =  0x38,
> > -	[DSIM_RXFIFO_REG] =  0x3c,
> > -	[DSIM_FIFOCTRL_REG] =  0x44,
> > -	[DSIM_PLLCTRL_REG] =  0x4c,
> > -	[DSIM_PHYCTRL_REG] =  0x5c,
> > -	[DSIM_PHYTIMING_REG] =  0x64,
> > -	[DSIM_PHYTIMING1_REG] =  0x68,
> > -	[DSIM_PHYTIMING2_REG] =  0x6c,
> > +static const unsigned int exynos5422_reg_values[] = {
> > +	[RESET_TYPE] = DSIM_SWRST,
> > +	[PLL_TIMER] = 500,
> > +	[STOP_STATE_CNT] = 0xf,
> > +	[PHYCTRL_ULPS_EXIT] = 0xaf,
> > +	[PHYCTRL_VREG_LP] = 0,
> > +	[PHYCTRL_SLEW_UP] = 0,
> > +	[PHYTIMING_LPX] = 0x08,
> > +	[PHYTIMING_HS_EXIT] = 0x0d,
> > +	[PHYTIMING_CLK_PREPARE] = 0x09,
> > +	[PHYTIMING_CLK_ZERO] = 0x30,
> > +	[PHYTIMING_CLK_POST] = 0x0e,
> > +	[PHYTIMING_CLK_TRAIL] = 0x0a,
> > +	[PHYTIMING_HS_PREPARE] = 0x0c,
> > +	[PHYTIMING_HS_ZERO] = 0x11,
> > +	[PHYTIMING_HS_TRAIL] = 0x0d,
> >  };
> >  
> > -static const unsigned int exynos5433_reg_ofs[] = {
> > -	[DSIM_STATUS_REG] = 0x04,
> > -	[DSIM_SWRST_REG] = 0x0C,
> > -	[DSIM_CLKCTRL_REG] = 0x10,
> > -	[DSIM_TIMEOUT_REG] = 0x14,
> > -	[DSIM_CONFIG_REG] = 0x18,
> > -	[DSIM_ESCMODE_REG] = 0x1C,
> > -	[DSIM_MDRESOL_REG] = 0x20,
> > -	[DSIM_MVPORCH_REG] = 0x24,
> > -	[DSIM_MHPORCH_REG] = 0x28,
> > -	[DSIM_MSYNC_REG] = 0x2C,
> > -	[DSIM_INTSRC_REG] = 0x34,
> > -	[DSIM_INTMSK_REG] = 0x38,
> > -	[DSIM_PKTHDR_REG] = 0x3C,
> > -	[DSIM_PAYLOAD_REG] = 0x40,
> > -	[DSIM_RXFIFO_REG] = 0x44,
> > -	[DSIM_FIFOCTRL_REG] = 0x4C,
> > -	[DSIM_PLLCTRL_REG] = 0x94,
> > -	[DSIM_PHYCTRL_REG] = 0xA4,
> > -	[DSIM_PHYTIMING_REG] = 0xB4,
> > -	[DSIM_PHYTIMING1_REG] = 0xB8,
> > -	[DSIM_PHYTIMING2_REG] = 0xBC,
> > +static const unsigned int exynos5433_reg_values[] = {
> > +	[RESET_TYPE] = DSIM_FUNCRST,
> > +	[PLL_TIMER] = 22200,
> > +	[STOP_STATE_CNT] = 0xa,
> > +	[PHYCTRL_ULPS_EXIT] = 0x190,
> > +	[PHYCTRL_VREG_LP] = 1,
> > +	[PHYCTRL_SLEW_UP] = 1,
> > +	[PHYTIMING_LPX] = 0x07,
> > +	[PHYTIMING_HS_EXIT] = 0x0c,
> > +	[PHYTIMING_CLK_PREPARE] = 0x09,
> > +	[PHYTIMING_CLK_ZERO] = 0x2d,
> > +	[PHYTIMING_CLK_POST] = 0x0e,
> > +	[PHYTIMING_CLK_TRAIL] = 0x09,
> > +	[PHYTIMING_HS_PREPARE] = 0x0b,
> > +	[PHYTIMING_HS_ZERO] = 0x10,
> > +	[PHYTIMING_HS_TRAIL] = 0x0c,
> >  };
> >  
> > -static inline void exynos_dsi_write(struct exynos_dsi *dsi, enum reg_idx idx,
> > -				    u32 val)
> > -{
> > -	const unsigned int *reg_ofs;
> > -
> > -	if (dsi->driver_data->reg_ofs == EXYNOS5433_REG_OFS)
> > -		reg_ofs = exynos5433_reg_ofs;
> > -	else
> > -		reg_ofs = exynos_reg_ofs;
> > -
> > -	writel(val, dsi->reg_base + reg_ofs[idx]);
> > -}
> > -
> > -static inline u32 exynos_dsi_read(struct exynos_dsi *dsi, enum reg_idx idx)
> > -{
> > -	const unsigned int *reg_ofs;
> > -
> > -	if (dsi->driver_data->reg_ofs == EXYNOS5433_REG_OFS)
> > -		reg_ofs = exynos5433_reg_ofs;
> > -	else
> > -		reg_ofs = exynos_reg_ofs;
> > -
> > -	return readl(dsi->reg_base + reg_ofs[idx]);
> > -}
> > -
> > -static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi)
> > -{
> > -	if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300)))
> > -		return;
> > -
> > -	dev_err(dsi->dev, "timeout waiting for reset\n");
> > -}
> > -
> > -static void exynos_dsi_reset(struct exynos_dsi *dsi)
> > -{
> > -	u32 reset_val = dsi->driver_data->reg_values[RESET_TYPE];
> > -
> > -	reinit_completion(&dsi->completed);
> > -	exynos_dsi_write(dsi, DSIM_SWRST_REG, reset_val);
> > -}
> > -
> > -#ifndef MHZ
> > -#define MHZ	(1000*1000)
> > -#endif
> > -
> > -static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi,
> > -		unsigned long fin, unsigned long fout, u8 *p, u16 *m, u8 *s)
> > -{
> > -	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
> > -	unsigned long best_freq = 0;
> > -	u32 min_delta = 0xffffffff;
> > -	u8 p_min, p_max;
> > -	u8 _p, best_p;
> > -	u16 _m, best_m;
> > -	u8 _s, best_s;
> > -
> > -	p_min = DIV_ROUND_UP(fin, (12 * MHZ));
> > -	p_max = fin / (6 * MHZ);
> > -
> > -	for (_p = p_min; _p <= p_max; ++_p) {
> > -		for (_s = 0; _s <= 5; ++_s) {
> > -			u64 tmp;
> > -			u32 delta;
> > -
> > -			tmp = (u64)fout * (_p << _s);
> > -			do_div(tmp, fin);
> > -			_m = tmp;
> > -			if (_m < 41 || _m > 125)
> > -				continue;
> > -
> > -			tmp = (u64)_m * fin;
> > -			do_div(tmp, _p);
> > -			if (tmp < 500 * MHZ ||
> > -					tmp > driver_data->max_freq * MHZ)
> > -				continue;
> > -
> > -			tmp = (u64)_m * fin;
> > -			do_div(tmp, _p << _s);
> > -
> > -			delta = abs(fout - tmp);
> > -			if (delta < min_delta) {
> > -				best_p = _p;
> > -				best_m = _m;
> > -				best_s = _s;
> > -				min_delta = delta;
> > -				best_freq = tmp;
> > -			}
> > -		}
> > -	}
> > -
> > -	if (best_freq) {
> > -		*p = best_p;
> > -		*m = best_m;
> > -		*s = best_s;
> > -	}
> > -
> > -	return best_freq;
> > -}
> > -
> > -static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi,
> > -					unsigned long freq)
> > -{
> > -	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
> > -	unsigned long fin, fout;
> > -	int timeout;
> > -	u8 p, s;
> > -	u16 m;
> > -	u32 reg;
> > -
> > -	fin = dsi->pll_clk_rate;
> > -	fout = exynos_dsi_pll_find_pms(dsi, fin, freq, &p, &m, &s);
> > -	if (!fout) {
> > -		dev_err(dsi->dev,
> > -			"failed to find PLL PMS for requested frequency\n");
> > -		return 0;
> > -	}
> > -	dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s);
> > -
> > -	writel(driver_data->reg_values[PLL_TIMER],
> > -			dsi->reg_base + driver_data->plltmr_reg);
> > -
> > -	reg = DSIM_PLL_EN | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s);
> > -
> > -	if (driver_data->has_freqband) {
> > -		static const unsigned long freq_bands[] = {
> > -			100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ,
> > -			270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ,
> > -			510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ,
> > -			770 * MHZ, 870 * MHZ, 950 * MHZ,
> > -		};
> > -		int band;
> > -
> > -		for (band = 0; band < ARRAY_SIZE(freq_bands); ++band)
> > -			if (fout < freq_bands[band])
> > -				break;
> > -
> > -		dev_dbg(dsi->dev, "band %d\n", band);
> > -
> > -		reg |= DSIM_FREQ_BAND(band);
> > -	}
> > -
> > -	exynos_dsi_write(dsi, DSIM_PLLCTRL_REG, reg);
> > -
> > -	timeout = 1000;
> > -	do {
> > -		if (timeout-- == 0) {
> > -			dev_err(dsi->dev, "PLL failed to stabilize\n");
> > -			return 0;
> > -		}
> > -		reg = exynos_dsi_read(dsi, DSIM_STATUS_REG);
> > -	} while ((reg & DSIM_PLL_STABLE) == 0);
> > -
> > -	return fout;
> > -}
> > -
> > -static int exynos_dsi_enable_clock(struct exynos_dsi *dsi)
> > -{
> > -	unsigned long hs_clk, byte_clk, esc_clk;
> > -	unsigned long esc_div;
> > -	u32 reg;
> > -
> > -	hs_clk = exynos_dsi_set_pll(dsi, dsi->burst_clk_rate);
> > -	if (!hs_clk) {
> > -		dev_err(dsi->dev, "failed to configure DSI PLL\n");
> > -		return -EFAULT;
> > -	}
> > -
> > -	byte_clk = hs_clk / 8;
> > -	esc_div = DIV_ROUND_UP(byte_clk, dsi->esc_clk_rate);
> > -	esc_clk = byte_clk / esc_div;
> > -
> > -	if (esc_clk > 20 * MHZ) {
> > -		++esc_div;
> > -		esc_clk = byte_clk / esc_div;
> > -	}
> > -
> > -	dev_dbg(dsi->dev, "hs_clk = %lu, byte_clk = %lu, esc_clk = %lu\n",
> > -		hs_clk, byte_clk, esc_clk);
> > -
> > -	reg = exynos_dsi_read(dsi, DSIM_CLKCTRL_REG);
> > -	reg &= ~(DSIM_ESC_PRESCALER_MASK | DSIM_LANE_ESC_CLK_EN_CLK
> > -			| DSIM_LANE_ESC_CLK_EN_DATA_MASK | DSIM_PLL_BYPASS
> > -			| DSIM_BYTE_CLK_SRC_MASK);
> > -	reg |= DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN
> > -			| DSIM_ESC_PRESCALER(esc_div)
> > -			| DSIM_LANE_ESC_CLK_EN_CLK
> > -			| DSIM_LANE_ESC_CLK_EN_DATA(BIT(dsi->lanes) - 1)
> > -			| DSIM_BYTE_CLK_SRC(0)
> > -			| DSIM_TX_REQUEST_HSCLK;
> > -	exynos_dsi_write(dsi, DSIM_CLKCTRL_REG, reg);
> > -
> > -	return 0;
> > -}
> > -
> > -static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi)
> > -{
> > -	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
> > -	const unsigned int *reg_values = driver_data->reg_values;
> > -	u32 reg;
> > -
> > -	if (driver_data->has_freqband)
> > -		return;
> > -
> > -	/* B D-PHY: D-PHY Master & Slave Analog Block control */
> > -	reg = DSIM_PHYCTRL_ULPS_EXIT(reg_values[PHYCTRL_ULPS_EXIT]);
> > -	if (reg_values[PHYCTRL_VREG_LP])
> > -		reg |= DSIM_PHYCTRL_B_DPHYCTL_VREG_LP;
> > -	if (reg_values[PHYCTRL_SLEW_UP])
> > -		reg |= DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP;
> > -	exynos_dsi_write(dsi, DSIM_PHYCTRL_REG, reg);
> > -
> > -	/*
> > -	 * T LPX: Transmitted length of any Low-Power state period
> > -	 * T HS-EXIT: Time that the transmitter drives LP-11 following a HS
> > -	 *	burst
> > -	 */
> > -	reg = DSIM_PHYTIMING_LPX(reg_values[PHYTIMING_LPX]) |
> > -		DSIM_PHYTIMING_HS_EXIT(reg_values[PHYTIMING_HS_EXIT]);
> > -	exynos_dsi_write(dsi, DSIM_PHYTIMING_REG, reg);
> > -
> > -	/*
> > -	 * T CLK-PREPARE: Time that the transmitter drives the Clock Lane LP-00
> > -	 *	Line state immediately before the HS-0 Line state starting the
> > -	 *	HS transmission
> > -	 * T CLK-ZERO: Time that the transmitter drives the HS-0 state prior to
> > -	 *	transmitting the Clock.
> > -	 * T CLK_POST: Time that the transmitter continues to send HS clock
> > -	 *	after the last associated Data Lane has transitioned to LP Mode
> > -	 *	Interval is defined as the period from the end of T HS-TRAIL to
> > -	 *	the beginning of T CLK-TRAIL
> > -	 * T CLK-TRAIL: Time that the transmitter drives the HS-0 state after
> > -	 *	the last payload clock bit of a HS transmission burst
> > -	 */
> > -	reg = DSIM_PHYTIMING1_CLK_PREPARE(reg_values[PHYTIMING_CLK_PREPARE]) |
> > -		DSIM_PHYTIMING1_CLK_ZERO(reg_values[PHYTIMING_CLK_ZERO]) |
> > -		DSIM_PHYTIMING1_CLK_POST(reg_values[PHYTIMING_CLK_POST]) |
> > -		DSIM_PHYTIMING1_CLK_TRAIL(reg_values[PHYTIMING_CLK_TRAIL]);
> > -	exynos_dsi_write(dsi, DSIM_PHYTIMING1_REG, reg);
> > -
> > -	/*
> > -	 * T HS-PREPARE: Time that the transmitter drives the Data Lane LP-00
> > -	 *	Line state immediately before the HS-0 Line state starting the
> > -	 *	HS transmission
> > -	 * T HS-ZERO: Time that the transmitter drives the HS-0 state prior to
> > -	 *	transmitting the Sync sequence.
> > -	 * T HS-TRAIL: Time that the transmitter drives the flipped differential
> > -	 *	state after last payload data bit of a HS transmission burst
> > -	 */
> > -	reg = DSIM_PHYTIMING2_HS_PREPARE(reg_values[PHYTIMING_HS_PREPARE]) |
> > -		DSIM_PHYTIMING2_HS_ZERO(reg_values[PHYTIMING_HS_ZERO]) |
> > -		DSIM_PHYTIMING2_HS_TRAIL(reg_values[PHYTIMING_HS_TRAIL]);
> > -	exynos_dsi_write(dsi, DSIM_PHYTIMING2_REG, reg);
> > -}
> > -
> > -static void exynos_dsi_disable_clock(struct exynos_dsi *dsi)
> > -{
> > -	u32 reg;
> > -
> > -	reg = exynos_dsi_read(dsi, DSIM_CLKCTRL_REG);
> > -	reg &= ~(DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK
> > -			| DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN);
> > -	exynos_dsi_write(dsi, DSIM_CLKCTRL_REG, reg);
> > -
> > -	reg = exynos_dsi_read(dsi, DSIM_PLLCTRL_REG);
> > -	reg &= ~DSIM_PLL_EN;
> > -	exynos_dsi_write(dsi, DSIM_PLLCTRL_REG, reg);
> > -}
> > -
> > -static void exynos_dsi_enable_lane(struct exynos_dsi *dsi, u32 lane)
> > -{
> > -	u32 reg = exynos_dsi_read(dsi, DSIM_CONFIG_REG);
> > -	reg |= (DSIM_NUM_OF_DATA_LANE(dsi->lanes - 1) | DSIM_LANE_EN_CLK |
> > -			DSIM_LANE_EN(lane));
> > -	exynos_dsi_write(dsi, DSIM_CONFIG_REG, reg);
> > -}
> > -
> > -static int exynos_dsi_init_link(struct exynos_dsi *dsi)
> > +static int exynos_dsi_host_attach(struct device *dev,
> > +				  struct mipi_dsi_device *device)
> >  {
> > -	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
> > -	int timeout;
> > -	u32 reg;
> > -	u32 lanes_mask;
> > -
> > -	/* Initialize FIFO pointers */
> > -	reg = exynos_dsi_read(dsi, DSIM_FIFOCTRL_REG);
> > -	reg &= ~0x1f;
> > -	exynos_dsi_write(dsi, DSIM_FIFOCTRL_REG, reg);
> > -
> > -	usleep_range(9000, 11000);
> > -
> > -	reg |= 0x1f;
> > -	exynos_dsi_write(dsi, DSIM_FIFOCTRL_REG, reg);
> > -	usleep_range(9000, 11000);
> > +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> > +	struct drm_device *drm = dsi->encoder.dev;
> > +	struct exynos_drm_crtc *crtc;
> >  
> > -	/* DSI configuration */
> > -	reg = 0;
> > +	mutex_lock(&drm->mode_config.mutex);
> > +	crtc = exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD);
> > +	crtc->i80_mode = !(device->mode_flags & MIPI_DSI_MODE_VIDEO);
> > +	mutex_unlock(&drm->mode_config.mutex);
> >  
> > -	/*
> > -	 * The first bit of mode_flags specifies display configuration.
> > -	 * If this bit is set[= MIPI_DSI_MODE_VIDEO], dsi will support video
> > -	 * mode, otherwise it will support command mode.
> > -	 */
> > -	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> > -		reg |= DSIM_VIDEO_MODE;
> > -
> > -		/*
> > -		 * The user manual describes that following bits are ignored in
> > -		 * command mode.
> > -		 */
> > -		if (!(dsi->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH))
> > -			reg |= DSIM_MFLUSH_VS;
> > -		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
> > -			reg |= DSIM_SYNC_INFORM;
> > -		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
> > -			reg |= DSIM_BURST_MODE;
> > -		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT)
> > -			reg |= DSIM_AUTO_MODE;
> > -		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSE)
> > -			reg |= DSIM_HSE_MODE;
> > -		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HFP))
> > -			reg |= DSIM_HFP_MODE;
> > -		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HBP))
> > -			reg |= DSIM_HBP_MODE;
> > -		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSA))
> > -			reg |= DSIM_HSA_MODE;
> > -	}
> > -
> > -	if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET))
> > -		reg |= DSIM_EOT_DISABLE;
> > -
> > -	switch (dsi->format) {
> > -	case MIPI_DSI_FMT_RGB888:
> > -		reg |= DSIM_MAIN_PIX_FORMAT_RGB888;
> > -		break;
> > -	case MIPI_DSI_FMT_RGB666:
> > -		reg |= DSIM_MAIN_PIX_FORMAT_RGB666;
> > -		break;
> > -	case MIPI_DSI_FMT_RGB666_PACKED:
> > -		reg |= DSIM_MAIN_PIX_FORMAT_RGB666_P;
> > -		break;
> > -	case MIPI_DSI_FMT_RGB565:
> > -		reg |= DSIM_MAIN_PIX_FORMAT_RGB565;
> > -		break;
> > -	default:
> > -		dev_err(dsi->dev, "invalid pixel format\n");
> > -		return -EINVAL;
> > -	}
> > -
> > -	/*
> > -	 * Use non-continuous clock mode if the periparal wants and
> > -	 * host controller supports
> > -	 *
> > -	 * In non-continous clock mode, host controller will turn off
> > -	 * the HS clock between high-speed transmissions to reduce
> > -	 * power consumption.
> > -	 */
> > -	if (driver_data->has_clklane_stop &&
> > -			dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
> > -		reg |= DSIM_CLKLANE_STOP;
> > -	}
> > -	exynos_dsi_write(dsi, DSIM_CONFIG_REG, reg);
> > -
> > -	lanes_mask = BIT(dsi->lanes) - 1;
> > -	exynos_dsi_enable_lane(dsi, lanes_mask);
> > -
> > -	/* Check clock and data lane state are stop state */
> > -	timeout = 100;
> > -	do {
> > -		if (timeout-- == 0) {
> > -			dev_err(dsi->dev, "waiting for bus lanes timed out\n");
> > -			return -EFAULT;
> > -		}
> > -
> > -		reg = exynos_dsi_read(dsi, DSIM_STATUS_REG);
> > -		if ((reg & DSIM_STOP_STATE_DAT(lanes_mask))
> > -		    != DSIM_STOP_STATE_DAT(lanes_mask))
> > -			continue;
> > -	} while (!(reg & (DSIM_STOP_STATE_CLK | DSIM_TX_READY_HS_CLK)));
> > -
> > -	reg = exynos_dsi_read(dsi, DSIM_ESCMODE_REG);
> > -	reg &= ~DSIM_STOP_STATE_CNT_MASK;
> > -	reg |= DSIM_STOP_STATE_CNT(driver_data->reg_values[STOP_STATE_CNT]);
> > -	exynos_dsi_write(dsi, DSIM_ESCMODE_REG, reg);
> > -
> > -	reg = DSIM_BTA_TIMEOUT(0xff) | DSIM_LPDR_TIMEOUT(0xffff);
> > -	exynos_dsi_write(dsi, DSIM_TIMEOUT_REG, reg);
> > +	if (drm->mode_config.poll_enabled)
> > +		drm_kms_helper_hotplug_event(drm);
> >  
> >  	return 0;
> >  }
> >  
> > -static void exynos_dsi_set_display_mode(struct exynos_dsi *dsi)
> > -{
> > -	struct drm_display_mode *m = &dsi->mode;
> > -	unsigned int num_bits_resol = dsi->driver_data->num_bits_resol;
> > -	u32 reg;
> > -
> > -	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> > -		reg = DSIM_CMD_ALLOW(0xf)
> > -			| DSIM_STABLE_VFP(m->vsync_start - m->vdisplay)
> > -			| DSIM_MAIN_VBP(m->vtotal - m->vsync_end);
> > -		exynos_dsi_write(dsi, DSIM_MVPORCH_REG, reg);
> > -
> > -		reg = DSIM_MAIN_HFP(m->hsync_start - m->hdisplay)
> > -			| DSIM_MAIN_HBP(m->htotal - m->hsync_end);
> > -		exynos_dsi_write(dsi, DSIM_MHPORCH_REG, reg);
> > -
> > -		reg = DSIM_MAIN_VSA(m->vsync_end - m->vsync_start)
> > -			| DSIM_MAIN_HSA(m->hsync_end - m->hsync_start);
> > -		exynos_dsi_write(dsi, DSIM_MSYNC_REG, reg);
> > -	}
> > -	reg =  DSIM_MAIN_HRESOL(m->hdisplay, num_bits_resol) |
> > -		DSIM_MAIN_VRESOL(m->vdisplay, num_bits_resol);
> > -
> > -	exynos_dsi_write(dsi, DSIM_MDRESOL_REG, reg);
> > -
> > -	dev_dbg(dsi->dev, "LCD size = %dx%d\n", m->hdisplay, m->vdisplay);
> > -}
> > -
> > -static void exynos_dsi_set_display_enable(struct exynos_dsi *dsi, bool enable)
> > -{
> > -	u32 reg;
> > -
> > -	reg = exynos_dsi_read(dsi, DSIM_MDRESOL_REG);
> > -	if (enable)
> > -		reg |= DSIM_MAIN_STAND_BY;
> > -	else
> > -		reg &= ~DSIM_MAIN_STAND_BY;
> > -	exynos_dsi_write(dsi, DSIM_MDRESOL_REG, reg);
> > -}
> > -
> > -static int exynos_dsi_wait_for_hdr_fifo(struct exynos_dsi *dsi)
> > -{
> > -	int timeout = 2000;
> > -
> > -	do {
> > -		u32 reg = exynos_dsi_read(dsi, DSIM_FIFOCTRL_REG);
> > -
> > -		if (!(reg & DSIM_SFR_HEADER_FULL))
> > -			return 0;
> > -
> > -		if (!cond_resched())
> > -			usleep_range(950, 1050);
> > -	} while (--timeout);
> > -
> > -	return -ETIMEDOUT;
> > -}
> > -
> > -static void exynos_dsi_set_cmd_lpm(struct exynos_dsi *dsi, bool lpm)
> > -{
> > -	u32 v = exynos_dsi_read(dsi, DSIM_ESCMODE_REG);
> > -
> > -	if (lpm)
> > -		v |= DSIM_CMD_LPDT_LP;
> > -	else
> > -		v &= ~DSIM_CMD_LPDT_LP;
> > -
> > -	exynos_dsi_write(dsi, DSIM_ESCMODE_REG, v);
> > -}
> > -
> > -static void exynos_dsi_force_bta(struct exynos_dsi *dsi)
> > -{
> > -	u32 v = exynos_dsi_read(dsi, DSIM_ESCMODE_REG);
> > -	v |= DSIM_FORCE_BTA;
> > -	exynos_dsi_write(dsi, DSIM_ESCMODE_REG, v);
> > -}
> > -
> > -static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi,
> > -					struct exynos_dsi_transfer *xfer)
> > -{
> > -	struct device *dev = dsi->dev;
> > -	struct mipi_dsi_packet *pkt = &xfer->packet;
> > -	const u8 *payload = pkt->payload + xfer->tx_done;
> > -	u16 length = pkt->payload_length - xfer->tx_done;
> > -	bool first = !xfer->tx_done;
> > -	u32 reg;
> > -
> > -	dev_dbg(dev, "< xfer %pK: tx len %u, done %u, rx len %u, done %u\n",
> > -		xfer, length, xfer->tx_done, xfer->rx_len, xfer->rx_done);
> > -
> > -	if (length > DSI_TX_FIFO_SIZE)
> > -		length = DSI_TX_FIFO_SIZE;
> > -
> > -	xfer->tx_done += length;
> > -
> > -	/* Send payload */
> > -	while (length >= 4) {
> > -		reg = get_unaligned_le32(payload);
> > -		exynos_dsi_write(dsi, DSIM_PAYLOAD_REG, reg);
> > -		payload += 4;
> > -		length -= 4;
> > -	}
> > -
> > -	reg = 0;
> > -	switch (length) {
> > -	case 3:
> > -		reg |= payload[2] << 16;
> > -		fallthrough;
> > -	case 2:
> > -		reg |= payload[1] << 8;
> > -		fallthrough;
> > -	case 1:
> > -		reg |= payload[0];
> > -		exynos_dsi_write(dsi, DSIM_PAYLOAD_REG, reg);
> > -		break;
> > -	}
> > -
> > -	/* Send packet header */
> > -	if (!first)
> > -		return;
> > -
> > -	reg = get_unaligned_le32(pkt->header);
> > -	if (exynos_dsi_wait_for_hdr_fifo(dsi)) {
> > -		dev_err(dev, "waiting for header FIFO timed out\n");
> > -		return;
> > -	}
> > -
> > -	if (NEQV(xfer->flags & MIPI_DSI_MSG_USE_LPM,
> > -		 dsi->state & DSIM_STATE_CMD_LPM)) {
> > -		exynos_dsi_set_cmd_lpm(dsi, xfer->flags & MIPI_DSI_MSG_USE_LPM);
> > -		dsi->state ^= DSIM_STATE_CMD_LPM;
> > -	}
> > -
> > -	exynos_dsi_write(dsi, DSIM_PKTHDR_REG, reg);
> > -
> > -	if (xfer->flags & MIPI_DSI_MSG_REQ_ACK)
> > -		exynos_dsi_force_bta(dsi);
> > -}
> > -
> > -static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi,
> > -					struct exynos_dsi_transfer *xfer)
> > -{
> > -	u8 *payload = xfer->rx_payload + xfer->rx_done;
> > -	bool first = !xfer->rx_done;
> > -	struct device *dev = dsi->dev;
> > -	u16 length;
> > -	u32 reg;
> > -
> > -	if (first) {
> > -		reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG);
> > -
> > -		switch (reg & 0x3f) {
> > -		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
> > -		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
> > -			if (xfer->rx_len >= 2) {
> > -				payload[1] = reg >> 16;
> > -				++xfer->rx_done;
> > -			}
> > -			fallthrough;
> > -		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
> > -		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
> > -			payload[0] = reg >> 8;
> > -			++xfer->rx_done;
> > -			xfer->rx_len = xfer->rx_done;
> > -			xfer->result = 0;
> > -			goto clear_fifo;
> > -		case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
> > -			dev_err(dev, "DSI Error Report: 0x%04x\n",
> > -				(reg >> 8) & 0xffff);
> > -			xfer->result = 0;
> > -			goto clear_fifo;
> > -		}
> > -
> > -		length = (reg >> 8) & 0xffff;
> > -		if (length > xfer->rx_len) {
> > -			dev_err(dev,
> > -				"response too long (%u > %u bytes), stripping\n",
> > -				xfer->rx_len, length);
> > -			length = xfer->rx_len;
> > -		} else if (length < xfer->rx_len)
> > -			xfer->rx_len = length;
> > -	}
> > -
> > -	length = xfer->rx_len - xfer->rx_done;
> > -	xfer->rx_done += length;
> > -
> > -	/* Receive payload */
> > -	while (length >= 4) {
> > -		reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG);
> > -		payload[0] = (reg >>  0) & 0xff;
> > -		payload[1] = (reg >>  8) & 0xff;
> > -		payload[2] = (reg >> 16) & 0xff;
> > -		payload[3] = (reg >> 24) & 0xff;
> > -		payload += 4;
> > -		length -= 4;
> > -	}
> > -
> > -	if (length) {
> > -		reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG);
> > -		switch (length) {
> > -		case 3:
> > -			payload[2] = (reg >> 16) & 0xff;
> > -			fallthrough;
> > -		case 2:
> > -			payload[1] = (reg >> 8) & 0xff;
> > -			fallthrough;
> > -		case 1:
> > -			payload[0] = reg & 0xff;
> > -		}
> > -	}
> > -
> > -	if (xfer->rx_done == xfer->rx_len)
> > -		xfer->result = 0;
> > -
> > -clear_fifo:
> > -	length = DSI_RX_FIFO_SIZE / 4;
> > -	do {
> > -		reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG);
> > -		if (reg == DSI_RX_FIFO_EMPTY)
> > -			break;
> > -	} while (--length);
> > -}
> > -
> > -static void exynos_dsi_transfer_start(struct exynos_dsi *dsi)
> > -{
> > -	unsigned long flags;
> > -	struct exynos_dsi_transfer *xfer;
> > -	bool start = false;
> > -
> > -again:
> > -	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > -
> > -	if (list_empty(&dsi->transfer_list)) {
> > -		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > -		return;
> > -	}
> > -
> > -	xfer = list_first_entry(&dsi->transfer_list,
> > -					struct exynos_dsi_transfer, list);
> > -
> > -	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > -
> > -	if (xfer->packet.payload_length &&
> > -	    xfer->tx_done == xfer->packet.payload_length)
> > -		/* waiting for RX */
> > -		return;
> > -
> > -	exynos_dsi_send_to_fifo(dsi, xfer);
> > -
> > -	if (xfer->packet.payload_length || xfer->rx_len)
> > -		return;
> > -
> > -	xfer->result = 0;
> > -	complete(&xfer->completed);
> > -
> > -	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > -
> > -	list_del_init(&xfer->list);
> > -	start = !list_empty(&dsi->transfer_list);
> > -
> > -	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > -
> > -	if (start)
> > -		goto again;
> > -}
> > -
> > -static bool exynos_dsi_transfer_finish(struct exynos_dsi *dsi)
> > -{
> > -	struct exynos_dsi_transfer *xfer;
> > -	unsigned long flags;
> > -	bool start = true;
> > -
> > -	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > -
> > -	if (list_empty(&dsi->transfer_list)) {
> > -		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > -		return false;
> > -	}
> > -
> > -	xfer = list_first_entry(&dsi->transfer_list,
> > -					struct exynos_dsi_transfer, list);
> > -
> > -	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > -
> > -	dev_dbg(dsi->dev,
> > -		"> xfer %pK, tx_len %zu, tx_done %u, rx_len %u, rx_done %u\n",
> > -		xfer, xfer->packet.payload_length, xfer->tx_done, xfer->rx_len,
> > -		xfer->rx_done);
> > -
> > -	if (xfer->tx_done != xfer->packet.payload_length)
> > -		return true;
> > -
> > -	if (xfer->rx_done != xfer->rx_len)
> > -		exynos_dsi_read_from_fifo(dsi, xfer);
> > -
> > -	if (xfer->rx_done != xfer->rx_len)
> > -		return true;
> > -
> > -	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > -
> > -	list_del_init(&xfer->list);
> > -	start = !list_empty(&dsi->transfer_list);
> > -
> > -	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > -
> > -	if (!xfer->rx_len)
> > -		xfer->result = 0;
> > -	complete(&xfer->completed);
> > -
> > -	return start;
> > -}
> > -
> > -static void exynos_dsi_remove_transfer(struct exynos_dsi *dsi,
> > -					struct exynos_dsi_transfer *xfer)
> > -{
> > -	unsigned long flags;
> > -	bool start;
> > -
> > -	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > -
> > -	if (!list_empty(&dsi->transfer_list) &&
> > -	    xfer == list_first_entry(&dsi->transfer_list,
> > -				     struct exynos_dsi_transfer, list)) {
> > -		list_del_init(&xfer->list);
> > -		start = !list_empty(&dsi->transfer_list);
> > -		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > -		if (start)
> > -			exynos_dsi_transfer_start(dsi);
> > -		return;
> > -	}
> > -
> > -	list_del_init(&xfer->list);
> > -
> > -	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > -}
> > -
> > -static int exynos_dsi_transfer(struct exynos_dsi *dsi,
> > -					struct exynos_dsi_transfer *xfer)
> > -{
> > -	unsigned long flags;
> > -	bool stopped;
> > -
> > -	xfer->tx_done = 0;
> > -	xfer->rx_done = 0;
> > -	xfer->result = -ETIMEDOUT;
> > -	init_completion(&xfer->completed);
> > -
> > -	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > -
> > -	stopped = list_empty(&dsi->transfer_list);
> > -	list_add_tail(&xfer->list, &dsi->transfer_list);
> > -
> > -	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > -
> > -	if (stopped)
> > -		exynos_dsi_transfer_start(dsi);
> > -
> > -	wait_for_completion_timeout(&xfer->completed,
> > -				    msecs_to_jiffies(DSI_XFER_TIMEOUT_MS));
> > -	if (xfer->result == -ETIMEDOUT) {
> > -		struct mipi_dsi_packet *pkt = &xfer->packet;
> > -		exynos_dsi_remove_transfer(dsi, xfer);
> > -		dev_err(dsi->dev, "xfer timed out: %*ph %*ph\n", 4, pkt->header,
> > -			(int)pkt->payload_length, pkt->payload);
> > -		return -ETIMEDOUT;
> > -	}
> > -
> > -	/* Also covers hardware timeout condition */
> > -	return xfer->result;
> > -}
> > -
> > -static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
> > -{
> > -	struct exynos_dsi *dsi = dev_id;
> > -	u32 status;
> > -
> > -	status = exynos_dsi_read(dsi, DSIM_INTSRC_REG);
> > -	if (!status) {
> > -		static unsigned long int j;
> > -		if (printk_timed_ratelimit(&j, 500))
> > -			dev_warn(dsi->dev, "spurious interrupt\n");
> > -		return IRQ_HANDLED;
> > -	}
> > -	exynos_dsi_write(dsi, DSIM_INTSRC_REG, status);
> > -
> > -	if (status & DSIM_INT_SW_RST_RELEASE) {
> > -		u32 mask = ~(DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY |
> > -			DSIM_INT_SFR_HDR_FIFO_EMPTY | DSIM_INT_RX_ECC_ERR |
> > -			DSIM_INT_SW_RST_RELEASE);
> > -		exynos_dsi_write(dsi, DSIM_INTMSK_REG, mask);
> > -		complete(&dsi->completed);
> > -		return IRQ_HANDLED;
> > -	}
> > -
> > -	if (!(status & (DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY |
> > -			DSIM_INT_PLL_STABLE)))
> > -		return IRQ_HANDLED;
> > -
> > -	if (exynos_dsi_transfer_finish(dsi))
> > -		exynos_dsi_transfer_start(dsi);
> > -
> > -	return IRQ_HANDLED;
> > -}
> > -
> > -static irqreturn_t exynos_dsi_te_irq_handler(int irq, void *dev_id)
> > -{
> > -	struct exynos_dsi *dsi = dev_id;
> > -	const struct exynos_dsi_host_ops *ops = dsi->driver_data->host_ops;
> > -
> > -	if (ops && ops->te_handler &&
> > -	    (dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
> > -		ops->te_handler(dsi->dsi_host.dev);
> > -
> > -	return IRQ_HANDLED;
> > -}
> > -
> > -static void exynos_dsi_enable_irq(struct exynos_dsi *dsi)
> > -{
> > -	enable_irq(dsi->irq);
> > -
> > -	if (gpio_is_valid(dsi->te_gpio))
> > -		enable_irq(gpio_to_irq(dsi->te_gpio));
> > -}
> > -
> > -static void exynos_dsi_disable_irq(struct exynos_dsi *dsi)
> > -{
> > -	if (gpio_is_valid(dsi->te_gpio))
> > -		disable_irq(gpio_to_irq(dsi->te_gpio));
> > -
> > -	disable_irq(dsi->irq);
> > -}
> > -
> > -static int exynos_dsi_init(struct exynos_dsi *dsi)
> > +static int exynos_dsi_host_detach(struct device *dev,
> > +				  struct mipi_dsi_device *device)
> >  {
> > -	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
> > -
> > -	exynos_dsi_reset(dsi);
> > -	exynos_dsi_enable_irq(dsi);
> > +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> > +	struct drm_device *drm = dsi->encoder.dev;
> >  
> > -	if (driver_data->reg_values[RESET_TYPE] == DSIM_FUNCRST)
> > -		exynos_dsi_enable_lane(dsi, BIT(dsi->lanes) - 1);
> > -
> > -	exynos_dsi_enable_clock(dsi);
> > -	if (driver_data->wait_for_reset)
> > -		exynos_dsi_wait_for_reset(dsi);
> > -	exynos_dsi_set_phy_ctrl(dsi);
> > -	exynos_dsi_init_link(dsi);
> > +	if (drm->mode_config.poll_enabled)
> > +		drm_kms_helper_hotplug_event(drm);
> >  
> >  	return 0;
> >  }
> >  
> > -static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi,
> > -				      struct device *panel)
> > +static void exynos_dsi_te_handler(struct device *dev)
> >  {
> > -	int ret;
> > -	int te_gpio_irq;
> > +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> >  
> > -	dsi->te_gpio = of_get_named_gpio(panel->of_node, "te-gpios", 0);
> > -	if (dsi->te_gpio == -ENOENT)
> > -		return 0;
> > -
> > -	if (!gpio_is_valid(dsi->te_gpio)) {
> > -		ret = dsi->te_gpio;
> > -		dev_err(dsi->dev, "cannot get te-gpios, %d\n", ret);
> > -		goto out;
> > -	}
> > -
> > -	ret = gpio_request(dsi->te_gpio, "te_gpio");
> > -	if (ret) {
> > -		dev_err(dsi->dev, "gpio request failed with %d\n", ret);
> > -		goto out;
> > -	}
> > -
> > -	te_gpio_irq = gpio_to_irq(dsi->te_gpio);
> > -	irq_set_status_flags(te_gpio_irq, IRQ_NOAUTOEN);
> > -
> > -	ret = request_threaded_irq(te_gpio_irq, exynos_dsi_te_irq_handler, NULL,
> > -					IRQF_TRIGGER_RISING, "TE", dsi);
> > -	if (ret) {
> > -		dev_err(dsi->dev, "request interrupt failed with %d\n", ret);
> > -		gpio_free(dsi->te_gpio);
> > -		goto out;
> > -	}
> > -
> > -out:
> > -	return ret;
> > +	exynos_drm_crtc_te_handler(dsi->encoder.crtc);
> >  }
> >  
> > -static void exynos_dsi_unregister_te_irq(struct exynos_dsi *dsi)
> > -{
> > -	if (gpio_is_valid(dsi->te_gpio)) {
> > -		free_irq(gpio_to_irq(dsi->te_gpio), dsi);
> > -		gpio_free(dsi->te_gpio);
> > -		dsi->te_gpio = -ENOENT;
> > -	}
> > -}
> > -
> > -static void exynos_dsi_enable(struct exynos_dsi *dsi)
> > -{
> > -	struct drm_bridge *iter;
> > -	int ret;
> > -
> > -	if (dsi->state & DSIM_STATE_ENABLED)
> > -		return;
> > -
> > -	pm_runtime_get_sync(dsi->dev);
> > -	dsi->state |= DSIM_STATE_ENABLED;
> > -
> > -	if (dsi->panel) {
> > -		ret = drm_panel_prepare(dsi->panel);
> > -		if (ret < 0)
> > -			goto err_put_sync;
> > -	} else {
> > -		list_for_each_entry_reverse(iter, &dsi->bridge_chain,
> > -					    chain_node) {
> > -			if (iter->funcs->pre_enable)
> > -				iter->funcs->pre_enable(iter);
> > -		}
> > -	}
> > -
> > -	exynos_dsi_set_display_mode(dsi);
> > -	exynos_dsi_set_display_enable(dsi, true);
> > -
> > -	if (dsi->panel) {
> > -		ret = drm_panel_enable(dsi->panel);
> > -		if (ret < 0)
> > -			goto err_display_disable;
> > -	} else {
> > -		list_for_each_entry(iter, &dsi->bridge_chain, chain_node) {
> > -			if (iter->funcs->enable)
> > -				iter->funcs->enable(iter);
> > -		}
> > -	}
> > -
> > -	dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
> > -	return;
> > -
> > -err_display_disable:
> > -	exynos_dsi_set_display_enable(dsi, false);
> > -	drm_panel_unprepare(dsi->panel);
> > -
> > -err_put_sync:
> > -	dsi->state &= ~DSIM_STATE_ENABLED;
> > -	pm_runtime_put(dsi->dev);
> > -}
> > -
> > -static void exynos_dsi_disable(struct exynos_dsi *dsi)
> > -{
> > -	struct drm_bridge *iter;
> > -
> > -	if (!(dsi->state & DSIM_STATE_ENABLED))
> > -		return;
> > -
> > -	dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
> > -
> > -	drm_panel_disable(dsi->panel);
> > -
> > -	list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) {
> > -		if (iter->funcs->disable)
> > -			iter->funcs->disable(iter);
> > -	}
> > -
> > -	exynos_dsi_set_display_enable(dsi, false);
> > -	drm_panel_unprepare(dsi->panel);
> > -
> > -	list_for_each_entry(iter, &dsi->bridge_chain, chain_node) {
> > -		if (iter->funcs->post_disable)
> > -			iter->funcs->post_disable(iter);
> > -	}
> > -
> > -	dsi->state &= ~DSIM_STATE_ENABLED;
> > -	pm_runtime_put_sync(dsi->dev);
> > -}
> > -
> > -static enum drm_connector_status
> > -exynos_dsi_detect(struct drm_connector *connector, bool force)
> > -{
> > -	return connector->status;
> > -}
> > +static const struct samsung_dsim_host_ops exynos_dsi_host_ops = {
> > +	.attach = exynos_dsi_host_attach,
> > +	.detach = exynos_dsi_host_detach,
> > +	.te_handler = exynos_dsi_te_handler,
> > +};
> >  
> > -static void exynos_dsi_connector_destroy(struct drm_connector *connector)
> > -{
> > -	drm_connector_unregister(connector);
> > -	drm_connector_cleanup(connector);
> > -	connector->dev = NULL;
> > -}
> > +static const struct samsung_dsim_driver_data exynos3_dsi_driver_data = {
> > +	.reg_ofs = EXYNOS_REG_OFS,
> > +	.plltmr_reg = 0x50,
> > +	.has_freqband = 1,
> > +	.has_clklane_stop = 1,
> > +	.num_clks = 2,
> > +	.max_freq = 1000,
> > +	.wait_for_reset = 1,
> > +	.num_bits_resol = 11,
> > +	.reg_values = reg_values,
> > +	.host_ops = &exynos_dsi_host_ops,
> > +};
> >  
> > -static const struct drm_connector_funcs exynos_dsi_connector_funcs = {
> > -	.detect = exynos_dsi_detect,
> > -	.fill_modes = drm_helper_probe_single_connector_modes,
> > -	.destroy = exynos_dsi_connector_destroy,
> > -	.reset = drm_atomic_helper_connector_reset,
> > -	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> > -	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> > +static const struct samsung_dsim_driver_data exynos4_dsi_driver_data = {
> > +	.reg_ofs = EXYNOS_REG_OFS,
> > +	.plltmr_reg = 0x50,
> > +	.has_freqband = 1,
> > +	.has_clklane_stop = 1,
> > +	.num_clks = 2,
> > +	.max_freq = 1000,
> > +	.wait_for_reset = 1,
> > +	.num_bits_resol = 11,
> > +	.reg_values = reg_values,
> > +	.host_ops = &exynos_dsi_host_ops,
> >  };
> >  
> > -static int exynos_dsi_get_modes(struct drm_connector *connector)
> > -{
> > -	struct exynos_dsi *dsi = connector_to_dsi(connector);
> > +static const struct samsung_dsim_driver_data exynos5_dsi_driver_data = {
> > +	.reg_ofs = EXYNOS_REG_OFS,
> > +	.plltmr_reg = 0x58,
> > +	.num_clks = 2,
> > +	.max_freq = 1000,
> > +	.wait_for_reset = 1,
> > +	.num_bits_resol = 11,
> > +	.reg_values = reg_values,
> > +	.host_ops = &exynos_dsi_host_ops,
> > +};
> >  
> > -	if (dsi->panel)
> > -		return drm_panel_get_modes(dsi->panel, connector);
> > +static const struct samsung_dsim_driver_data exynos5433_dsi_driver_data = {
> > +	.reg_ofs = EXYNOS5433_REG_OFS,
> > +	.plltmr_reg = 0xa0,
> > +	.has_clklane_stop = 1,
> > +	.num_clks = 5,
> > +	.max_freq = 1500,
> > +	.wait_for_reset = 0,
> > +	.num_bits_resol = 12,
> > +	.reg_values = exynos5433_reg_values,
> > +	.host_ops = &exynos_dsi_host_ops,
> > +};
> >  
> > -	return 0;
> > -}
> > +static const struct samsung_dsim_driver_data exynos5422_dsi_driver_data = {
> > +	.reg_ofs = EXYNOS5433_REG_OFS,
> > +	.plltmr_reg = 0xa0,
> > +	.has_clklane_stop = 1,
> > +	.num_clks = 2,
> > +	.max_freq = 1500,
> > +	.wait_for_reset = 1,
> > +	.num_bits_resol = 12,
> > +	.reg_values = exynos5422_reg_values,
> > +	.host_ops = &exynos_dsi_host_ops,
> > +};
> >  
> > -static const struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = {
> > -	.get_modes = exynos_dsi_get_modes,
> > +static const struct of_device_id exynos_dsi_of_match[] = {
> > +	{ .compatible = "samsung,exynos3250-mipi-dsi",
> > +	  .data = &exynos3_dsi_driver_data },
> > +	{ .compatible = "samsung,exynos4210-mipi-dsi",
> > +	  .data = &exynos4_dsi_driver_data },
> > +	{ .compatible = "samsung,exynos5410-mipi-dsi",
> > +	  .data = &exynos5_dsi_driver_data },
> > +	{ .compatible = "samsung,exynos5422-mipi-dsi",
> > +	  .data = &exynos5422_dsi_driver_data },
> > +	{ .compatible = "samsung,exynos5433-mipi-dsi",
> > +	  .data = &exynos5433_dsi_driver_data },
> > +	{ }
> >  };
> >  
> > -static int exynos_dsi_create_connector(struct exynos_dsi *dsi)
> > +static int exynos_dsi_bind(struct device *dev,
> > +			   struct device *master, void *data)
> >  {
> > -	struct drm_connector *connector = &dsi->connector;
> > -	struct drm_device *drm = dsi->bridge.dev;
> > +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> > +	struct drm_encoder *encoder = &dsi->encoder;
> > +	struct drm_device *drm_dev = data;
> > +	struct device_node *in_bridge_node;
> > +	struct drm_bridge *in_bridge;
> >  	int ret;
> >  
> > -	connector->polled = DRM_CONNECTOR_POLL_HPD;
> > +	drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_TMDS);
> >  
> > -	ret = drm_connector_init(drm, connector, &exynos_dsi_connector_funcs,
> > -				 DRM_MODE_CONNECTOR_DSI);
> > -	if (ret) {
> > -		DRM_DEV_ERROR(dsi->dev,
> > -			      "Failed to initialize connector with drm\n");
> > +	ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
> > +	if (ret < 0)
> >  		return ret;
> > -	}
> > -
> > -	connector->status = connector_status_disconnected;
> > -	drm_connector_helper_add(connector, &exynos_dsi_connector_helper_funcs);
> > -	drm_connector_attach_encoder(connector, dsi->bridge.encoder);
> > -	if (!drm->registered)
> > -		return 0;
> > -
> > -	connector->funcs->reset(connector);
> > -	drm_connector_register(connector);
> > -	return 0;
> > -}
> > -
> > -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(dsi);
> > -		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);
> > -		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);
> > +	in_bridge_node = of_graph_get_remote_node(dev->of_node, DSI_PORT_IN, 0);
> > +	if (in_bridge_node) {
> > +		in_bridge = of_drm_find_bridge(in_bridge_node);
> > +		if (in_bridge)
> > +			drm_bridge_attach(encoder, in_bridge, NULL, 0);
> > +		of_node_put(in_bridge_node);
> >  	}
> > -}
> > -
> > -static void exynos_dsi_bridge_enable(struct drm_bridge *bridge)
> > -{
> > -	struct exynos_dsi *dsi = bridge->driver_private;
> > -
> > -	exynos_dsi_enable(dsi);
> > -}
> > -
> > -static void exynos_dsi_bridge_disable(struct drm_bridge *bridge)
> > -{
> > -	struct exynos_dsi *dsi = bridge->driver_private;
> > -
> > -	exynos_dsi_disable(dsi);
> > -}
> >  
> > -static void exynos_dsi_bridge_mode_set(struct drm_bridge *bridge,
> > -				       const struct drm_display_mode *mode,
> > -				       const struct drm_display_mode *adjusted_mode)
> > -{
> > -	struct exynos_dsi *dsi = bridge->driver_private;
> > -
> > -	/* The mode is set when actually enabling the device. */
> > -	drm_mode_copy(&dsi->mode, adjusted_mode);
> > -}
> > -
> > -static const struct drm_bridge_funcs exynos_dsi_bridge_funcs = {
> > -	.attach = exynos_dsi_bridge_attach,
> > -	.detach = exynos_dsi_bridge_detach,
> > -	.enable = exynos_dsi_bridge_enable,
> > -	.disable = exynos_dsi_bridge_disable,
> > -	.mode_set = exynos_dsi_bridge_mode_set,
> > -};
> > -
> > -static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
> > -				  struct mipi_dsi_device *device)
> > -{
> > -	struct exynos_dsi *dsi = host_to_dsi(host);
> > -	const struct exynos_dsi_host_ops *ops = dsi->driver_data->host_ops;
> > -	struct drm_bridge *out_bridge;
> > -
> > -	out_bridge = of_drm_find_bridge(device->dev.of_node);
> > -	if (out_bridge) {
> > -		dsi->out_bridge = out_bridge;
> > -	} else {
> > -		dsi->panel = of_drm_find_panel(device->dev.of_node);
> > -		if (IS_ERR(dsi->panel))
> > -			dsi->panel = NULL;
> > -		else
> > -			dsi->connector.status = connector_status_connected;
> > -	}
> > -
> > -	/*
> > -	 * This is a temporary solution and should be made by more generic way.
> > -	 *
> > -	 * If attached panel device is for command mode one, dsi should register
> > -	 * TE interrupt handler.
> > -	 */
> > -	if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO)) {
> > -		int ret = exynos_dsi_register_te_irq(dsi, &device->dev);
> > -		if (ret)
> > -			return ret;
> > -	}
> > -
> > -	dsi->lanes = device->lanes;
> > -	dsi->format = device->format;
> > -	dsi->mode_flags = device->mode_flags;
> > -
> > -	if (ops && ops->attach)
> > -		ops->attach(dsi->dsi_host.dev, device);
> > +	ret = samsung_dsim_bind(dsi->dsi, encoder);
> > +	if (ret)
> > +		goto err;
> >  
> >  	return 0;
> > -}
> > -
> > -static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
> > -				  struct mipi_dsi_device *device)
> > -{
> > -	struct exynos_dsi *dsi = host_to_dsi(host);
> > -	const struct exynos_dsi_host_ops *ops = dsi->driver_data->host_ops;
> >  
> > -	if (ops && ops->detach)
> > -		ops->detach(dsi->dsi_host.dev, device);
> > -
> > -	exynos_dsi_unregister_te_irq(dsi);
> > -
> > -	return 0;
> > +err:
> > +	drm_encoder_cleanup(encoder);
> > +	return ret;
> >  }
> >  
> > -static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host,
> > -					 const struct mipi_dsi_msg *msg)
> > +static void exynos_dsi_unbind(struct device *dev,
> > +			      struct device *master, void *data)
> >  {
> > -	struct exynos_dsi *dsi = host_to_dsi(host);
> > -	struct exynos_dsi_transfer xfer;
> > -	int ret;
> > +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> > +	struct drm_encoder *encoder = &dsi->encoder;
> >  
> > -	if (!(dsi->state & DSIM_STATE_ENABLED))
> > -		return -EINVAL;
> > -
> > -	if (!(dsi->state & DSIM_STATE_INITIALIZED)) {
> > -		ret = exynos_dsi_init(dsi);
> > -		if (ret)
> > -			return ret;
> > -		dsi->state |= DSIM_STATE_INITIALIZED;
> > -	}
> > -
> > -	ret = mipi_dsi_create_packet(&xfer.packet, msg);
> > -	if (ret < 0)
> > -		return ret;
> > +	samsung_dsim_unbind(dsi->dsi);
> >  
> > -	xfer.rx_len = msg->rx_len;
> > -	xfer.rx_payload = msg->rx_buf;
> > -	xfer.flags = msg->flags;
> > -
> > -	ret = exynos_dsi_transfer(dsi, &xfer);
> > -	return (ret < 0) ? ret : xfer.rx_done;
> > +	drm_encoder_cleanup(encoder);
> >  }
> >  
> > -static const struct mipi_dsi_host_ops exynos_dsi_ops = {
> > -	.attach = exynos_dsi_host_attach,
> > -	.detach = exynos_dsi_host_detach,
> > -	.transfer = exynos_dsi_host_transfer,
> > +static const struct component_ops exynos_dsi_component_ops = {
> > +	.bind	= exynos_dsi_bind,
> > +	.unbind	= exynos_dsi_unbind,
> >  };
> >  
> > -static int exynos_dsi_of_read_u32(const struct device_node *np,
> > -				  const char *propname, u32 *out_value)
> > -{
> > -	int ret = of_property_read_u32(np, propname, out_value);
> > -
> > -	if (ret < 0)
> > -		pr_err("%pOF: failed to get '%s' property\n", np, propname);
> > -
> > -	return ret;
> > -}
> > -
> > -static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
> > -{
> > -	struct device *dev = dsi->dev;
> > -	struct device_node *node = dev->of_node;
> > -	int ret;
> > -
> > -	ret = exynos_dsi_of_read_u32(node, "samsung,pll-clock-frequency",
> > -				     &dsi->pll_clk_rate);
> > -	if (ret < 0)
> > -		return ret;
> > -
> > -	ret = exynos_dsi_of_read_u32(node, "samsung,burst-clock-frequency",
> > -				     &dsi->burst_clk_rate);
> > -	if (ret < 0)
> > -		return ret;
> > -
> > -	ret = exynos_dsi_of_read_u32(node, "samsung,esc-clock-frequency",
> > -				     &dsi->esc_clk_rate);
> > -	if (ret < 0)
> > -		return ret;
> > -
> > -	return 0;
> > -}
> > -
> > -static struct exynos_dsi *__exynos_dsi_probe(struct platform_device *pdev)
> > +static int 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;
> > +	struct device *dev = &pdev->dev;
> > +	int ret;
> >  
> >  	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
> >  	if (!dsi)
> > -		return ERR_PTR(-ENOMEM);
> > +		return -ENOMEM;
> > +	platform_set_drvdata(pdev, dsi);
> >  
> > -	/* To be checked as invalid one */
> > -	dsi->te_gpio = -ENOENT;
> > +	dsi->dsi = samsung_dsim_probe(pdev);
> > +	if (IS_ERR(dsi->dsi))
> > +		return PTR_ERR(dsi->dsi);
> >  
> > -	init_completion(&dsi->completed);
> > -	spin_lock_init(&dsi->transfer_lock);
> > -	INIT_LIST_HEAD(&dsi->transfer_list);
> > -	INIT_LIST_HEAD(&dsi->bridge_chain);
> > +	pm_runtime_enable(dev);
> >  
> > -	dsi->dsi_host.ops = &exynos_dsi_ops;
> > -	dsi->dsi_host.dev = dev;
> > -
> > -	dsi->dev = dev;
> > -	dsi->driver_data = of_device_get_match_data(dev);
> > -
> > -	dsi->supplies[0].supply = "vddcore";
> > -	dsi->supplies[1].supply = "vddio";
> > -	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(dsi->supplies),
> > -				      dsi->supplies);
> > -	if (ret) {
> > -		if (ret != -EPROBE_DEFER)
> > -			dev_info(dev, "failed to get regulators: %d\n", ret);
> > -		return ERR_PTR(ret);
> > -	}
> > -
> > -	dsi->clks = devm_kcalloc(dev,
> > -			dsi->driver_data->num_clks, sizeof(*dsi->clks),
> > -			GFP_KERNEL);
> > -	if (!dsi->clks)
> > -		return ERR_PTR(-ENOMEM);
> > -
> > -	for (i = 0; i < dsi->driver_data->num_clks; i++) {
> > -		dsi->clks[i] = devm_clk_get(dev, clk_names[i]);
> > -		if (IS_ERR(dsi->clks[i])) {
> > -			if (strcmp(clk_names[i], "sclk_mipi") == 0) {
> > -				dsi->clks[i] = devm_clk_get(dev,
> > -							OLD_SCLK_MIPI_CLK_NAME);
> > -				if (!IS_ERR(dsi->clks[i]))
> > -					continue;
> > -			}
> > -
> > -			dev_info(dev, "failed to get the clock: %s\n",
> > -					clk_names[i]);
> > -			return ERR_PTR(PTR_ERR(dsi->clks[i]));
> > -		}
> > -	}
> > -
> > -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > -	dsi->reg_base = devm_ioremap_resource(dev, res);
> > -	if (IS_ERR(dsi->reg_base)) {
> > -		dev_err(dev, "failed to remap io region\n");
> > -		return dsi->reg_base;
> > -	}
> > -
> > -	dsi->phy = devm_phy_get(dev, "dsim");
> > -	if (IS_ERR(dsi->phy)) {
> > -		dev_info(dev, "failed to get dsim phy\n");
> > -		return ERR_PTR(PTR_ERR(dsi->phy));
> > -	}
> > -
> > -	dsi->irq = platform_get_irq(pdev, 0);
> > -	if (dsi->irq < 0)
> > -		return ERR_PTR(dsi->irq);
> > -
> > -	irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
> > -	ret = devm_request_threaded_irq(dev, dsi->irq, NULL,
> > -					exynos_dsi_irq, IRQF_ONESHOT,
> > -					dev_name(dev), dsi);
> > -	if (ret) {
> > -		dev_err(dev, "failed to request dsi irq\n");
> > -		return ERR_PTR(ret);
> > -	}
> > -
> > -	ret = exynos_dsi_parse_dt(dsi);
> > +	ret = component_add(dev, &exynos_dsi_component_ops);
> >  	if (ret)
> > -		return ERR_PTR(ret);
> > +		goto err_disable_runtime;
> >  
> > -	ret = mipi_dsi_host_register(&dsi->dsi_host);
> > -	if (ret)
> > -		return ERR_PTR(ret);
> > +	return 0;
> >  
> > -	bridge = &dsi->bridge;
> > -	bridge->driver_private = dsi;
> > -	bridge->funcs = &exynos_dsi_bridge_funcs;
> > -	bridge->of_node = dev->of_node;
> > -	drm_bridge_add(bridge);
> > +err_disable_runtime:
> > +	pm_runtime_disable(dev);
> >  
> > -	return dsi;
> > +	return ret;
> >  }
> >  
> > -static void __exynos_dsi_remove(struct exynos_dsi *dsi)
> > +static int exynos_dsi_remove(struct platform_device *pdev)
> >  {
> > -	drm_bridge_remove(&dsi->bridge);
> > +	struct exynos_dsi *dsi = platform_get_drvdata(pdev);
> >  
> > -	mipi_dsi_host_unregister(&dsi->dsi_host);
> > -}
> > -
> > -/*
> > - * Probe/remove API, used from platforms based on the DRM bridge API.
> > - */
> > -struct exynos_dsi *exynos_dsi_probe(struct platform_device *pdev)
> > -{
> > -	return __exynos_dsi_probe(pdev);
> > -}
> > +	pm_runtime_disable(&pdev->dev);
> >  
> > -void exynos_dsi_remove(struct exynos_dsi *dsi)
> > -{
> > -	return __exynos_dsi_remove(dsi);
> > -}
> > +	samsung_dsim_remove(dsi->dsi);
> >  
> > -/*
> > - * Bind/unbind API, used from platforms based on the component framework.
> > - */
> > -int exynos_dsi_bind(struct exynos_dsi *dsi, struct drm_encoder *encoder)
> > -{
> > -	struct drm_bridge *previous = drm_bridge_chain_get_first_bridge(encoder);
> > +	component_del(&pdev->dev, &exynos_dsi_component_ops);
> >  
> > -	return drm_bridge_attach(encoder, &dsi->bridge, previous, 0);
> > -}
> > -
> > -void exynos_dsi_unbind(struct exynos_dsi *dsi)
> > -{
> > -	exynos_dsi_disable(dsi);
> > +	return 0;
> >  }
> >  
> > -int exynos_dsi_suspend(struct exynos_dsi *dsi)
> > +static int __maybe_unused exynos_dsi_suspend(struct device *dev)
> >  {
> > -	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
> > -	int ret, i;
> > -
> > -	usleep_range(10000, 20000);
> > -
> > -	if (dsi->state & DSIM_STATE_INITIALIZED) {
> > -		dsi->state &= ~DSIM_STATE_INITIALIZED;
> > -
> > -		exynos_dsi_disable_clock(dsi);
> > -
> > -		exynos_dsi_disable_irq(dsi);
> > -	}
> > -
> > -	dsi->state &= ~DSIM_STATE_CMD_LPM;
> > -
> > -	phy_power_off(dsi->phy);
> > -
> > -	for (i = driver_data->num_clks - 1; i > -1; i--)
> > -		clk_disable_unprepare(dsi->clks[i]);
> > -
> > -	ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
> > -	if (ret < 0)
> > -		dev_err(dsi->dev, "cannot disable regulators %d\n", ret);
> > +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> >  
> > -	return 0;
> > +	return samsung_dsim_suspend(dsi->dsi);
> >  }
> >  
> > -int exynos_dsi_resume(struct exynos_dsi *dsi)
> > +static int __maybe_unused exynos_dsi_resume(struct device *dev)
> >  {
> > -	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
> > -	int ret, i;
> > -
> > -	ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
> > -	if (ret < 0) {
> > -		dev_err(dsi->dev, "cannot enable regulators %d\n", ret);
> > -		return ret;
> > -	}
> > -
> > -	for (i = 0; i < driver_data->num_clks; i++) {
> > -		ret = clk_prepare_enable(dsi->clks[i]);
> > -		if (ret < 0)
> > -			goto err_clk;
> > -	}
> > -
> > -	ret = phy_power_on(dsi->phy);
> > -	if (ret < 0) {
> > -		dev_err(dsi->dev, "cannot enable phy %d\n", ret);
> > -		goto err_clk;
> > -	}
> > +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> >  
> > -	return 0;
> > +	return samsung_dsim_resume(dsi->dsi);
> > +}
> >  
> > -err_clk:
> > -	while (--i > -1)
> > -		clk_disable_unprepare(dsi->clks[i]);
> > -	regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
> > +static const struct dev_pm_ops exynos_dsi_pm_ops = {
> > +	SET_RUNTIME_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume, NULL)
> > +	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> > +				pm_runtime_force_resume)
> > +};
> >  
> > -	return ret;
> > -}
> > +struct platform_driver dsi_driver = {
> > +	.probe = exynos_dsi_probe,
> > +	.remove = exynos_dsi_remove,
> > +	.driver = {
> > +		   .name = "exynos-dsi",
> > +		   .owner = THIS_MODULE,
> > +		   .pm = &exynos_dsi_pm_ops,
> > +		   .of_match_table = exynos_dsi_of_match,
> > +	},
> > +};
> >  
> >  MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
> >  MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
> > diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c b/drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c
> > deleted file mode 100644
> > index 79d9ec6ade45..000000000000
> > --- a/drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c
> > +++ /dev/null
> > @@ -1,333 +0,0 @@
> > -// SPDX-License-Identifier: GPL-2.0-only
> > -/*
> > - * Samsung SoC MIPI DSI Master driver.
> > - *
> > - * Copyright (c) 2014 Samsung Electronics Co., Ltd
> > - *
> > - * Contacts: Tomasz Figa <t.figa@samsung.com>
> > - */
> > -
> > -#include <linux/component.h>
> > -#include <linux/of_device.h>
> > -#include <linux/of_graph.h>
> > -#include <linux/pm_runtime.h>
> > -
> > -#include <drm/drm_bridge.h>
> > -#include <drm/drm_encoder.h>
> > -#include <drm/drm_mipi_dsi.h>
> > -#include <drm/drm_probe_helper.h>
> > -#include <drm/drm_simple_kms_helper.h>
> > -
> > -#include "exynos_drm_crtc.h"
> > -#include "exynos_drm_drv.h"
> > -#include "exynos_drm_dsi.h"
> > -
> > -enum {
> > -	DSI_PORT_IN,
> > -	DSI_PORT_OUT
> > -};
> > -
> > -struct exynos_dsi_pltfm {
> > -	struct exynos_dsi *dsi;
> > -	struct drm_encoder encoder;
> > -};
> > -
> > -static const unsigned int reg_values[] = {
> > -	[RESET_TYPE] = DSIM_SWRST,
> > -	[PLL_TIMER] = 500,
> > -	[STOP_STATE_CNT] = 0xf,
> > -	[PHYCTRL_ULPS_EXIT] = 0x0af,
> > -	[PHYCTRL_VREG_LP] = 0,
> > -	[PHYCTRL_SLEW_UP] = 0,
> > -	[PHYTIMING_LPX] = 0x06,
> > -	[PHYTIMING_HS_EXIT] = 0x0b,
> > -	[PHYTIMING_CLK_PREPARE] = 0x07,
> > -	[PHYTIMING_CLK_ZERO] = 0x27,
> > -	[PHYTIMING_CLK_POST] = 0x0d,
> > -	[PHYTIMING_CLK_TRAIL] = 0x08,
> > -	[PHYTIMING_HS_PREPARE] = 0x09,
> > -	[PHYTIMING_HS_ZERO] = 0x0d,
> > -	[PHYTIMING_HS_TRAIL] = 0x0b,
> > -};
> > -
> > -static const unsigned int exynos5422_reg_values[] = {
> > -	[RESET_TYPE] = DSIM_SWRST,
> > -	[PLL_TIMER] = 500,
> > -	[STOP_STATE_CNT] = 0xf,
> > -	[PHYCTRL_ULPS_EXIT] = 0xaf,
> > -	[PHYCTRL_VREG_LP] = 0,
> > -	[PHYCTRL_SLEW_UP] = 0,
> > -	[PHYTIMING_LPX] = 0x08,
> > -	[PHYTIMING_HS_EXIT] = 0x0d,
> > -	[PHYTIMING_CLK_PREPARE] = 0x09,
> > -	[PHYTIMING_CLK_ZERO] = 0x30,
> > -	[PHYTIMING_CLK_POST] = 0x0e,
> > -	[PHYTIMING_CLK_TRAIL] = 0x0a,
> > -	[PHYTIMING_HS_PREPARE] = 0x0c,
> > -	[PHYTIMING_HS_ZERO] = 0x11,
> > -	[PHYTIMING_HS_TRAIL] = 0x0d,
> > -};
> > -
> > -static const unsigned int exynos5433_reg_values[] = {
> > -	[RESET_TYPE] = DSIM_FUNCRST,
> > -	[PLL_TIMER] = 22200,
> > -	[STOP_STATE_CNT] = 0xa,
> > -	[PHYCTRL_ULPS_EXIT] = 0x190,
> > -	[PHYCTRL_VREG_LP] = 1,
> > -	[PHYCTRL_SLEW_UP] = 1,
> > -	[PHYTIMING_LPX] = 0x07,
> > -	[PHYTIMING_HS_EXIT] = 0x0c,
> > -	[PHYTIMING_CLK_PREPARE] = 0x09,
> > -	[PHYTIMING_CLK_ZERO] = 0x2d,
> > -	[PHYTIMING_CLK_POST] = 0x0e,
> > -	[PHYTIMING_CLK_TRAIL] = 0x09,
> > -	[PHYTIMING_HS_PREPARE] = 0x0b,
> > -	[PHYTIMING_HS_ZERO] = 0x10,
> > -	[PHYTIMING_HS_TRAIL] = 0x0c,
> > -};
> > -
> > -static int __exynos_dsi_host_attach(struct device *dev,
> > -				    struct mipi_dsi_device *device)
> > -{
> > -	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
> > -	struct drm_device *drm = dsi->encoder.dev;
> > -	struct exynos_drm_crtc *crtc;
> > -
> > -	mutex_lock(&drm->mode_config.mutex);
> > -	crtc = exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD);
> > -	crtc->i80_mode = !(device->mode_flags & MIPI_DSI_MODE_VIDEO);
> > -	mutex_unlock(&drm->mode_config.mutex);
> > -
> > -	if (drm->mode_config.poll_enabled)
> > -		drm_kms_helper_hotplug_event(drm);
> > -
> > -	return 0;
> > -}
> > -
> > -static int __exynos_dsi_host_detach(struct device *dev,
> > -				    struct mipi_dsi_device *device)
> > -{
> > -	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
> > -	struct drm_device *drm = dsi->encoder.dev;
> > -
> > -	if (drm->mode_config.poll_enabled)
> > -		drm_kms_helper_hotplug_event(drm);
> > -
> > -	return 0;
> > -}
> > -
> > -static void __exynos_dsi_te_handler(struct device *dev)
> > -{
> > -	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
> > -
> > -	exynos_drm_crtc_te_handler(dsi->encoder.crtc);
> > -}
> > -
> > -static const struct exynos_dsi_host_ops exynos_dsi_host_ops = {
> > -	.attach = __exynos_dsi_host_attach,
> > -	.detach = __exynos_dsi_host_detach,
> > -	.te_handler = __exynos_dsi_te_handler,
> > -};
> > -
> > -static const struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
> > -	.reg_ofs = EXYNOS_REG_OFS,
> > -	.plltmr_reg = 0x50,
> > -	.has_freqband = 1,
> > -	.has_clklane_stop = 1,
> > -	.num_clks = 2,
> > -	.max_freq = 1000,
> > -	.wait_for_reset = 1,
> > -	.num_bits_resol = 11,
> > -	.reg_values = reg_values,
> > -	.host_ops = &exynos_dsi_host_ops,
> > -};
> > -
> > -static const struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
> > -	.reg_ofs = EXYNOS_REG_OFS,
> > -	.plltmr_reg = 0x50,
> > -	.has_freqband = 1,
> > -	.has_clklane_stop = 1,
> > -	.num_clks = 2,
> > -	.max_freq = 1000,
> > -	.wait_for_reset = 1,
> > -	.num_bits_resol = 11,
> > -	.reg_values = reg_values,
> > -	.host_ops = &exynos_dsi_host_ops,
> > -};
> > -
> > -static const struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
> > -	.reg_ofs = EXYNOS_REG_OFS,
> > -	.plltmr_reg = 0x58,
> > -	.num_clks = 2,
> > -	.max_freq = 1000,
> > -	.wait_for_reset = 1,
> > -	.num_bits_resol = 11,
> > -	.reg_values = reg_values,
> > -	.host_ops = &exynos_dsi_host_ops,
> > -};
> > -
> > -static const struct exynos_dsi_driver_data exynos5433_dsi_driver_data = {
> > -	.reg_ofs = EXYNOS5433_REG_OFS,
> > -	.plltmr_reg = 0xa0,
> > -	.has_clklane_stop = 1,
> > -	.num_clks = 5,
> > -	.max_freq = 1500,
> > -	.wait_for_reset = 0,
> > -	.num_bits_resol = 12,
> > -	.reg_values = exynos5433_reg_values,
> > -	.host_ops = &exynos_dsi_host_ops,
> > -};
> > -
> > -static const struct exynos_dsi_driver_data exynos5422_dsi_driver_data = {
> > -	.reg_ofs = EXYNOS5433_REG_OFS,
> > -	.plltmr_reg = 0xa0,
> > -	.has_clklane_stop = 1,
> > -	.num_clks = 2,
> > -	.max_freq = 1500,
> > -	.wait_for_reset = 1,
> > -	.num_bits_resol = 12,
> > -	.reg_values = exynos5422_reg_values,
> > -	.host_ops = &exynos_dsi_host_ops,
> > -};
> > -
> > -static const struct of_device_id exynos_dsi_of_match[] = {
> > -	{ .compatible = "samsung,exynos3250-mipi-dsi",
> > -	  .data = &exynos3_dsi_driver_data },
> > -	{ .compatible = "samsung,exynos4210-mipi-dsi",
> > -	  .data = &exynos4_dsi_driver_data },
> > -	{ .compatible = "samsung,exynos5410-mipi-dsi",
> > -	  .data = &exynos5_dsi_driver_data },
> > -	{ .compatible = "samsung,exynos5422-mipi-dsi",
> > -	  .data = &exynos5422_dsi_driver_data },
> > -	{ .compatible = "samsung,exynos5433-mipi-dsi",
> > -	  .data = &exynos5433_dsi_driver_data },
> > -	{ }
> > -};
> > -
> > -static int exynos_dsi_pltfm_bind(struct device *dev, struct device *master, void *data)
> > -{
> > -	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
> > -	struct drm_encoder *encoder = &dsi->encoder;
> > -	struct drm_device *drm_dev = data;
> > -	struct device_node *in_bridge_node;
> > -	struct drm_bridge *in_bridge;
> > -	int ret;
> > -
> > -	drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_TMDS);
> > -
> > -	ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
> > -	if (ret < 0)
> > -		return ret;
> > -
> > -	in_bridge_node = of_graph_get_remote_node(dev->of_node, DSI_PORT_IN, 0);
> > -	if (in_bridge_node) {
> > -		in_bridge = of_drm_find_bridge(in_bridge_node);
> > -		if (in_bridge)
> > -			drm_bridge_attach(encoder, in_bridge, NULL, 0);
> > -		of_node_put(in_bridge_node);
> > -	}
> > -
> > -	ret = exynos_dsi_bind(dsi->dsi, encoder);
> > -	if (ret)
> > -		goto err;
> > -
> > -	return 0;
> > -
> > -err:
> > -	drm_encoder_cleanup(encoder);
> > -	return ret;
> > -}
> > -
> > -static void exynos_dsi_pltfm_unbind(struct device *dev, struct device *master,
> > -				    void *data)
> > -{
> > -	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
> > -	struct drm_encoder *encoder = &dsi->encoder;
> > -
> > -	exynos_dsi_unbind(dsi->dsi);
> > -
> > -	drm_encoder_cleanup(encoder);
> > -}
> > -
> > -static const struct component_ops exynos_dsi_pltfm_component_ops = {
> > -	.bind	= exynos_dsi_pltfm_bind,
> > -	.unbind	= exynos_dsi_pltfm_unbind,
> > -};
> > -
> > -static int exynos_dsi_pltfm_probe(struct platform_device *pdev)
> > -{
> > -	struct exynos_dsi_pltfm *dsi;
> > -	struct device *dev = &pdev->dev;
> > -	int ret;
> > -
> > -	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
> > -	if (!dsi)
> > -		return -ENOMEM;
> > -	platform_set_drvdata(pdev, dsi);
> > -
> > -	dsi->dsi = exynos_dsi_probe(pdev);
> > -	if (IS_ERR(dsi->dsi))
> > -		return PTR_ERR(dsi->dsi);
> > -
> > -	pm_runtime_enable(dev);
> > -
> > -	ret = component_add(dev, &exynos_dsi_pltfm_component_ops);
> > -	if (ret)
> > -		goto err_disable_runtime;
> > -
> > -	return 0;
> > -
> > -err_disable_runtime:
> > -	pm_runtime_disable(dev);
> > -
> > -	return ret;
> > -}
> > -
> > -static int exynos_dsi_pltfm_remove(struct platform_device *pdev)
> > -{
> > -	struct exynos_dsi_pltfm *dsi = platform_get_drvdata(pdev);
> > -
> > -	pm_runtime_disable(&pdev->dev);
> > -
> > -	exynos_dsi_remove(dsi->dsi);
> > -
> > -	component_del(&pdev->dev, &exynos_dsi_pltfm_component_ops);
> > -
> > -	return 0;
> > -}
> > -
> > -static int __maybe_unused exynos_dsi_pltfm_suspend(struct device *dev)
> > -{
> > -	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
> > -
> > -	return exynos_dsi_suspend(dsi->dsi);
> > -}
> > -
> > -static int __maybe_unused exynos_dsi_pltfm_resume(struct device *dev)
> > -{
> > -	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
> > -
> > -	return exynos_dsi_resume(dsi->dsi);
> > -}
> > -
> > -static const struct dev_pm_ops exynos_dsi_pm_ops = {
> > -	SET_RUNTIME_PM_OPS(exynos_dsi_pltfm_suspend, exynos_dsi_pltfm_resume, NULL)
> > -	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> > -				pm_runtime_force_resume)
> > -};
> > -
> > -struct platform_driver dsi_driver = {
> > -	.probe = exynos_dsi_pltfm_probe,
> > -	.remove = exynos_dsi_pltfm_remove,
> > -	.driver = {
> > -		   .name = "exynos-dsi",
> > -		   .owner = THIS_MODULE,
> > -		   .pm = &exynos_dsi_pm_ops,
> > -		   .of_match_table = exynos_dsi_of_match,
> > -	},
> > -};
> > -
> > -MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
> > -MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
> > -MODULE_DESCRIPTION("Samsung SoC MIPI DSI Master");
> > -MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.h b/include/drm/bridge/samsung-dsim.h
> > similarity index 69%
> > rename from drivers/gpu/drm/exynos/exynos_drm_dsi.h
> > rename to include/drm/bridge/samsung-dsim.h
> > index 8fa3276889de..be8b4913aa9c 100644
> > --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.h
> > +++ b/include/drm/bridge/samsung-dsim.h
> > @@ -3,7 +3,7 @@
> >  #define __EXYNOS_DRM_DSI__
> >  
> >  struct drm_encoder;
> > -struct exynos_dsi;
> > +struct samsung_dsim;
> >  struct platform_device;
> >  struct mipi_dsi_device;
> >  
> > @@ -12,13 +12,13 @@ enum exynos_reg_offset {
> >  	EXYNOS5433_REG_OFS
> >  };
> >  
> > -struct exynos_dsi *exynos_dsi_probe(struct platform_device *pdev);
> > -void exynos_dsi_remove(struct exynos_dsi *dsi);
> > -int exynos_dsi_bind(struct exynos_dsi *dsi, struct drm_encoder *encoder);
> > -void exynos_dsi_unbind(struct exynos_dsi *dsi);
> > +struct samsung_dsim *samsung_dsim_probe(struct platform_device *pdev);
> > +void samsung_dsim_remove(struct samsung_dsim *dsi);
> > +int samsung_dsim_bind(struct samsung_dsim *dsi, struct drm_encoder *encoder);
> > +void samsung_dsim_unbind(struct samsung_dsim *dsi);
> >  
> > -int exynos_dsi_suspend(struct exynos_dsi *dsi);
> > -int exynos_dsi_resume(struct exynos_dsi *dsi);
> > +int samsung_dsim_suspend(struct samsung_dsim *dsi);
> > +int samsung_dsim_resume(struct samsung_dsim *dsi);
> >  
> >  enum reg_value_idx {
> >  	RESET_TYPE,
> > @@ -42,13 +42,13 @@ enum reg_value_idx {
> >  #define DSIM_FUNCRST			(1 << 16)
> >  #define DSIM_SWRST			(1 << 0)
> >  
> > -struct exynos_dsi_host_ops {
> > +struct samsung_dsim_host_ops {
> >  	int (*attach)(struct device *dev, struct mipi_dsi_device *device);
> >  	int (*detach)(struct device *dev, struct mipi_dsi_device *device);
> >  	void (*te_handler)(struct device *dev);
> >  };
> >  
> > -struct exynos_dsi_driver_data {
> > +struct samsung_dsim_driver_data {
> >  	enum exynos_reg_offset reg_ofs;
> >  	unsigned int plltmr_reg;
> >  	unsigned int has_freqband:1;
> > @@ -58,7 +58,7 @@ struct exynos_dsi_driver_data {
> >  	unsigned int wait_for_reset;
> >  	unsigned int num_bits_resol;
> >  	const unsigned int *reg_values;
> > -	const struct exynos_dsi_host_ops *host_ops;
> > +	const struct samsung_dsim_host_ops *host_ops;
> >  };
> >  
> >  #endif /* __EXYNOS_DRM_DSI__ */
> > -- 
> > 2.20.1
> > 
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 
> -- 
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
> 

-- 
Pengutronix e.K.                           | Michael Tretter             |
Steuerwalder Str. 21                       | https://www.pengutronix.de/ |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 16/16] drm/exynos: move bridge driver to bridges
  2020-09-16  8:58       ` Michael Tretter
@ 2020-09-16  9:03         ` Daniel Vetter
  0 siblings, 0 replies; 61+ messages in thread
From: Daniel Vetter @ 2020-09-16  9:03 UTC (permalink / raw)
  To: Michael Tretter
  Cc: linux-samsung-soc, jy0922.shim, narmstrong, b.zolnierkie,
	sw0312.kim, dri-devel, a.hajda, krzk, sylvester.nawrocki, kernel,
	Laurent.pinchart

On Wed, Sep 16, 2020 at 10:58:33AM +0200, Michael Tretter wrote:
> On Wed, 16 Sep 2020 09:58:39 +0200, Daniel Vetter wrote:
> > On Fri, Sep 11, 2020 at 03:54:13PM +0200, Michael Tretter wrote:
> > > As the driver is not platform dependent anymore, move it to the drm
> > > bridge driver directory.
> > > 
> > > Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
> > 
> > So new drm_bridge drivers that still use the component stuff is a bit
> > uncool. We're trying to get away from that everywhere, bridges should be
> > abstracted enough that just going through the of lookup functions to get
> > at your bridge should work.
> > 
> > Is there anything here that prevents this, or could this be included?
> 
> The Exynos drm driver uses the component framework. Maybe I can avoid exposing
> the bind/unbind API in the drm_bridge driver by implementing the bind/unbind
> in the platform specific part. However, completely getting rid of the
> component framework for this drm_bridge is not possible without changing the
> entire Exynos drm driver.

You can combine the component framework for internal stuff with
of_drm_find_bridge() for bridges. That's at least supposed to work.

I guess the of_drm_find_bridge would need to be stuffed in one of the
existing bind hooks (maybe the master one), since having a component for
the bridge and using the of_ function doesn't really make sense I think.

I guess another option would be that instead of adding the bridge
component when you create your match, call of_drm_find_bridge, but at that
point you don't yet have the drm_device nor drm_encoder.
-Daniel

> 
> Michael
> 
> > -Daniel
> > 
> > > ---
> > > v2:
> > > - select DRM_SAMSUNG_DSIM from DRM_EXYNOS_DSI
> > > - add removal of depends on !FB_S3C
> > > ---
> > >  drivers/gpu/drm/bridge/Kconfig                |    9 +
> > >  drivers/gpu/drm/bridge/Makefile               |    1 +
> > >  drivers/gpu/drm/bridge/samsung-dsim.c         | 1790 ++++++++++++++++
> > >  drivers/gpu/drm/exynos/Kconfig                |    5 +-
> > >  drivers/gpu/drm/exynos/Makefile               |    2 +-
> > >  drivers/gpu/drm/exynos/exynos_drm_dsi.c       | 1896 ++---------------
> > >  drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c |  333 ---
> > >  .../drm/bridge/samsung-dsim.h                 |   20 +-
> > >  8 files changed, 2037 insertions(+), 2019 deletions(-)
> > >  create mode 100644 drivers/gpu/drm/bridge/samsung-dsim.c
> > >  delete mode 100644 drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c
> > >  rename drivers/gpu/drm/exynos/exynos_drm_dsi.h => include/drm/bridge/samsung-dsim.h (69%)
> > > 
> > > diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> > > index 3e11af4e9f63..55ab5030c6cf 100644
> > > --- a/drivers/gpu/drm/bridge/Kconfig
> > > +++ b/drivers/gpu/drm/bridge/Kconfig
> > > @@ -125,6 +125,15 @@ config DRM_PARADE_PS8640
> > >  	  The PS8640 is a high-performance and low-power
> > >  	  MIPI DSI to eDP converter
> > >  
> > > +config DRM_SAMSUNG_DSIM
> > > +	tristate "Samsung MIPI DSI bridge"
> > > +	depends on OF
> > > +	select DRM_KMS_HELPER
> > > +	select DRM_MIPI_DSI
> > > +	select DRM_PANEL
> > > +	help
> > > +	  Samsung MIPI DSI bridge driver.
> > > +
> > >  config DRM_SIL_SII8620
> > >  	tristate "Silicon Image SII8620 HDMI/MHL bridge"
> > >  	depends on OF
> > > diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> > > index c589a6a7cbe1..5ac7a5c413dc 100644
> > > --- a/drivers/gpu/drm/bridge/Makefile
> > > +++ b/drivers/gpu/drm/bridge/Makefile
> > > @@ -8,6 +8,7 @@ obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v
> > >  obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
> > >  obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
> > >  obj-$(CONFIG_DRM_PARADE_PS8640) += parade-ps8640.o
> > > +obj-$(CONFIG_DRM_SAMSUNG_DSIM) += samsung-dsim.o
> > >  obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o
> > >  obj-$(CONFIG_DRM_SII902X) += sii902x.o
> > >  obj-$(CONFIG_DRM_SII9234) += sii9234.o
> > > diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c
> > > new file mode 100644
> > > index 000000000000..6d2d8dc027de
> > > --- /dev/null
> > > +++ b/drivers/gpu/drm/bridge/samsung-dsim.c
> > > @@ -0,0 +1,1790 @@
> > > +// SPDX-License-Identifier: GPL-2.0-only
> > > +/*
> > > + * Samsung SoC MIPI DSI Master driver.
> > > + *
> > > + * Copyright (c) 2014 Samsung Electronics Co., Ltd
> > > + *
> > > + * Contacts: Tomasz Figa <t.figa@samsung.com>
> > > +*/
> > > +
> > > +#include <linux/clk.h>
> > > +#include <linux/delay.h>
> > > +#include <linux/component.h>
> > > +#include <linux/gpio/consumer.h>
> > > +#include <linux/irq.h>
> > > +#include <linux/of_device.h>
> > > +#include <linux/of_gpio.h>
> > > +#include <linux/of_graph.h>
> > > +#include <linux/phy/phy.h>
> > > +#include <linux/regulator/consumer.h>
> > > +
> > > +#include <asm/unaligned.h>
> > > +
> > > +#include <video/mipi_display.h>
> > > +#include <video/videomode.h>
> > > +
> > > +#include <drm/bridge/samsung-dsim.h>
> > > +#include <drm/drm_atomic_helper.h>
> > > +#include <drm/drm_bridge.h>
> > > +#include <drm/drm_fb_helper.h>
> > > +#include <drm/drm_mipi_dsi.h>
> > > +#include <drm/drm_panel.h>
> > > +#include <drm/drm_print.h>
> > > +#include <drm/drm_probe_helper.h>
> > > +#include <drm/drm_simple_kms_helper.h>
> > > +
> > > +/* returns true iff both arguments logically differs */
> > > +#define NEQV(a, b) (!(a) ^ !(b))
> > > +
> > > +/* DSIM_STATUS */
> > > +#define DSIM_STOP_STATE_DAT(x)		(((x) & 0xf) << 0)
> > > +#define DSIM_STOP_STATE_CLK		(1 << 8)
> > > +#define DSIM_TX_READY_HS_CLK		(1 << 10)
> > > +#define DSIM_PLL_STABLE			(1 << 31)
> > > +
> > > +/* DSIM_TIMEOUT */
> > > +#define DSIM_LPDR_TIMEOUT(x)		((x) << 0)
> > > +#define DSIM_BTA_TIMEOUT(x)		((x) << 16)
> > > +
> > > +/* DSIM_CLKCTRL */
> > > +#define DSIM_ESC_PRESCALER(x)		(((x) & 0xffff) << 0)
> > > +#define DSIM_ESC_PRESCALER_MASK		(0xffff << 0)
> > > +#define DSIM_LANE_ESC_CLK_EN_CLK	(1 << 19)
> > > +#define DSIM_LANE_ESC_CLK_EN_DATA(x)	(((x) & 0xf) << 20)
> > > +#define DSIM_LANE_ESC_CLK_EN_DATA_MASK	(0xf << 20)
> > > +#define DSIM_BYTE_CLKEN			(1 << 24)
> > > +#define DSIM_BYTE_CLK_SRC(x)		(((x) & 0x3) << 25)
> > > +#define DSIM_BYTE_CLK_SRC_MASK		(0x3 << 25)
> > > +#define DSIM_PLL_BYPASS			(1 << 27)
> > > +#define DSIM_ESC_CLKEN			(1 << 28)
> > > +#define DSIM_TX_REQUEST_HSCLK		(1 << 31)
> > > +
> > > +/* DSIM_CONFIG */
> > > +#define DSIM_LANE_EN_CLK		(1 << 0)
> > > +#define DSIM_LANE_EN(x)			(((x) & 0xf) << 1)
> > > +#define DSIM_NUM_OF_DATA_LANE(x)	(((x) & 0x3) << 5)
> > > +#define DSIM_SUB_PIX_FORMAT(x)		(((x) & 0x7) << 8)
> > > +#define DSIM_MAIN_PIX_FORMAT_MASK	(0x7 << 12)
> > > +#define DSIM_MAIN_PIX_FORMAT_RGB888	(0x7 << 12)
> > > +#define DSIM_MAIN_PIX_FORMAT_RGB666	(0x6 << 12)
> > > +#define DSIM_MAIN_PIX_FORMAT_RGB666_P	(0x5 << 12)
> > > +#define DSIM_MAIN_PIX_FORMAT_RGB565	(0x4 << 12)
> > > +#define DSIM_SUB_VC			(((x) & 0x3) << 16)
> > > +#define DSIM_MAIN_VC			(((x) & 0x3) << 18)
> > > +#define DSIM_HSA_MODE			(1 << 20)
> > > +#define DSIM_HBP_MODE			(1 << 21)
> > > +#define DSIM_HFP_MODE			(1 << 22)
> > > +#define DSIM_HSE_MODE			(1 << 23)
> > > +#define DSIM_AUTO_MODE			(1 << 24)
> > > +#define DSIM_VIDEO_MODE			(1 << 25)
> > > +#define DSIM_BURST_MODE			(1 << 26)
> > > +#define DSIM_SYNC_INFORM		(1 << 27)
> > > +#define DSIM_EOT_DISABLE		(1 << 28)
> > > +#define DSIM_MFLUSH_VS			(1 << 29)
> > > +/* This flag is valid only for exynos3250/3472/5260/5430 */
> > > +#define DSIM_CLKLANE_STOP		(1 << 30)
> > > +
> > > +/* DSIM_ESCMODE */
> > > +#define DSIM_TX_TRIGGER_RST		(1 << 4)
> > > +#define DSIM_TX_LPDT_LP			(1 << 6)
> > > +#define DSIM_CMD_LPDT_LP		(1 << 7)
> > > +#define DSIM_FORCE_BTA			(1 << 16)
> > > +#define DSIM_FORCE_STOP_STATE		(1 << 20)
> > > +#define DSIM_STOP_STATE_CNT(x)		(((x) & 0x7ff) << 21)
> > > +#define DSIM_STOP_STATE_CNT_MASK	(0x7ff << 21)
> > > +
> > > +/* DSIM_MDRESOL */
> > > +#define DSIM_MAIN_STAND_BY		(1 << 31)
> > > +#define DSIM_MAIN_VRESOL(x, num_bits)	(((x) & ((1 << (num_bits)) - 1)) << 16)
> > > +#define DSIM_MAIN_HRESOL(x, num_bits)	(((x) & ((1 << (num_bits)) - 1)) << 0)
> > > +
> > > +/* DSIM_MVPORCH */
> > > +#define DSIM_CMD_ALLOW(x)		((x) << 28)
> > > +#define DSIM_STABLE_VFP(x)		((x) << 16)
> > > +#define DSIM_MAIN_VBP(x)		((x) << 0)
> > > +#define DSIM_CMD_ALLOW_MASK		(0xf << 28)
> > > +#define DSIM_STABLE_VFP_MASK		(0x7ff << 16)
> > > +#define DSIM_MAIN_VBP_MASK		(0x7ff << 0)
> > > +
> > > +/* DSIM_MHPORCH */
> > > +#define DSIM_MAIN_HFP(x)		((x) << 16)
> > > +#define DSIM_MAIN_HBP(x)		((x) << 0)
> > > +#define DSIM_MAIN_HFP_MASK		((0xffff) << 16)
> > > +#define DSIM_MAIN_HBP_MASK		((0xffff) << 0)
> > > +
> > > +/* DSIM_MSYNC */
> > > +#define DSIM_MAIN_VSA(x)		((x) << 22)
> > > +#define DSIM_MAIN_HSA(x)		((x) << 0)
> > > +#define DSIM_MAIN_VSA_MASK		((0x3ff) << 22)
> > > +#define DSIM_MAIN_HSA_MASK		((0xffff) << 0)
> > > +
> > > +/* DSIM_SDRESOL */
> > > +#define DSIM_SUB_STANDY(x)		((x) << 31)
> > > +#define DSIM_SUB_VRESOL(x)		((x) << 16)
> > > +#define DSIM_SUB_HRESOL(x)		((x) << 0)
> > > +#define DSIM_SUB_STANDY_MASK		((0x1) << 31)
> > > +#define DSIM_SUB_VRESOL_MASK		((0x7ff) << 16)
> > > +#define DSIM_SUB_HRESOL_MASK		((0x7ff) << 0)
> > > +
> > > +/* DSIM_INTSRC */
> > > +#define DSIM_INT_PLL_STABLE		(1 << 31)
> > > +#define DSIM_INT_SW_RST_RELEASE		(1 << 30)
> > > +#define DSIM_INT_SFR_FIFO_EMPTY		(1 << 29)
> > > +#define DSIM_INT_SFR_HDR_FIFO_EMPTY	(1 << 28)
> > > +#define DSIM_INT_BTA			(1 << 25)
> > > +#define DSIM_INT_FRAME_DONE		(1 << 24)
> > > +#define DSIM_INT_RX_TIMEOUT		(1 << 21)
> > > +#define DSIM_INT_BTA_TIMEOUT		(1 << 20)
> > > +#define DSIM_INT_RX_DONE		(1 << 18)
> > > +#define DSIM_INT_RX_TE			(1 << 17)
> > > +#define DSIM_INT_RX_ACK			(1 << 16)
> > > +#define DSIM_INT_RX_ECC_ERR		(1 << 15)
> > > +#define DSIM_INT_RX_CRC_ERR		(1 << 14)
> > > +
> > > +/* DSIM_FIFOCTRL */
> > > +#define DSIM_RX_DATA_FULL		(1 << 25)
> > > +#define DSIM_RX_DATA_EMPTY		(1 << 24)
> > > +#define DSIM_SFR_HEADER_FULL		(1 << 23)
> > > +#define DSIM_SFR_HEADER_EMPTY		(1 << 22)
> > > +#define DSIM_SFR_PAYLOAD_FULL		(1 << 21)
> > > +#define DSIM_SFR_PAYLOAD_EMPTY		(1 << 20)
> > > +#define DSIM_I80_HEADER_FULL		(1 << 19)
> > > +#define DSIM_I80_HEADER_EMPTY		(1 << 18)
> > > +#define DSIM_I80_PAYLOAD_FULL		(1 << 17)
> > > +#define DSIM_I80_PAYLOAD_EMPTY		(1 << 16)
> > > +#define DSIM_SD_HEADER_FULL		(1 << 15)
> > > +#define DSIM_SD_HEADER_EMPTY		(1 << 14)
> > > +#define DSIM_SD_PAYLOAD_FULL		(1 << 13)
> > > +#define DSIM_SD_PAYLOAD_EMPTY		(1 << 12)
> > > +#define DSIM_MD_HEADER_FULL		(1 << 11)
> > > +#define DSIM_MD_HEADER_EMPTY		(1 << 10)
> > > +#define DSIM_MD_PAYLOAD_FULL		(1 << 9)
> > > +#define DSIM_MD_PAYLOAD_EMPTY		(1 << 8)
> > > +#define DSIM_RX_FIFO			(1 << 4)
> > > +#define DSIM_SFR_FIFO			(1 << 3)
> > > +#define DSIM_I80_FIFO			(1 << 2)
> > > +#define DSIM_SD_FIFO			(1 << 1)
> > > +#define DSIM_MD_FIFO			(1 << 0)
> > > +
> > > +/* DSIM_PHYACCHR */
> > > +#define DSIM_AFC_EN			(1 << 14)
> > > +#define DSIM_AFC_CTL(x)			(((x) & 0x7) << 5)
> > > +
> > > +/* DSIM_PLLCTRL */
> > > +#define DSIM_FREQ_BAND(x)		((x) << 24)
> > > +#define DSIM_PLL_EN			(1 << 23)
> > > +#define DSIM_PLL_P(x)			((x) << 13)
> > > +#define DSIM_PLL_M(x)			((x) << 4)
> > > +#define DSIM_PLL_S(x)			((x) << 1)
> > > +
> > > +/* DSIM_PHYCTRL */
> > > +#define DSIM_PHYCTRL_ULPS_EXIT(x)	(((x) & 0x1ff) << 0)
> > > +#define DSIM_PHYCTRL_B_DPHYCTL_VREG_LP	(1 << 30)
> > > +#define DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP	(1 << 14)
> > > +
> > > +/* DSIM_PHYTIMING */
> > > +#define DSIM_PHYTIMING_LPX(x)		((x) << 8)
> > > +#define DSIM_PHYTIMING_HS_EXIT(x)	((x) << 0)
> > > +
> > > +/* DSIM_PHYTIMING1 */
> > > +#define DSIM_PHYTIMING1_CLK_PREPARE(x)	((x) << 24)
> > > +#define DSIM_PHYTIMING1_CLK_ZERO(x)	((x) << 16)
> > > +#define DSIM_PHYTIMING1_CLK_POST(x)	((x) << 8)
> > > +#define DSIM_PHYTIMING1_CLK_TRAIL(x)	((x) << 0)
> > > +
> > > +/* DSIM_PHYTIMING2 */
> > > +#define DSIM_PHYTIMING2_HS_PREPARE(x)	((x) << 16)
> > > +#define DSIM_PHYTIMING2_HS_ZERO(x)	((x) << 8)
> > > +#define DSIM_PHYTIMING2_HS_TRAIL(x)	((x) << 0)
> > > +
> > > +#define DSI_MAX_BUS_WIDTH		4
> > > +#define DSI_NUM_VIRTUAL_CHANNELS	4
> > > +#define DSI_TX_FIFO_SIZE		2048
> > > +#define DSI_RX_FIFO_SIZE		256
> > > +#define DSI_XFER_TIMEOUT_MS		100
> > > +#define DSI_RX_FIFO_EMPTY		0x30800002
> > > +
> > > +#define OLD_SCLK_MIPI_CLK_NAME "pll_clk"
> > > +
> > > +static const char *const clk_names[5] = { "bus_clk", "sclk_mipi",
> > > +	"phyclk_mipidphy0_bitclkdiv8", "phyclk_mipidphy0_rxclkesc0",
> > > +	"sclk_rgb_vclk_to_dsim0" };
> > > +
> > > +enum samsung_dsim_transfer_type {
> > > +	EXYNOS_DSI_TX,
> > > +	EXYNOS_DSI_RX,
> > > +};
> > > +
> > > +struct samsung_dsim_transfer {
> > > +	struct list_head list;
> > > +	struct completion completed;
> > > +	int result;
> > > +	struct mipi_dsi_packet packet;
> > > +	u16 flags;
> > > +	u16 tx_done;
> > > +
> > > +	u8 *rx_payload;
> > > +	u16 rx_len;
> > > +	u16 rx_done;
> > > +};
> > > +
> > > +#define DSIM_STATE_ENABLED		BIT(0)
> > > +#define DSIM_STATE_INITIALIZED		BIT(1)
> > > +#define DSIM_STATE_CMD_LPM		BIT(2)
> > > +#define DSIM_STATE_VIDOUT_AVAILABLE	BIT(3)
> > > +
> > > +struct samsung_dsim {
> > > +	struct drm_bridge bridge;
> > > +	struct mipi_dsi_host dsi_host;
> > > +	struct drm_connector connector;
> > > +	struct drm_panel *panel;
> > > +	struct list_head bridge_chain;
> > > +	struct drm_bridge *out_bridge;
> > > +	struct device *dev;
> > > +
> > > +	void __iomem *reg_base;
> > > +	struct phy *phy;
> > > +	struct clk **clks;
> > > +	struct regulator_bulk_data supplies[2];
> > > +	int irq;
> > > +	int te_gpio;
> > > +
> > > +	u32 pll_clk_rate;
> > > +	u32 burst_clk_rate;
> > > +	u32 esc_clk_rate;
> > > +	u32 lanes;
> > > +	u32 mode_flags;
> > > +	u32 format;
> > > +
> > > +	struct drm_display_mode mode;
> > > +
> > > +	int state;
> > > +	struct drm_property *brightness;
> > > +	struct completion completed;
> > > +
> > > +	spinlock_t transfer_lock; /* protects transfer_list */
> > > +	struct list_head transfer_list;
> > > +
> > > +	const struct samsung_dsim_driver_data *driver_data;
> > > +};
> > > +
> > > +#define host_to_dsi(host) container_of(host, struct samsung_dsim, dsi_host)
> > > +#define connector_to_dsi(c) container_of(c, struct samsung_dsim, connector)
> > > +
> > > +enum reg_idx {
> > > +	DSIM_STATUS_REG,	/* Status register */
> > > +	DSIM_SWRST_REG,		/* Software reset register */
> > > +	DSIM_CLKCTRL_REG,	/* Clock control register */
> > > +	DSIM_TIMEOUT_REG,	/* Time out register */
> > > +	DSIM_CONFIG_REG,	/* Configuration register */
> > > +	DSIM_ESCMODE_REG,	/* Escape mode register */
> > > +	DSIM_MDRESOL_REG,
> > > +	DSIM_MVPORCH_REG,	/* Main display Vporch register */
> > > +	DSIM_MHPORCH_REG,	/* Main display Hporch register */
> > > +	DSIM_MSYNC_REG,		/* Main display sync area register */
> > > +	DSIM_INTSRC_REG,	/* Interrupt source register */
> > > +	DSIM_INTMSK_REG,	/* Interrupt mask register */
> > > +	DSIM_PKTHDR_REG,	/* Packet Header FIFO register */
> > > +	DSIM_PAYLOAD_REG,	/* Payload FIFO register */
> > > +	DSIM_RXFIFO_REG,	/* Read FIFO register */
> > > +	DSIM_FIFOCTRL_REG,	/* FIFO status and control register */
> > > +	DSIM_PLLCTRL_REG,	/* PLL control register */
> > > +	DSIM_PHYCTRL_REG,
> > > +	DSIM_PHYTIMING_REG,
> > > +	DSIM_PHYTIMING1_REG,
> > > +	DSIM_PHYTIMING2_REG,
> > > +	NUM_REGS
> > > +};
> > > +
> > > +static const unsigned int exynos_reg_ofs[] = {
> > > +	[DSIM_STATUS_REG] =  0x00,
> > > +	[DSIM_SWRST_REG] =  0x04,
> > > +	[DSIM_CLKCTRL_REG] =  0x08,
> > > +	[DSIM_TIMEOUT_REG] =  0x0c,
> > > +	[DSIM_CONFIG_REG] =  0x10,
> > > +	[DSIM_ESCMODE_REG] =  0x14,
> > > +	[DSIM_MDRESOL_REG] =  0x18,
> > > +	[DSIM_MVPORCH_REG] =  0x1c,
> > > +	[DSIM_MHPORCH_REG] =  0x20,
> > > +	[DSIM_MSYNC_REG] =  0x24,
> > > +	[DSIM_INTSRC_REG] =  0x2c,
> > > +	[DSIM_INTMSK_REG] =  0x30,
> > > +	[DSIM_PKTHDR_REG] =  0x34,
> > > +	[DSIM_PAYLOAD_REG] =  0x38,
> > > +	[DSIM_RXFIFO_REG] =  0x3c,
> > > +	[DSIM_FIFOCTRL_REG] =  0x44,
> > > +	[DSIM_PLLCTRL_REG] =  0x4c,
> > > +	[DSIM_PHYCTRL_REG] =  0x5c,
> > > +	[DSIM_PHYTIMING_REG] =  0x64,
> > > +	[DSIM_PHYTIMING1_REG] =  0x68,
> > > +	[DSIM_PHYTIMING2_REG] =  0x6c,
> > > +};
> > > +
> > > +static const unsigned int exynos5433_reg_ofs[] = {
> > > +	[DSIM_STATUS_REG] = 0x04,
> > > +	[DSIM_SWRST_REG] = 0x0C,
> > > +	[DSIM_CLKCTRL_REG] = 0x10,
> > > +	[DSIM_TIMEOUT_REG] = 0x14,
> > > +	[DSIM_CONFIG_REG] = 0x18,
> > > +	[DSIM_ESCMODE_REG] = 0x1C,
> > > +	[DSIM_MDRESOL_REG] = 0x20,
> > > +	[DSIM_MVPORCH_REG] = 0x24,
> > > +	[DSIM_MHPORCH_REG] = 0x28,
> > > +	[DSIM_MSYNC_REG] = 0x2C,
> > > +	[DSIM_INTSRC_REG] = 0x34,
> > > +	[DSIM_INTMSK_REG] = 0x38,
> > > +	[DSIM_PKTHDR_REG] = 0x3C,
> > > +	[DSIM_PAYLOAD_REG] = 0x40,
> > > +	[DSIM_RXFIFO_REG] = 0x44,
> > > +	[DSIM_FIFOCTRL_REG] = 0x4C,
> > > +	[DSIM_PLLCTRL_REG] = 0x94,
> > > +	[DSIM_PHYCTRL_REG] = 0xA4,
> > > +	[DSIM_PHYTIMING_REG] = 0xB4,
> > > +	[DSIM_PHYTIMING1_REG] = 0xB8,
> > > +	[DSIM_PHYTIMING2_REG] = 0xBC,
> > > +};
> > > +
> > > +static inline void samsung_dsim_write(struct samsung_dsim *dsi,
> > > +				      enum reg_idx idx, u32 val)
> > > +{
> > > +	const unsigned int *reg_ofs;
> > > +
> > > +	if (dsi->driver_data->reg_ofs == EXYNOS5433_REG_OFS)
> > > +		reg_ofs = exynos5433_reg_ofs;
> > > +	else
> > > +		reg_ofs = exynos_reg_ofs;
> > > +
> > > +	writel(val, dsi->reg_base + reg_ofs[idx]);
> > > +}
> > > +
> > > +static inline u32 samsung_dsim_read(struct samsung_dsim *dsi, enum reg_idx idx)
> > > +{
> > > +	const unsigned int *reg_ofs;
> > > +
> > > +	if (dsi->driver_data->reg_ofs == EXYNOS5433_REG_OFS)
> > > +		reg_ofs = exynos5433_reg_ofs;
> > > +	else
> > > +		reg_ofs = exynos_reg_ofs;
> > > +
> > > +	return readl(dsi->reg_base + reg_ofs[idx]);
> > > +}
> > > +
> > > +static void samsung_dsim_wait_for_reset(struct samsung_dsim *dsi)
> > > +{
> > > +	if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300)))
> > > +		return;
> > > +
> > > +	dev_err(dsi->dev, "timeout waiting for reset\n");
> > > +}
> > > +
> > > +static void samsung_dsim_reset(struct samsung_dsim *dsi)
> > > +{
> > > +	u32 reset_val = dsi->driver_data->reg_values[RESET_TYPE];
> > > +
> > > +	reinit_completion(&dsi->completed);
> > > +	samsung_dsim_write(dsi, DSIM_SWRST_REG, reset_val);
> > > +}
> > > +
> > > +#ifndef MHZ
> > > +#define MHZ	(1000*1000)
> > > +#endif
> > > +
> > > +static unsigned long samsung_dsim_pll_find_pms(struct samsung_dsim *dsi,
> > > +					       unsigned long fin,
> > > +					       unsigned long fout,
> > > +					       u8 *p, u16 *m, u8 *s)
> > > +{
> > > +	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
> > > +	unsigned long best_freq = 0;
> > > +	u32 min_delta = 0xffffffff;
> > > +	u8 p_min, p_max;
> > > +	u8 _p, best_p;
> > > +	u16 _m, best_m;
> > > +	u8 _s, best_s;
> > > +
> > > +	p_min = DIV_ROUND_UP(fin, (12 * MHZ));
> > > +	p_max = fin / (6 * MHZ);
> > > +
> > > +	for (_p = p_min; _p <= p_max; ++_p) {
> > > +		for (_s = 0; _s <= 5; ++_s) {
> > > +			u64 tmp;
> > > +			u32 delta;
> > > +
> > > +			tmp = (u64)fout * (_p << _s);
> > > +			do_div(tmp, fin);
> > > +			_m = tmp;
> > > +			if (_m < 41 || _m > 125)
> > > +				continue;
> > > +
> > > +			tmp = (u64)_m * fin;
> > > +			do_div(tmp, _p);
> > > +			if (tmp < 500 * MHZ ||
> > > +					tmp > driver_data->max_freq * MHZ)
> > > +				continue;
> > > +
> > > +			tmp = (u64)_m * fin;
> > > +			do_div(tmp, _p << _s);
> > > +
> > > +			delta = abs(fout - tmp);
> > > +			if (delta < min_delta) {
> > > +				best_p = _p;
> > > +				best_m = _m;
> > > +				best_s = _s;
> > > +				min_delta = delta;
> > > +				best_freq = tmp;
> > > +			}
> > > +		}
> > > +	}
> > > +
> > > +	if (best_freq) {
> > > +		*p = best_p;
> > > +		*m = best_m;
> > > +		*s = best_s;
> > > +	}
> > > +
> > > +	return best_freq;
> > > +}
> > > +
> > > +static unsigned long samsung_dsim_set_pll(struct samsung_dsim *dsi,
> > > +					  unsigned long freq)
> > > +{
> > > +	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
> > > +	unsigned long fin, fout;
> > > +	int timeout;
> > > +	u8 p, s;
> > > +	u16 m;
> > > +	u32 reg;
> > > +
> > > +	fin = dsi->pll_clk_rate;
> > > +	fout = samsung_dsim_pll_find_pms(dsi, fin, freq, &p, &m, &s);
> > > +	if (!fout) {
> > > +		dev_err(dsi->dev,
> > > +			"failed to find PLL PMS for requested frequency\n");
> > > +		return 0;
> > > +	}
> > > +	dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s);
> > > +
> > > +	writel(driver_data->reg_values[PLL_TIMER],
> > > +			dsi->reg_base + driver_data->plltmr_reg);
> > > +
> > > +	reg = DSIM_PLL_EN | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s);
> > > +
> > > +	if (driver_data->has_freqband) {
> > > +		static const unsigned long freq_bands[] = {
> > > +			100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ,
> > > +			270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ,
> > > +			510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ,
> > > +			770 * MHZ, 870 * MHZ, 950 * MHZ,
> > > +		};
> > > +		int band;
> > > +
> > > +		for (band = 0; band < ARRAY_SIZE(freq_bands); ++band)
> > > +			if (fout < freq_bands[band])
> > > +				break;
> > > +
> > > +		dev_dbg(dsi->dev, "band %d\n", band);
> > > +
> > > +		reg |= DSIM_FREQ_BAND(band);
> > > +	}
> > > +
> > > +	samsung_dsim_write(dsi, DSIM_PLLCTRL_REG, reg);
> > > +
> > > +	timeout = 1000;
> > > +	do {
> > > +		if (timeout-- == 0) {
> > > +			dev_err(dsi->dev, "PLL failed to stabilize\n");
> > > +			return 0;
> > > +		}
> > > +		reg = samsung_dsim_read(dsi, DSIM_STATUS_REG);
> > > +	} while ((reg & DSIM_PLL_STABLE) == 0);
> > > +
> > > +	return fout;
> > > +}
> > > +
> > > +static int samsung_dsim_enable_clock(struct samsung_dsim *dsi)
> > > +{
> > > +	unsigned long hs_clk, byte_clk, esc_clk;
> > > +	unsigned long esc_div;
> > > +	u32 reg;
> > > +
> > > +	hs_clk = samsung_dsim_set_pll(dsi, dsi->burst_clk_rate);
> > > +	if (!hs_clk) {
> > > +		dev_err(dsi->dev, "failed to configure DSI PLL\n");
> > > +		return -EFAULT;
> > > +	}
> > > +
> > > +	byte_clk = hs_clk / 8;
> > > +	esc_div = DIV_ROUND_UP(byte_clk, dsi->esc_clk_rate);
> > > +	esc_clk = byte_clk / esc_div;
> > > +
> > > +	if (esc_clk > 20 * MHZ) {
> > > +		++esc_div;
> > > +		esc_clk = byte_clk / esc_div;
> > > +	}
> > > +
> > > +	dev_dbg(dsi->dev, "hs_clk = %lu, byte_clk = %lu, esc_clk = %lu\n",
> > > +		hs_clk, byte_clk, esc_clk);
> > > +
> > > +	reg = samsung_dsim_read(dsi, DSIM_CLKCTRL_REG);
> > > +	reg &= ~(DSIM_ESC_PRESCALER_MASK | DSIM_LANE_ESC_CLK_EN_CLK
> > > +			| DSIM_LANE_ESC_CLK_EN_DATA_MASK | DSIM_PLL_BYPASS
> > > +			| DSIM_BYTE_CLK_SRC_MASK);
> > > +	reg |= DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN
> > > +			| DSIM_ESC_PRESCALER(esc_div)
> > > +			| DSIM_LANE_ESC_CLK_EN_CLK
> > > +			| DSIM_LANE_ESC_CLK_EN_DATA(BIT(dsi->lanes) - 1)
> > > +			| DSIM_BYTE_CLK_SRC(0)
> > > +			| DSIM_TX_REQUEST_HSCLK;
> > > +	samsung_dsim_write(dsi, DSIM_CLKCTRL_REG, reg);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static void samsung_dsim_set_phy_ctrl(struct samsung_dsim *dsi)
> > > +{
> > > +	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
> > > +	const unsigned int *reg_values = driver_data->reg_values;
> > > +	u32 reg;
> > > +
> > > +	if (driver_data->has_freqband)
> > > +		return;
> > > +
> > > +	/* B D-PHY: D-PHY Master & Slave Analog Block control */
> > > +	reg = DSIM_PHYCTRL_ULPS_EXIT(reg_values[PHYCTRL_ULPS_EXIT]);
> > > +	if (reg_values[PHYCTRL_VREG_LP])
> > > +		reg |= DSIM_PHYCTRL_B_DPHYCTL_VREG_LP;
> > > +	if (reg_values[PHYCTRL_SLEW_UP])
> > > +		reg |= DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP;
> > > +	samsung_dsim_write(dsi, DSIM_PHYCTRL_REG, reg);
> > > +
> > > +	/*
> > > +	 * T LPX: Transmitted length of any Low-Power state period
> > > +	 * T HS-EXIT: Time that the transmitter drives LP-11 following a HS
> > > +	 *	burst
> > > +	 */
> > > +	reg = DSIM_PHYTIMING_LPX(reg_values[PHYTIMING_LPX]) |
> > > +		DSIM_PHYTIMING_HS_EXIT(reg_values[PHYTIMING_HS_EXIT]);
> > > +	samsung_dsim_write(dsi, DSIM_PHYTIMING_REG, reg);
> > > +
> > > +	/*
> > > +	 * T CLK-PREPARE: Time that the transmitter drives the Clock Lane LP-00
> > > +	 *	Line state immediately before the HS-0 Line state starting the
> > > +	 *	HS transmission
> > > +	 * T CLK-ZERO: Time that the transmitter drives the HS-0 state prior to
> > > +	 *	transmitting the Clock.
> > > +	 * T CLK_POST: Time that the transmitter continues to send HS clock
> > > +	 *	after the last associated Data Lane has transitioned to LP Mode
> > > +	 *	Interval is defined as the period from the end of T HS-TRAIL to
> > > +	 *	the beginning of T CLK-TRAIL
> > > +	 * T CLK-TRAIL: Time that the transmitter drives the HS-0 state after
> > > +	 *	the last payload clock bit of a HS transmission burst
> > > +	 */
> > > +	reg = DSIM_PHYTIMING1_CLK_PREPARE(reg_values[PHYTIMING_CLK_PREPARE]) |
> > > +		DSIM_PHYTIMING1_CLK_ZERO(reg_values[PHYTIMING_CLK_ZERO]) |
> > > +		DSIM_PHYTIMING1_CLK_POST(reg_values[PHYTIMING_CLK_POST]) |
> > > +		DSIM_PHYTIMING1_CLK_TRAIL(reg_values[PHYTIMING_CLK_TRAIL]);
> > > +	samsung_dsim_write(dsi, DSIM_PHYTIMING1_REG, reg);
> > > +
> > > +	/*
> > > +	 * T HS-PREPARE: Time that the transmitter drives the Data Lane LP-00
> > > +	 *	Line state immediately before the HS-0 Line state starting the
> > > +	 *	HS transmission
> > > +	 * T HS-ZERO: Time that the transmitter drives the HS-0 state prior to
> > > +	 *	transmitting the Sync sequence.
> > > +	 * T HS-TRAIL: Time that the transmitter drives the flipped differential
> > > +	 *	state after last payload data bit of a HS transmission burst
> > > +	 */
> > > +	reg = DSIM_PHYTIMING2_HS_PREPARE(reg_values[PHYTIMING_HS_PREPARE]) |
> > > +		DSIM_PHYTIMING2_HS_ZERO(reg_values[PHYTIMING_HS_ZERO]) |
> > > +		DSIM_PHYTIMING2_HS_TRAIL(reg_values[PHYTIMING_HS_TRAIL]);
> > > +	samsung_dsim_write(dsi, DSIM_PHYTIMING2_REG, reg);
> > > +}
> > > +
> > > +static void samsung_dsim_disable_clock(struct samsung_dsim *dsi)
> > > +{
> > > +	u32 reg;
> > > +
> > > +	reg = samsung_dsim_read(dsi, DSIM_CLKCTRL_REG);
> > > +	reg &= ~(DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK
> > > +			| DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN);
> > > +	samsung_dsim_write(dsi, DSIM_CLKCTRL_REG, reg);
> > > +
> > > +	reg = samsung_dsim_read(dsi, DSIM_PLLCTRL_REG);
> > > +	reg &= ~DSIM_PLL_EN;
> > > +	samsung_dsim_write(dsi, DSIM_PLLCTRL_REG, reg);
> > > +}
> > > +
> > > +static void samsung_dsim_enable_lane(struct samsung_dsim *dsi, u32 lane)
> > > +{
> > > +	u32 reg = samsung_dsim_read(dsi, DSIM_CONFIG_REG);
> > > +	reg |= (DSIM_NUM_OF_DATA_LANE(dsi->lanes - 1) | DSIM_LANE_EN_CLK |
> > > +			DSIM_LANE_EN(lane));
> > > +	samsung_dsim_write(dsi, DSIM_CONFIG_REG, reg);
> > > +}
> > > +
> > > +static int samsung_dsim_init_link(struct samsung_dsim *dsi)
> > > +{
> > > +	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
> > > +	int timeout;
> > > +	u32 reg;
> > > +	u32 lanes_mask;
> > > +
> > > +	/* Initialize FIFO pointers */
> > > +	reg = samsung_dsim_read(dsi, DSIM_FIFOCTRL_REG);
> > > +	reg &= ~0x1f;
> > > +	samsung_dsim_write(dsi, DSIM_FIFOCTRL_REG, reg);
> > > +
> > > +	usleep_range(9000, 11000);
> > > +
> > > +	reg |= 0x1f;
> > > +	samsung_dsim_write(dsi, DSIM_FIFOCTRL_REG, reg);
> > > +	usleep_range(9000, 11000);
> > > +
> > > +	/* DSI configuration */
> > > +	reg = 0;
> > > +
> > > +	/*
> > > +	 * The first bit of mode_flags specifies display configuration.
> > > +	 * If this bit is set[= MIPI_DSI_MODE_VIDEO], dsi will support video
> > > +	 * mode, otherwise it will support command mode.
> > > +	 */
> > > +	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> > > +		reg |= DSIM_VIDEO_MODE;
> > > +
> > > +		/*
> > > +		 * The user manual describes that following bits are ignored in
> > > +		 * command mode.
> > > +		 */
> > > +		if (!(dsi->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH))
> > > +			reg |= DSIM_MFLUSH_VS;
> > > +		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
> > > +			reg |= DSIM_SYNC_INFORM;
> > > +		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
> > > +			reg |= DSIM_BURST_MODE;
> > > +		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT)
> > > +			reg |= DSIM_AUTO_MODE;
> > > +		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSE)
> > > +			reg |= DSIM_HSE_MODE;
> > > +		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HFP))
> > > +			reg |= DSIM_HFP_MODE;
> > > +		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HBP))
> > > +			reg |= DSIM_HBP_MODE;
> > > +		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSA))
> > > +			reg |= DSIM_HSA_MODE;
> > > +	}
> > > +
> > > +	if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET))
> > > +		reg |= DSIM_EOT_DISABLE;
> > > +
> > > +	switch (dsi->format) {
> > > +	case MIPI_DSI_FMT_RGB888:
> > > +		reg |= DSIM_MAIN_PIX_FORMAT_RGB888;
> > > +		break;
> > > +	case MIPI_DSI_FMT_RGB666:
> > > +		reg |= DSIM_MAIN_PIX_FORMAT_RGB666;
> > > +		break;
> > > +	case MIPI_DSI_FMT_RGB666_PACKED:
> > > +		reg |= DSIM_MAIN_PIX_FORMAT_RGB666_P;
> > > +		break;
> > > +	case MIPI_DSI_FMT_RGB565:
> > > +		reg |= DSIM_MAIN_PIX_FORMAT_RGB565;
> > > +		break;
> > > +	default:
> > > +		dev_err(dsi->dev, "invalid pixel format\n");
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	/*
> > > +	 * Use non-continuous clock mode if the periparal wants and
> > > +	 * host controller supports
> > > +	 *
> > > +	 * In non-continous clock mode, host controller will turn off
> > > +	 * the HS clock between high-speed transmissions to reduce
> > > +	 * power consumption.
> > > +	 */
> > > +	if (driver_data->has_clklane_stop &&
> > > +			dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
> > > +		reg |= DSIM_CLKLANE_STOP;
> > > +	}
> > > +	samsung_dsim_write(dsi, DSIM_CONFIG_REG, reg);
> > > +
> > > +	lanes_mask = BIT(dsi->lanes) - 1;
> > > +	samsung_dsim_enable_lane(dsi, lanes_mask);
> > > +
> > > +	/* Check clock and data lane state are stop state */
> > > +	timeout = 100;
> > > +	do {
> > > +		if (timeout-- == 0) {
> > > +			dev_err(dsi->dev, "waiting for bus lanes timed out\n");
> > > +			return -EFAULT;
> > > +		}
> > > +
> > > +		reg = samsung_dsim_read(dsi, DSIM_STATUS_REG);
> > > +		if ((reg & DSIM_STOP_STATE_DAT(lanes_mask))
> > > +		    != DSIM_STOP_STATE_DAT(lanes_mask))
> > > +			continue;
> > > +	} while (!(reg & (DSIM_STOP_STATE_CLK | DSIM_TX_READY_HS_CLK)));
> > > +
> > > +	reg = samsung_dsim_read(dsi, DSIM_ESCMODE_REG);
> > > +	reg &= ~DSIM_STOP_STATE_CNT_MASK;
> > > +	reg |= DSIM_STOP_STATE_CNT(driver_data->reg_values[STOP_STATE_CNT]);
> > > +	samsung_dsim_write(dsi, DSIM_ESCMODE_REG, reg);
> > > +
> > > +	reg = DSIM_BTA_TIMEOUT(0xff) | DSIM_LPDR_TIMEOUT(0xffff);
> > > +	samsung_dsim_write(dsi, DSIM_TIMEOUT_REG, reg);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static void samsung_dsim_set_display_mode(struct samsung_dsim *dsi)
> > > +{
> > > +	struct drm_display_mode *m = &dsi->mode;
> > > +	unsigned int num_bits_resol = dsi->driver_data->num_bits_resol;
> > > +	u32 reg;
> > > +
> > > +	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> > > +		reg = DSIM_CMD_ALLOW(0xf)
> > > +			| DSIM_STABLE_VFP(m->vsync_start - m->vdisplay)
> > > +			| DSIM_MAIN_VBP(m->vtotal - m->vsync_end);
> > > +		samsung_dsim_write(dsi, DSIM_MVPORCH_REG, reg);
> > > +
> > > +		reg = DSIM_MAIN_HFP(m->hsync_start - m->hdisplay)
> > > +			| DSIM_MAIN_HBP(m->htotal - m->hsync_end);
> > > +		samsung_dsim_write(dsi, DSIM_MHPORCH_REG, reg);
> > > +
> > > +		reg = DSIM_MAIN_VSA(m->vsync_end - m->vsync_start)
> > > +			| DSIM_MAIN_HSA(m->hsync_end - m->hsync_start);
> > > +		samsung_dsim_write(dsi, DSIM_MSYNC_REG, reg);
> > > +	}
> > > +	reg =  DSIM_MAIN_HRESOL(m->hdisplay, num_bits_resol) |
> > > +		DSIM_MAIN_VRESOL(m->vdisplay, num_bits_resol);
> > > +
> > > +	samsung_dsim_write(dsi, DSIM_MDRESOL_REG, reg);
> > > +
> > > +	dev_dbg(dsi->dev, "LCD size = %dx%d\n", m->hdisplay, m->vdisplay);
> > > +}
> > > +
> > > +static void samsung_dsim_set_display_enable(struct samsung_dsim *dsi,
> > > +					    bool enable)
> > > +{
> > > +	u32 reg;
> > > +
> > > +	reg = samsung_dsim_read(dsi, DSIM_MDRESOL_REG);
> > > +	if (enable)
> > > +		reg |= DSIM_MAIN_STAND_BY;
> > > +	else
> > > +		reg &= ~DSIM_MAIN_STAND_BY;
> > > +	samsung_dsim_write(dsi, DSIM_MDRESOL_REG, reg);
> > > +}
> > > +
> > > +static int samsung_dsim_wait_for_hdr_fifo(struct samsung_dsim *dsi)
> > > +{
> > > +	int timeout = 2000;
> > > +
> > > +	do {
> > > +		u32 reg = samsung_dsim_read(dsi, DSIM_FIFOCTRL_REG);
> > > +
> > > +		if (!(reg & DSIM_SFR_HEADER_FULL))
> > > +			return 0;
> > > +
> > > +		if (!cond_resched())
> > > +			usleep_range(950, 1050);
> > > +	} while (--timeout);
> > > +
> > > +	return -ETIMEDOUT;
> > > +}
> > > +
> > > +static void samsung_dsim_set_cmd_lpm(struct samsung_dsim *dsi, bool lpm)
> > > +{
> > > +	u32 v = samsung_dsim_read(dsi, DSIM_ESCMODE_REG);
> > > +
> > > +	if (lpm)
> > > +		v |= DSIM_CMD_LPDT_LP;
> > > +	else
> > > +		v &= ~DSIM_CMD_LPDT_LP;
> > > +
> > > +	samsung_dsim_write(dsi, DSIM_ESCMODE_REG, v);
> > > +}
> > > +
> > > +static void samsung_dsim_force_bta(struct samsung_dsim *dsi)
> > > +{
> > > +	u32 v = samsung_dsim_read(dsi, DSIM_ESCMODE_REG);
> > > +	v |= DSIM_FORCE_BTA;
> > > +	samsung_dsim_write(dsi, DSIM_ESCMODE_REG, v);
> > > +}
> > > +
> > > +static void samsung_dsim_send_to_fifo(struct samsung_dsim *dsi,
> > > +				      struct samsung_dsim_transfer *xfer)
> > > +{
> > > +	struct device *dev = dsi->dev;
> > > +	struct mipi_dsi_packet *pkt = &xfer->packet;
> > > +	const u8 *payload = pkt->payload + xfer->tx_done;
> > > +	u16 length = pkt->payload_length - xfer->tx_done;
> > > +	bool first = !xfer->tx_done;
> > > +	u32 reg;
> > > +
> > > +	dev_dbg(dev, "< xfer %pK: tx len %u, done %u, rx len %u, done %u\n",
> > > +		xfer, length, xfer->tx_done, xfer->rx_len, xfer->rx_done);
> > > +
> > > +	if (length > DSI_TX_FIFO_SIZE)
> > > +		length = DSI_TX_FIFO_SIZE;
> > > +
> > > +	xfer->tx_done += length;
> > > +
> > > +	/* Send payload */
> > > +	while (length >= 4) {
> > > +		reg = get_unaligned_le32(payload);
> > > +		samsung_dsim_write(dsi, DSIM_PAYLOAD_REG, reg);
> > > +		payload += 4;
> > > +		length -= 4;
> > > +	}
> > > +
> > > +	reg = 0;
> > > +	switch (length) {
> > > +	case 3:
> > > +		reg |= payload[2] << 16;
> > > +		fallthrough;
> > > +	case 2:
> > > +		reg |= payload[1] << 8;
> > > +		fallthrough;
> > > +	case 1:
> > > +		reg |= payload[0];
> > > +		samsung_dsim_write(dsi, DSIM_PAYLOAD_REG, reg);
> > > +		break;
> > > +	}
> > > +
> > > +	/* Send packet header */
> > > +	if (!first)
> > > +		return;
> > > +
> > > +	reg = get_unaligned_le32(pkt->header);
> > > +	if (samsung_dsim_wait_for_hdr_fifo(dsi)) {
> > > +		dev_err(dev, "waiting for header FIFO timed out\n");
> > > +		return;
> > > +	}
> > > +
> > > +	if (NEQV(xfer->flags & MIPI_DSI_MSG_USE_LPM,
> > > +		 dsi->state & DSIM_STATE_CMD_LPM)) {
> > > +		samsung_dsim_set_cmd_lpm(dsi, xfer->flags & MIPI_DSI_MSG_USE_LPM);
> > > +		dsi->state ^= DSIM_STATE_CMD_LPM;
> > > +	}
> > > +
> > > +	samsung_dsim_write(dsi, DSIM_PKTHDR_REG, reg);
> > > +
> > > +	if (xfer->flags & MIPI_DSI_MSG_REQ_ACK)
> > > +		samsung_dsim_force_bta(dsi);
> > > +}
> > > +
> > > +static void samsung_dsim_read_from_fifo(struct samsung_dsim *dsi,
> > > +					struct samsung_dsim_transfer *xfer)
> > > +{
> > > +	u8 *payload = xfer->rx_payload + xfer->rx_done;
> > > +	bool first = !xfer->rx_done;
> > > +	struct device *dev = dsi->dev;
> > > +	u16 length;
> > > +	u32 reg;
> > > +
> > > +	if (first) {
> > > +		reg = samsung_dsim_read(dsi, DSIM_RXFIFO_REG);
> > > +
> > > +		switch (reg & 0x3f) {
> > > +		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
> > > +		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
> > > +			if (xfer->rx_len >= 2) {
> > > +				payload[1] = reg >> 16;
> > > +				++xfer->rx_done;
> > > +			}
> > > +			fallthrough;
> > > +		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
> > > +		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
> > > +			payload[0] = reg >> 8;
> > > +			++xfer->rx_done;
> > > +			xfer->rx_len = xfer->rx_done;
> > > +			xfer->result = 0;
> > > +			goto clear_fifo;
> > > +		case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
> > > +			dev_err(dev, "DSI Error Report: 0x%04x\n",
> > > +				(reg >> 8) & 0xffff);
> > > +			xfer->result = 0;
> > > +			goto clear_fifo;
> > > +		}
> > > +
> > > +		length = (reg >> 8) & 0xffff;
> > > +		if (length > xfer->rx_len) {
> > > +			dev_err(dev,
> > > +				"response too long (%u > %u bytes), stripping\n",
> > > +				xfer->rx_len, length);
> > > +			length = xfer->rx_len;
> > > +		} else if (length < xfer->rx_len)
> > > +			xfer->rx_len = length;
> > > +	}
> > > +
> > > +	length = xfer->rx_len - xfer->rx_done;
> > > +	xfer->rx_done += length;
> > > +
> > > +	/* Receive payload */
> > > +	while (length >= 4) {
> > > +		reg = samsung_dsim_read(dsi, DSIM_RXFIFO_REG);
> > > +		payload[0] = (reg >>  0) & 0xff;
> > > +		payload[1] = (reg >>  8) & 0xff;
> > > +		payload[2] = (reg >> 16) & 0xff;
> > > +		payload[3] = (reg >> 24) & 0xff;
> > > +		payload += 4;
> > > +		length -= 4;
> > > +	}
> > > +
> > > +	if (length) {
> > > +		reg = samsung_dsim_read(dsi, DSIM_RXFIFO_REG);
> > > +		switch (length) {
> > > +		case 3:
> > > +			payload[2] = (reg >> 16) & 0xff;
> > > +			fallthrough;
> > > +		case 2:
> > > +			payload[1] = (reg >> 8) & 0xff;
> > > +			fallthrough;
> > > +		case 1:
> > > +			payload[0] = reg & 0xff;
> > > +		}
> > > +	}
> > > +
> > > +	if (xfer->rx_done == xfer->rx_len)
> > > +		xfer->result = 0;
> > > +
> > > +clear_fifo:
> > > +	length = DSI_RX_FIFO_SIZE / 4;
> > > +	do {
> > > +		reg = samsung_dsim_read(dsi, DSIM_RXFIFO_REG);
> > > +		if (reg == DSI_RX_FIFO_EMPTY)
> > > +			break;
> > > +	} while (--length);
> > > +}
> > > +
> > > +static void samsung_dsim_transfer_start(struct samsung_dsim *dsi)
> > > +{
> > > +	unsigned long flags;
> > > +	struct samsung_dsim_transfer *xfer;
> > > +	bool start = false;
> > > +
> > > +again:
> > > +	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > > +
> > > +	if (list_empty(&dsi->transfer_list)) {
> > > +		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > > +		return;
> > > +	}
> > > +
> > > +	xfer = list_first_entry(&dsi->transfer_list,
> > > +					struct samsung_dsim_transfer, list);
> > > +
> > > +	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > > +
> > > +	if (xfer->packet.payload_length &&
> > > +	    xfer->tx_done == xfer->packet.payload_length)
> > > +		/* waiting for RX */
> > > +		return;
> > > +
> > > +	samsung_dsim_send_to_fifo(dsi, xfer);
> > > +
> > > +	if (xfer->packet.payload_length || xfer->rx_len)
> > > +		return;
> > > +
> > > +	xfer->result = 0;
> > > +	complete(&xfer->completed);
> > > +
> > > +	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > > +
> > > +	list_del_init(&xfer->list);
> > > +	start = !list_empty(&dsi->transfer_list);
> > > +
> > > +	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > > +
> > > +	if (start)
> > > +		goto again;
> > > +}
> > > +
> > > +static bool samsung_dsim_transfer_finish(struct samsung_dsim *dsi)
> > > +{
> > > +	struct samsung_dsim_transfer *xfer;
> > > +	unsigned long flags;
> > > +	bool start = true;
> > > +
> > > +	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > > +
> > > +	if (list_empty(&dsi->transfer_list)) {
> > > +		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > > +		return false;
> > > +	}
> > > +
> > > +	xfer = list_first_entry(&dsi->transfer_list,
> > > +					struct samsung_dsim_transfer, list);
> > > +
> > > +	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > > +
> > > +	dev_dbg(dsi->dev,
> > > +		"> xfer %pK, tx_len %zu, tx_done %u, rx_len %u, rx_done %u\n",
> > > +		xfer, xfer->packet.payload_length, xfer->tx_done, xfer->rx_len,
> > > +		xfer->rx_done);
> > > +
> > > +	if (xfer->tx_done != xfer->packet.payload_length)
> > > +		return true;
> > > +
> > > +	if (xfer->rx_done != xfer->rx_len)
> > > +		samsung_dsim_read_from_fifo(dsi, xfer);
> > > +
> > > +	if (xfer->rx_done != xfer->rx_len)
> > > +		return true;
> > > +
> > > +	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > > +
> > > +	list_del_init(&xfer->list);
> > > +	start = !list_empty(&dsi->transfer_list);
> > > +
> > > +	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > > +
> > > +	if (!xfer->rx_len)
> > > +		xfer->result = 0;
> > > +	complete(&xfer->completed);
> > > +
> > > +	return start;
> > > +}
> > > +
> > > +static void samsung_dsim_remove_transfer(struct samsung_dsim *dsi,
> > > +					 struct samsung_dsim_transfer *xfer)
> > > +{
> > > +	unsigned long flags;
> > > +	bool start;
> > > +
> > > +	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > > +
> > > +	if (!list_empty(&dsi->transfer_list) &&
> > > +	    xfer == list_first_entry(&dsi->transfer_list,
> > > +				     struct samsung_dsim_transfer, list)) {
> > > +		list_del_init(&xfer->list);
> > > +		start = !list_empty(&dsi->transfer_list);
> > > +		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > > +		if (start)
> > > +			samsung_dsim_transfer_start(dsi);
> > > +		return;
> > > +	}
> > > +
> > > +	list_del_init(&xfer->list);
> > > +
> > > +	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > > +}
> > > +
> > > +static int samsung_dsim_transfer(struct samsung_dsim *dsi,
> > > +				 struct samsung_dsim_transfer *xfer)
> > > +{
> > > +	unsigned long flags;
> > > +	bool stopped;
> > > +
> > > +	xfer->tx_done = 0;
> > > +	xfer->rx_done = 0;
> > > +	xfer->result = -ETIMEDOUT;
> > > +	init_completion(&xfer->completed);
> > > +
> > > +	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > > +
> > > +	stopped = list_empty(&dsi->transfer_list);
> > > +	list_add_tail(&xfer->list, &dsi->transfer_list);
> > > +
> > > +	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > > +
> > > +	if (stopped)
> > > +		samsung_dsim_transfer_start(dsi);
> > > +
> > > +	wait_for_completion_timeout(&xfer->completed,
> > > +				    msecs_to_jiffies(DSI_XFER_TIMEOUT_MS));
> > > +	if (xfer->result == -ETIMEDOUT) {
> > > +		struct mipi_dsi_packet *pkt = &xfer->packet;
> > > +		samsung_dsim_remove_transfer(dsi, xfer);
> > > +		dev_err(dsi->dev, "xfer timed out: %*ph %*ph\n", 4, pkt->header,
> > > +			(int)pkt->payload_length, pkt->payload);
> > > +		return -ETIMEDOUT;
> > > +	}
> > > +
> > > +	/* Also covers hardware timeout condition */
> > > +	return xfer->result;
> > > +}
> > > +
> > > +static irqreturn_t samsung_dsim_irq(int irq, void *dev_id)
> > > +{
> > > +	struct samsung_dsim *dsi = dev_id;
> > > +	u32 status;
> > > +
> > > +	status = samsung_dsim_read(dsi, DSIM_INTSRC_REG);
> > > +	if (!status) {
> > > +		static unsigned long int j;
> > > +		if (printk_timed_ratelimit(&j, 500))
> > > +			dev_warn(dsi->dev, "spurious interrupt\n");
> > > +		return IRQ_HANDLED;
> > > +	}
> > > +	samsung_dsim_write(dsi, DSIM_INTSRC_REG, status);
> > > +
> > > +	if (status & DSIM_INT_SW_RST_RELEASE) {
> > > +		u32 mask = ~(DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY |
> > > +			DSIM_INT_SFR_HDR_FIFO_EMPTY | DSIM_INT_RX_ECC_ERR |
> > > +			DSIM_INT_SW_RST_RELEASE);
> > > +		samsung_dsim_write(dsi, DSIM_INTMSK_REG, mask);
> > > +		complete(&dsi->completed);
> > > +		return IRQ_HANDLED;
> > > +	}
> > > +
> > > +	if (!(status & (DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY |
> > > +			DSIM_INT_PLL_STABLE)))
> > > +		return IRQ_HANDLED;
> > > +
> > > +	if (samsung_dsim_transfer_finish(dsi))
> > > +		samsung_dsim_transfer_start(dsi);
> > > +
> > > +	return IRQ_HANDLED;
> > > +}
> > > +
> > > +static irqreturn_t samsung_dsim_te_irq_handler(int irq, void *dev_id)
> > > +{
> > > +	struct samsung_dsim *dsi = dev_id;
> > > +	const struct samsung_dsim_host_ops *ops = dsi->driver_data->host_ops;
> > > +
> > > +	if (ops && ops->te_handler &&
> > > +	    (dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
> > > +		ops->te_handler(dsi->dsi_host.dev);
> > > +
> > > +	return IRQ_HANDLED;
> > > +}
> > > +
> > > +static void samsung_dsim_enable_irq(struct samsung_dsim *dsi)
> > > +{
> > > +	enable_irq(dsi->irq);
> > > +
> > > +	if (gpio_is_valid(dsi->te_gpio))
> > > +		enable_irq(gpio_to_irq(dsi->te_gpio));
> > > +}
> > > +
> > > +static void samsung_dsim_disable_irq(struct samsung_dsim *dsi)
> > > +{
> > > +	if (gpio_is_valid(dsi->te_gpio))
> > > +		disable_irq(gpio_to_irq(dsi->te_gpio));
> > > +
> > > +	disable_irq(dsi->irq);
> > > +}
> > > +
> > > +static int samsung_dsim_init(struct samsung_dsim *dsi)
> > > +{
> > > +	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
> > > +
> > > +	samsung_dsim_reset(dsi);
> > > +	samsung_dsim_enable_irq(dsi);
> > > +
> > > +	if (driver_data->reg_values[RESET_TYPE] == DSIM_FUNCRST)
> > > +		samsung_dsim_enable_lane(dsi, BIT(dsi->lanes) - 1);
> > > +
> > > +	samsung_dsim_enable_clock(dsi);
> > > +	if (driver_data->wait_for_reset)
> > > +		samsung_dsim_wait_for_reset(dsi);
> > > +	samsung_dsim_set_phy_ctrl(dsi);
> > > +	samsung_dsim_init_link(dsi);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int samsung_dsim_register_te_irq(struct samsung_dsim *dsi,
> > > +					struct device *panel)
> > > +{
> > > +	int ret;
> > > +	int te_gpio_irq;
> > > +
> > > +	dsi->te_gpio = of_get_named_gpio(panel->of_node, "te-gpios", 0);
> > > +	if (dsi->te_gpio == -ENOENT)
> > > +		return 0;
> > > +
> > > +	if (!gpio_is_valid(dsi->te_gpio)) {
> > > +		ret = dsi->te_gpio;
> > > +		dev_err(dsi->dev, "cannot get te-gpios, %d\n", ret);
> > > +		goto out;
> > > +	}
> > > +
> > > +	ret = gpio_request(dsi->te_gpio, "te_gpio");
> > > +	if (ret) {
> > > +		dev_err(dsi->dev, "gpio request failed with %d\n", ret);
> > > +		goto out;
> > > +	}
> > > +
> > > +	te_gpio_irq = gpio_to_irq(dsi->te_gpio);
> > > +	irq_set_status_flags(te_gpio_irq, IRQ_NOAUTOEN);
> > > +
> > > +	ret = request_threaded_irq(te_gpio_irq, samsung_dsim_te_irq_handler,
> > > +				   NULL, IRQF_TRIGGER_RISING, "TE", dsi);
> > > +	if (ret) {
> > > +		dev_err(dsi->dev, "request interrupt failed with %d\n", ret);
> > > +		gpio_free(dsi->te_gpio);
> > > +		goto out;
> > > +	}
> > > +
> > > +out:
> > > +	return ret;
> > > +}
> > > +
> > > +static void samsung_dsim_unregister_te_irq(struct samsung_dsim *dsi)
> > > +{
> > > +	if (gpio_is_valid(dsi->te_gpio)) {
> > > +		free_irq(gpio_to_irq(dsi->te_gpio), dsi);
> > > +		gpio_free(dsi->te_gpio);
> > > +		dsi->te_gpio = -ENOENT;
> > > +	}
> > > +}
> > > +
> > > +static void samsung_dsim_enable(struct samsung_dsim *dsi)
> > > +{
> > > +	struct drm_bridge *iter;
> > > +	int ret;
> > > +
> > > +	if (dsi->state & DSIM_STATE_ENABLED)
> > > +		return;
> > > +
> > > +	pm_runtime_get_sync(dsi->dev);
> > > +	dsi->state |= DSIM_STATE_ENABLED;
> > > +
> > > +	if (dsi->panel) {
> > > +		ret = drm_panel_prepare(dsi->panel);
> > > +		if (ret < 0)
> > > +			goto err_put_sync;
> > > +	} else {
> > > +		list_for_each_entry_reverse(iter, &dsi->bridge_chain,
> > > +					    chain_node) {
> > > +			if (iter->funcs->pre_enable)
> > > +				iter->funcs->pre_enable(iter);
> > > +		}
> > > +	}
> > > +
> > > +	samsung_dsim_set_display_mode(dsi);
> > > +	samsung_dsim_set_display_enable(dsi, true);
> > > +
> > > +	if (dsi->panel) {
> > > +		ret = drm_panel_enable(dsi->panel);
> > > +		if (ret < 0)
> > > +			goto err_display_disable;
> > > +	} else {
> > > +		list_for_each_entry(iter, &dsi->bridge_chain, chain_node) {
> > > +			if (iter->funcs->enable)
> > > +				iter->funcs->enable(iter);
> > > +		}
> > > +	}
> > > +
> > > +	dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
> > > +	return;
> > > +
> > > +err_display_disable:
> > > +	samsung_dsim_set_display_enable(dsi, false);
> > > +	drm_panel_unprepare(dsi->panel);
> > > +
> > > +err_put_sync:
> > > +	dsi->state &= ~DSIM_STATE_ENABLED;
> > > +	pm_runtime_put(dsi->dev);
> > > +}
> > > +
> > > +static void samsung_dsim_disable(struct samsung_dsim *dsi)
> > > +{
> > > +	struct drm_bridge *iter;
> > > +
> > > +	if (!(dsi->state & DSIM_STATE_ENABLED))
> > > +		return;
> > > +
> > > +	dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
> > > +
> > > +	drm_panel_disable(dsi->panel);
> > > +
> > > +	list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) {
> > > +		if (iter->funcs->disable)
> > > +			iter->funcs->disable(iter);
> > > +	}
> > > +
> > > +	samsung_dsim_set_display_enable(dsi, false);
> > > +	drm_panel_unprepare(dsi->panel);
> > > +
> > > +	list_for_each_entry(iter, &dsi->bridge_chain, chain_node) {
> > > +		if (iter->funcs->post_disable)
> > > +			iter->funcs->post_disable(iter);
> > > +	}
> > > +
> > > +	dsi->state &= ~DSIM_STATE_ENABLED;
> > > +	pm_runtime_put_sync(dsi->dev);
> > > +}
> > > +
> > > +static enum drm_connector_status
> > > +samsung_dsim_detect(struct drm_connector *connector, bool force)
> > > +{
> > > +	return connector->status;
> > > +}
> > > +
> > > +static void samsung_dsim_connector_destroy(struct drm_connector *connector)
> > > +{
> > > +	drm_connector_unregister(connector);
> > > +	drm_connector_cleanup(connector);
> > > +	connector->dev = NULL;
> > > +}
> > > +
> > > +static const struct drm_connector_funcs samsung_dsim_connector_funcs = {
> > > +	.detect = samsung_dsim_detect,
> > > +	.fill_modes = drm_helper_probe_single_connector_modes,
> > > +	.destroy = samsung_dsim_connector_destroy,
> > > +	.reset = drm_atomic_helper_connector_reset,
> > > +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> > > +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> > > +};
> > > +
> > > +static int samsung_dsim_get_modes(struct drm_connector *connector)
> > > +{
> > > +	struct samsung_dsim *dsi = connector_to_dsi(connector);
> > > +
> > > +	if (dsi->panel)
> > > +		return drm_panel_get_modes(dsi->panel, connector);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static const struct drm_connector_helper_funcs samsung_dsim_connector_helper_funcs = {
> > > +	.get_modes = samsung_dsim_get_modes,
> > > +};
> > > +
> > > +static int samsung_dsim_create_connector(struct samsung_dsim *dsi)
> > > +{
> > > +	struct drm_connector *connector = &dsi->connector;
> > > +	struct drm_device *drm = dsi->bridge.dev;
> > > +	int ret;
> > > +
> > > +	connector->polled = DRM_CONNECTOR_POLL_HPD;
> > > +
> > > +	ret = drm_connector_init(drm, connector, &samsung_dsim_connector_funcs,
> > > +				 DRM_MODE_CONNECTOR_DSI);
> > > +	if (ret) {
> > > +		DRM_DEV_ERROR(dsi->dev,
> > > +			      "Failed to initialize connector with drm\n");
> > > +		return ret;
> > > +	}
> > > +
> > > +	connector->status = connector_status_disconnected;
> > > +	drm_connector_helper_add(connector, &samsung_dsim_connector_helper_funcs);
> > > +	drm_connector_attach_encoder(connector, dsi->bridge.encoder);
> > > +	if (!drm->registered)
> > > +		return 0;
> > > +
> > > +	connector->funcs->reset(connector);
> > > +	drm_connector_register(connector);
> > > +	return 0;
> > > +}
> > > +
> > > +static int samsung_dsim_bridge_attach(struct drm_bridge *bridge,
> > > +				      enum drm_bridge_attach_flags flags)
> > > +{
> > > +	struct samsung_dsim *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 = samsung_dsim_create_connector(dsi);
> > > +		if (ret)
> > > +			return ret;
> > > +
> > > +		if (dsi->panel) {
> > > +			dsi->connector.status = connector_status_connected;
> > > +		}
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static void samsung_dsim_bridge_detach(struct drm_bridge *bridge)
> > > +{
> > > +	struct samsung_dsim *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);
> > > +		samsung_dsim_disable(dsi);
> > > +		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 void samsung_dsim_bridge_enable(struct drm_bridge *bridge)
> > > +{
> > > +	struct samsung_dsim *dsi = bridge->driver_private;
> > > +
> > > +	samsung_dsim_enable(dsi);
> > > +}
> > > +
> > > +static void samsung_dsim_bridge_disable(struct drm_bridge *bridge)
> > > +{
> > > +	struct samsung_dsim *dsi = bridge->driver_private;
> > > +
> > > +	samsung_dsim_disable(dsi);
> > > +}
> > > +
> > > +static void samsung_dsim_bridge_mode_set(struct drm_bridge *bridge,
> > > +					 const struct drm_display_mode *mode,
> > > +					 const struct drm_display_mode *adjusted_mode)
> > > +{
> > > +	struct samsung_dsim *dsi = bridge->driver_private;
> > > +
> > > +	/* The mode is set when actually enabling the device. */
> > > +	drm_mode_copy(&dsi->mode, adjusted_mode);
> > > +}
> > > +
> > > +static const struct drm_bridge_funcs samsung_dsim_bridge_funcs = {
> > > +	.attach = samsung_dsim_bridge_attach,
> > > +	.detach = samsung_dsim_bridge_detach,
> > > +	.enable = samsung_dsim_bridge_enable,
> > > +	.disable = samsung_dsim_bridge_disable,
> > > +	.mode_set = samsung_dsim_bridge_mode_set,
> > > +};
> > > +
> > > +static int samsung_dsim_host_attach(struct mipi_dsi_host *host,
> > > +				    struct mipi_dsi_device *device)
> > > +{
> > > +	struct samsung_dsim *dsi = host_to_dsi(host);
> > > +	const struct samsung_dsim_host_ops *ops = dsi->driver_data->host_ops;
> > > +	struct drm_bridge *out_bridge;
> > > +
> > > +	out_bridge = of_drm_find_bridge(device->dev.of_node);
> > > +	if (out_bridge) {
> > > +		dsi->out_bridge = out_bridge;
> > > +	} else {
> > > +		dsi->panel = of_drm_find_panel(device->dev.of_node);
> > > +		if (IS_ERR(dsi->panel))
> > > +			dsi->panel = NULL;
> > > +		else
> > > +			dsi->connector.status = connector_status_connected;
> > > +	}
> > > +
> > > +	/*
> > > +	 * This is a temporary solution and should be made by more generic way.
> > > +	 *
> > > +	 * If attached panel device is for command mode one, dsi should register
> > > +	 * TE interrupt handler.
> > > +	 */
> > > +	if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO)) {
> > > +		int ret = samsung_dsim_register_te_irq(dsi, &device->dev);
> > > +		if (ret)
> > > +			return ret;
> > > +	}
> > > +
> > > +	dsi->lanes = device->lanes;
> > > +	dsi->format = device->format;
> > > +	dsi->mode_flags = device->mode_flags;
> > > +
> > > +	if (ops && ops->attach)
> > > +		ops->attach(dsi->dsi_host.dev, device);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int samsung_dsim_host_detach(struct mipi_dsi_host *host,
> > > +				    struct mipi_dsi_device *device)
> > > +{
> > > +	struct samsung_dsim *dsi = host_to_dsi(host);
> > > +	const struct samsung_dsim_host_ops *ops = dsi->driver_data->host_ops;
> > > +
> > > +	samsung_dsim_unregister_te_irq(dsi);
> > > +
> > > +	if (ops && ops->detach)
> > > +		ops->detach(dsi->dsi_host.dev, device);
> > > +
> > > +	samsung_dsim_unregister_te_irq(dsi);
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static ssize_t samsung_dsim_host_transfer(struct mipi_dsi_host *host,
> > > +					  const struct mipi_dsi_msg *msg)
> > > +{
> > > +	struct samsung_dsim *dsi = host_to_dsi(host);
> > > +	struct samsung_dsim_transfer xfer;
> > > +	int ret;
> > > +
> > > +	if (!(dsi->state & DSIM_STATE_ENABLED))
> > > +		return -EINVAL;
> > > +
> > > +	if (!(dsi->state & DSIM_STATE_INITIALIZED)) {
> > > +		ret = samsung_dsim_init(dsi);
> > > +		if (ret)
> > > +			return ret;
> > > +		dsi->state |= DSIM_STATE_INITIALIZED;
> > > +	}
> > > +
> > > +	ret = mipi_dsi_create_packet(&xfer.packet, msg);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	xfer.rx_len = msg->rx_len;
> > > +	xfer.rx_payload = msg->rx_buf;
> > > +	xfer.flags = msg->flags;
> > > +
> > > +	ret = samsung_dsim_transfer(dsi, &xfer);
> > > +	return (ret < 0) ? ret : xfer.rx_done;
> > > +}
> > > +
> > > +static const struct mipi_dsi_host_ops samsung_dsim_ops = {
> > > +	.attach = samsung_dsim_host_attach,
> > > +	.detach = samsung_dsim_host_detach,
> > > +	.transfer = samsung_dsim_host_transfer,
> > > +};
> > > +
> > > +static int samsung_dsim_of_read_u32(const struct device_node *np,
> > > +				    const char *propname, u32 *out_value)
> > > +{
> > > +	int ret = of_property_read_u32(np, propname, out_value);
> > > +
> > > +	if (ret < 0)
> > > +		pr_err("%pOF: failed to get '%s' property\n", np, propname);
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +static int samsung_dsim_parse_dt(struct samsung_dsim *dsi)
> > > +{
> > > +	struct device *dev = dsi->dev;
> > > +	struct device_node *node = dev->of_node;
> > > +	int ret;
> > > +
> > > +	ret = samsung_dsim_of_read_u32(node, "samsung,pll-clock-frequency",
> > > +				       &dsi->pll_clk_rate);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	ret = samsung_dsim_of_read_u32(node, "samsung,burst-clock-frequency",
> > > +				       &dsi->burst_clk_rate);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	ret = samsung_dsim_of_read_u32(node, "samsung,esc-clock-frequency",
> > > +				       &dsi->esc_clk_rate);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static struct samsung_dsim *__samsung_dsim_probe(struct platform_device *pdev)
> > > +{
> > > +	struct device *dev = &pdev->dev;
> > > +	struct drm_bridge *bridge;
> > > +	struct resource *res;
> > > +	struct samsung_dsim *dsi;
> > > +	int ret, i;
> > > +
> > > +	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
> > > +	if (!dsi)
> > > +		return ERR_PTR(-ENOMEM);
> > > +
> > > +	/* To be checked as invalid one */
> > > +	dsi->te_gpio = -ENOENT;
> > > +
> > > +	init_completion(&dsi->completed);
> > > +	spin_lock_init(&dsi->transfer_lock);
> > > +	INIT_LIST_HEAD(&dsi->transfer_list);
> > > +	INIT_LIST_HEAD(&dsi->bridge_chain);
> > > +
> > > +	dsi->dsi_host.ops = &samsung_dsim_ops;
> > > +	dsi->dsi_host.dev = dev;
> > > +
> > > +	dsi->dev = dev;
> > > +	dsi->driver_data = of_device_get_match_data(dev);
> > > +
> > > +	dsi->supplies[0].supply = "vddcore";
> > > +	dsi->supplies[1].supply = "vddio";
> > > +	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(dsi->supplies),
> > > +				      dsi->supplies);
> > > +	if (ret) {
> > > +		if (ret != -EPROBE_DEFER)
> > > +			dev_info(dev, "failed to get regulators: %d\n", ret);
> > > +		return ERR_PTR(ret);
> > > +	}
> > > +
> > > +	dsi->clks = devm_kcalloc(dev,
> > > +			dsi->driver_data->num_clks, sizeof(*dsi->clks),
> > > +			GFP_KERNEL);
> > > +	if (!dsi->clks)
> > > +		return ERR_PTR(-ENOMEM);
> > > +
> > > +	for (i = 0; i < dsi->driver_data->num_clks; i++) {
> > > +		dsi->clks[i] = devm_clk_get(dev, clk_names[i]);
> > > +		if (IS_ERR(dsi->clks[i])) {
> > > +			if (strcmp(clk_names[i], "sclk_mipi") == 0) {
> > > +				dsi->clks[i] = devm_clk_get(dev,
> > > +							OLD_SCLK_MIPI_CLK_NAME);
> > > +				if (!IS_ERR(dsi->clks[i]))
> > > +					continue;
> > > +			}
> > > +
> > > +			dev_info(dev, "failed to get the clock: %s\n",
> > > +					clk_names[i]);
> > > +			return ERR_PTR(PTR_ERR(dsi->clks[i]));
> > > +		}
> > > +	}
> > > +
> > > +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > > +	dsi->reg_base = devm_ioremap_resource(dev, res);
> > > +	if (IS_ERR(dsi->reg_base)) {
> > > +		dev_err(dev, "failed to remap io region\n");
> > > +		return dsi->reg_base;
> > > +	}
> > > +
> > > +	dsi->phy = devm_phy_get(dev, "dsim");
> > > +	if (IS_ERR(dsi->phy)) {
> > > +		dev_info(dev, "failed to get dsim phy\n");
> > > +		return ERR_PTR(PTR_ERR(dsi->phy));
> > > +	}
> > > +
> > > +	dsi->irq = platform_get_irq(pdev, 0);
> > > +	if (dsi->irq < 0)
> > > +		return ERR_PTR(dsi->irq);
> > > +
> > > +	irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
> > > +	ret = devm_request_threaded_irq(dev, dsi->irq, NULL,
> > > +					samsung_dsim_irq, IRQF_ONESHOT,
> > > +					dev_name(dev), dsi);
> > > +	if (ret) {
> > > +		dev_err(dev, "failed to request dsi irq\n");
> > > +		return ERR_PTR(ret);
> > > +	}
> > > +
> > > +	ret = samsung_dsim_parse_dt(dsi);
> > > +	if (ret)
> > > +		return ERR_PTR(ret);
> > > +
> > > +	ret = mipi_dsi_host_register(&dsi->dsi_host);
> > > +	if (ret)
> > > +		return ERR_PTR(ret);
> > > +
> > > +	bridge = &dsi->bridge;
> > > +	bridge->driver_private = dsi;
> > > +	bridge->funcs = &samsung_dsim_bridge_funcs;
> > > +	bridge->of_node = dev->of_node;
> > > +	drm_bridge_add(bridge);
> > > +
> > > +	return dsi;
> > > +}
> > > +
> > > +static void __samsung_dsim_remove(struct samsung_dsim *dsi)
> > > +{
> > > +	drm_bridge_remove(&dsi->bridge);
> > > +
> > > +	mipi_dsi_host_unregister(&dsi->dsi_host);
> > > +}
> > > +
> > > +/*
> > > + * Probe/remove API, used from platforms based on the DRM bridge API.
> > > + */
> > > +struct samsung_dsim *samsung_dsim_probe(struct platform_device *pdev)
> > > +{
> > > +	return __samsung_dsim_probe(pdev);
> > > +}
> > > +EXPORT_SYMBOL_GPL(samsung_dsim_probe);
> > > +
> > > +void samsung_dsim_remove(struct samsung_dsim *dsi)
> > > +{
> > > +	return __samsung_dsim_remove(dsi);
> > > +}
> > > +EXPORT_SYMBOL_GPL(samsung_dsim_remove);
> > > +
> > > +/*
> > > + * Bind/unbind API, used from platforms based on the component framework.
> > > + */
> > > +int samsung_dsim_bind(struct samsung_dsim *dsi, struct drm_encoder *encoder)
> > > +{
> > > +	struct drm_bridge *previous = drm_bridge_chain_get_first_bridge(encoder);
> > > +
> > > +	return drm_bridge_attach(encoder, &dsi->bridge, previous, 0);
> > > +}
> > > +EXPORT_SYMBOL_GPL(samsung_dsim_bind);
> > > +
> > > +void samsung_dsim_unbind(struct samsung_dsim *dsi)
> > > +{
> > > +	samsung_dsim_disable(dsi);
> > > +}
> > > +EXPORT_SYMBOL_GPL(samsung_dsim_unbind);
> > > +
> > > +int samsung_dsim_suspend(struct samsung_dsim *dsi)
> > > +{
> > > +	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
> > > +	int ret, i;
> > > +
> > > +	usleep_range(10000, 20000);
> > > +
> > > +	if (dsi->state & DSIM_STATE_INITIALIZED) {
> > > +		dsi->state &= ~DSIM_STATE_INITIALIZED;
> > > +
> > > +		samsung_dsim_disable_clock(dsi);
> > > +
> > > +		samsung_dsim_disable_irq(dsi);
> > > +	}
> > > +
> > > +	dsi->state &= ~DSIM_STATE_CMD_LPM;
> > > +
> > > +	phy_power_off(dsi->phy);
> > > +
> > > +	for (i = driver_data->num_clks - 1; i > -1; i--)
> > > +		clk_disable_unprepare(dsi->clks[i]);
> > > +
> > > +	ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
> > > +	if (ret < 0)
> > > +		dev_err(dsi->dev, "cannot disable regulators %d\n", ret);
> > > +
> > > +	return 0;
> > > +}
> > > +EXPORT_SYMBOL_GPL(samsung_dsim_suspend);
> > > +
> > > +int samsung_dsim_resume(struct samsung_dsim *dsi)
> > > +{
> > > +	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
> > > +	int ret, i;
> > > +
> > > +	ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
> > > +	if (ret < 0) {
> > > +		dev_err(dsi->dev, "cannot enable regulators %d\n", ret);
> > > +		return ret;
> > > +	}
> > > +
> > > +	for (i = 0; i < driver_data->num_clks; i++) {
> > > +		ret = clk_prepare_enable(dsi->clks[i]);
> > > +		if (ret < 0)
> > > +			goto err_clk;
> > > +	}
> > > +
> > > +	ret = phy_power_on(dsi->phy);
> > > +	if (ret < 0) {
> > > +		dev_err(dsi->dev, "cannot enable phy %d\n", ret);
> > > +		goto err_clk;
> > > +	}
> > > +
> > > +	return 0;
> > > +
> > > +err_clk:
> > > +	while (--i > -1)
> > > +		clk_disable_unprepare(dsi->clks[i]);
> > > +	regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
> > > +
> > > +	return ret;
> > > +}
> > > +EXPORT_SYMBOL_GPL(samsung_dsim_resume);
> > > +
> > > +MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
> > > +MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
> > > +MODULE_DESCRIPTION("Samsung SoC MIPI DSI Master");
> > > +MODULE_LICENSE("GPL v2");
> > > diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
> > > index 6417f374b923..3bc321ab5bc8 100644
> > > --- a/drivers/gpu/drm/exynos/Kconfig
> > > +++ b/drivers/gpu/drm/exynos/Kconfig
> > > @@ -16,7 +16,6 @@ comment "CRTCs"
> > >  
> > >  config DRM_EXYNOS_FIMD
> > >  	bool "FIMD"
> > > -	depends on !FB_S3C
> > >  	select MFD_SYSCON
> > >  	help
> > >  	  Choose this option if you want to use Exynos FIMD for DRM.
> > > @@ -28,7 +27,6 @@ config DRM_EXYNOS5433_DECON
> > >  
> > >  config DRM_EXYNOS7_DECON
> > >  	bool "DECON on Exynos7"
> > > -	depends on !FB_S3C
> > >  	help
> > >  	  Choose this option if you want to use Exynos DECON for DRM.
> > >  
> > > @@ -55,8 +53,7 @@ config DRM_EXYNOS_DPI
> > >  config DRM_EXYNOS_DSI
> > >  	bool "MIPI-DSI host"
> > >  	depends on DRM_EXYNOS_FIMD || DRM_EXYNOS5433_DECON || DRM_EXYNOS7_DECON
> > > -	select DRM_MIPI_DSI
> > > -	select DRM_PANEL
> > > +	select DRM_SAMSUNG_DSIM
> > >  	default n
> > >  	help
> > >  	  This enables support for Exynos MIPI-DSI device.
> > > diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile
> > > index add70b336935..2fd2f3ee4fcf 100644
> > > --- a/drivers/gpu/drm/exynos/Makefile
> > > +++ b/drivers/gpu/drm/exynos/Makefile
> > > @@ -11,7 +11,7 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD)	+= exynos_drm_fimd.o
> > >  exynosdrm-$(CONFIG_DRM_EXYNOS5433_DECON)	+= exynos5433_drm_decon.o
> > >  exynosdrm-$(CONFIG_DRM_EXYNOS7_DECON)	+= exynos7_drm_decon.o
> > >  exynosdrm-$(CONFIG_DRM_EXYNOS_DPI)	+= exynos_drm_dpi.o
> > > -exynosdrm-$(CONFIG_DRM_EXYNOS_DSI)	+= exynos_drm_dsi.o exynos_drm_dsi_pltfm.o
> > > +exynosdrm-$(CONFIG_DRM_EXYNOS_DSI)	+= exynos_drm_dsi.o
> > >  exynosdrm-$(CONFIG_DRM_EXYNOS_DP)	+= exynos_dp.o
> > >  exynosdrm-$(CONFIG_DRM_EXYNOS_MIXER)	+= exynos_mixer.o
> > >  exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI)	+= exynos_hdmi.o
> > > diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > > index e8aea9d90c34..17f37fa74718 100644
> > > --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > > +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
> > > @@ -5,1774 +5,328 @@
> > >   * Copyright (c) 2014 Samsung Electronics Co., Ltd
> > >   *
> > >   * Contacts: Tomasz Figa <t.figa@samsung.com>
> > > -*/
> > > + */
> > >  
> > > -#include <linux/clk.h>
> > > -#include <linux/delay.h>
> > >  #include <linux/component.h>
> > > -#include <linux/gpio/consumer.h>
> > > -#include <linux/irq.h>
> > >  #include <linux/of_device.h>
> > > -#include <linux/of_gpio.h>
> > >  #include <linux/of_graph.h>
> > > -#include <linux/phy/phy.h>
> > > -#include <linux/regulator/consumer.h>
> > > -
> > > -#include <asm/unaligned.h>
> > > +#include <linux/pm_runtime.h>
> > >  
> > > -#include <video/mipi_display.h>
> > > -#include <video/videomode.h>
> > > -
> > > -#include <drm/drm_atomic_helper.h>
> > > +#include <drm/bridge/samsung-dsim.h>
> > >  #include <drm/drm_bridge.h>
> > > -#include <drm/drm_fb_helper.h>
> > > +#include <drm/drm_encoder.h>
> > >  #include <drm/drm_mipi_dsi.h>
> > > -#include <drm/drm_panel.h>
> > > -#include <drm/drm_print.h>
> > >  #include <drm/drm_probe_helper.h>
> > >  #include <drm/drm_simple_kms_helper.h>
> > >  
> > > -#include "exynos_drm_dsi.h"
> > > -
> > > -/* returns true iff both arguments logically differs */
> > > -#define NEQV(a, b) (!(a) ^ !(b))
> > > -
> > > -/* DSIM_STATUS */
> > > -#define DSIM_STOP_STATE_DAT(x)		(((x) & 0xf) << 0)
> > > -#define DSIM_STOP_STATE_CLK		(1 << 8)
> > > -#define DSIM_TX_READY_HS_CLK		(1 << 10)
> > > -#define DSIM_PLL_STABLE			(1 << 31)
> > > -
> > > -/* DSIM_TIMEOUT */
> > > -#define DSIM_LPDR_TIMEOUT(x)		((x) << 0)
> > > -#define DSIM_BTA_TIMEOUT(x)		((x) << 16)
> > > -
> > > -/* DSIM_CLKCTRL */
> > > -#define DSIM_ESC_PRESCALER(x)		(((x) & 0xffff) << 0)
> > > -#define DSIM_ESC_PRESCALER_MASK		(0xffff << 0)
> > > -#define DSIM_LANE_ESC_CLK_EN_CLK	(1 << 19)
> > > -#define DSIM_LANE_ESC_CLK_EN_DATA(x)	(((x) & 0xf) << 20)
> > > -#define DSIM_LANE_ESC_CLK_EN_DATA_MASK	(0xf << 20)
> > > -#define DSIM_BYTE_CLKEN			(1 << 24)
> > > -#define DSIM_BYTE_CLK_SRC(x)		(((x) & 0x3) << 25)
> > > -#define DSIM_BYTE_CLK_SRC_MASK		(0x3 << 25)
> > > -#define DSIM_PLL_BYPASS			(1 << 27)
> > > -#define DSIM_ESC_CLKEN			(1 << 28)
> > > -#define DSIM_TX_REQUEST_HSCLK		(1 << 31)
> > > -
> > > -/* DSIM_CONFIG */
> > > -#define DSIM_LANE_EN_CLK		(1 << 0)
> > > -#define DSIM_LANE_EN(x)			(((x) & 0xf) << 1)
> > > -#define DSIM_NUM_OF_DATA_LANE(x)	(((x) & 0x3) << 5)
> > > -#define DSIM_SUB_PIX_FORMAT(x)		(((x) & 0x7) << 8)
> > > -#define DSIM_MAIN_PIX_FORMAT_MASK	(0x7 << 12)
> > > -#define DSIM_MAIN_PIX_FORMAT_RGB888	(0x7 << 12)
> > > -#define DSIM_MAIN_PIX_FORMAT_RGB666	(0x6 << 12)
> > > -#define DSIM_MAIN_PIX_FORMAT_RGB666_P	(0x5 << 12)
> > > -#define DSIM_MAIN_PIX_FORMAT_RGB565	(0x4 << 12)
> > > -#define DSIM_SUB_VC			(((x) & 0x3) << 16)
> > > -#define DSIM_MAIN_VC			(((x) & 0x3) << 18)
> > > -#define DSIM_HSA_MODE			(1 << 20)
> > > -#define DSIM_HBP_MODE			(1 << 21)
> > > -#define DSIM_HFP_MODE			(1 << 22)
> > > -#define DSIM_HSE_MODE			(1 << 23)
> > > -#define DSIM_AUTO_MODE			(1 << 24)
> > > -#define DSIM_VIDEO_MODE			(1 << 25)
> > > -#define DSIM_BURST_MODE			(1 << 26)
> > > -#define DSIM_SYNC_INFORM		(1 << 27)
> > > -#define DSIM_EOT_DISABLE		(1 << 28)
> > > -#define DSIM_MFLUSH_VS			(1 << 29)
> > > -/* This flag is valid only for exynos3250/3472/5260/5430 */
> > > -#define DSIM_CLKLANE_STOP		(1 << 30)
> > > -
> > > -/* DSIM_ESCMODE */
> > > -#define DSIM_TX_TRIGGER_RST		(1 << 4)
> > > -#define DSIM_TX_LPDT_LP			(1 << 6)
> > > -#define DSIM_CMD_LPDT_LP		(1 << 7)
> > > -#define DSIM_FORCE_BTA			(1 << 16)
> > > -#define DSIM_FORCE_STOP_STATE		(1 << 20)
> > > -#define DSIM_STOP_STATE_CNT(x)		(((x) & 0x7ff) << 21)
> > > -#define DSIM_STOP_STATE_CNT_MASK	(0x7ff << 21)
> > > -
> > > -/* DSIM_MDRESOL */
> > > -#define DSIM_MAIN_STAND_BY		(1 << 31)
> > > -#define DSIM_MAIN_VRESOL(x, num_bits)	(((x) & ((1 << (num_bits)) - 1)) << 16)
> > > -#define DSIM_MAIN_HRESOL(x, num_bits)	(((x) & ((1 << (num_bits)) - 1)) << 0)
> > > -
> > > -/* DSIM_MVPORCH */
> > > -#define DSIM_CMD_ALLOW(x)		((x) << 28)
> > > -#define DSIM_STABLE_VFP(x)		((x) << 16)
> > > -#define DSIM_MAIN_VBP(x)		((x) << 0)
> > > -#define DSIM_CMD_ALLOW_MASK		(0xf << 28)
> > > -#define DSIM_STABLE_VFP_MASK		(0x7ff << 16)
> > > -#define DSIM_MAIN_VBP_MASK		(0x7ff << 0)
> > > -
> > > -/* DSIM_MHPORCH */
> > > -#define DSIM_MAIN_HFP(x)		((x) << 16)
> > > -#define DSIM_MAIN_HBP(x)		((x) << 0)
> > > -#define DSIM_MAIN_HFP_MASK		((0xffff) << 16)
> > > -#define DSIM_MAIN_HBP_MASK		((0xffff) << 0)
> > > -
> > > -/* DSIM_MSYNC */
> > > -#define DSIM_MAIN_VSA(x)		((x) << 22)
> > > -#define DSIM_MAIN_HSA(x)		((x) << 0)
> > > -#define DSIM_MAIN_VSA_MASK		((0x3ff) << 22)
> > > -#define DSIM_MAIN_HSA_MASK		((0xffff) << 0)
> > > -
> > > -/* DSIM_SDRESOL */
> > > -#define DSIM_SUB_STANDY(x)		((x) << 31)
> > > -#define DSIM_SUB_VRESOL(x)		((x) << 16)
> > > -#define DSIM_SUB_HRESOL(x)		((x) << 0)
> > > -#define DSIM_SUB_STANDY_MASK		((0x1) << 31)
> > > -#define DSIM_SUB_VRESOL_MASK		((0x7ff) << 16)
> > > -#define DSIM_SUB_HRESOL_MASK		((0x7ff) << 0)
> > > -
> > > -/* DSIM_INTSRC */
> > > -#define DSIM_INT_PLL_STABLE		(1 << 31)
> > > -#define DSIM_INT_SW_RST_RELEASE		(1 << 30)
> > > -#define DSIM_INT_SFR_FIFO_EMPTY		(1 << 29)
> > > -#define DSIM_INT_SFR_HDR_FIFO_EMPTY	(1 << 28)
> > > -#define DSIM_INT_BTA			(1 << 25)
> > > -#define DSIM_INT_FRAME_DONE		(1 << 24)
> > > -#define DSIM_INT_RX_TIMEOUT		(1 << 21)
> > > -#define DSIM_INT_BTA_TIMEOUT		(1 << 20)
> > > -#define DSIM_INT_RX_DONE		(1 << 18)
> > > -#define DSIM_INT_RX_TE			(1 << 17)
> > > -#define DSIM_INT_RX_ACK			(1 << 16)
> > > -#define DSIM_INT_RX_ECC_ERR		(1 << 15)
> > > -#define DSIM_INT_RX_CRC_ERR		(1 << 14)
> > > +#include "exynos_drm_crtc.h"
> > > +#include "exynos_drm_drv.h"
> > >  
> > > -/* DSIM_FIFOCTRL */
> > > -#define DSIM_RX_DATA_FULL		(1 << 25)
> > > -#define DSIM_RX_DATA_EMPTY		(1 << 24)
> > > -#define DSIM_SFR_HEADER_FULL		(1 << 23)
> > > -#define DSIM_SFR_HEADER_EMPTY		(1 << 22)
> > > -#define DSIM_SFR_PAYLOAD_FULL		(1 << 21)
> > > -#define DSIM_SFR_PAYLOAD_EMPTY		(1 << 20)
> > > -#define DSIM_I80_HEADER_FULL		(1 << 19)
> > > -#define DSIM_I80_HEADER_EMPTY		(1 << 18)
> > > -#define DSIM_I80_PAYLOAD_FULL		(1 << 17)
> > > -#define DSIM_I80_PAYLOAD_EMPTY		(1 << 16)
> > > -#define DSIM_SD_HEADER_FULL		(1 << 15)
> > > -#define DSIM_SD_HEADER_EMPTY		(1 << 14)
> > > -#define DSIM_SD_PAYLOAD_FULL		(1 << 13)
> > > -#define DSIM_SD_PAYLOAD_EMPTY		(1 << 12)
> > > -#define DSIM_MD_HEADER_FULL		(1 << 11)
> > > -#define DSIM_MD_HEADER_EMPTY		(1 << 10)
> > > -#define DSIM_MD_PAYLOAD_FULL		(1 << 9)
> > > -#define DSIM_MD_PAYLOAD_EMPTY		(1 << 8)
> > > -#define DSIM_RX_FIFO			(1 << 4)
> > > -#define DSIM_SFR_FIFO			(1 << 3)
> > > -#define DSIM_I80_FIFO			(1 << 2)
> > > -#define DSIM_SD_FIFO			(1 << 1)
> > > -#define DSIM_MD_FIFO			(1 << 0)
> > > -
> > > -/* DSIM_PHYACCHR */
> > > -#define DSIM_AFC_EN			(1 << 14)
> > > -#define DSIM_AFC_CTL(x)			(((x) & 0x7) << 5)
> > > -
> > > -/* DSIM_PLLCTRL */
> > > -#define DSIM_FREQ_BAND(x)		((x) << 24)
> > > -#define DSIM_PLL_EN			(1 << 23)
> > > -#define DSIM_PLL_P(x)			((x) << 13)
> > > -#define DSIM_PLL_M(x)			((x) << 4)
> > > -#define DSIM_PLL_S(x)			((x) << 1)
> > > -
> > > -/* DSIM_PHYCTRL */
> > > -#define DSIM_PHYCTRL_ULPS_EXIT(x)	(((x) & 0x1ff) << 0)
> > > -#define DSIM_PHYCTRL_B_DPHYCTL_VREG_LP	(1 << 30)
> > > -#define DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP	(1 << 14)
> > > -
> > > -/* DSIM_PHYTIMING */
> > > -#define DSIM_PHYTIMING_LPX(x)		((x) << 8)
> > > -#define DSIM_PHYTIMING_HS_EXIT(x)	((x) << 0)
> > > -
> > > -/* DSIM_PHYTIMING1 */
> > > -#define DSIM_PHYTIMING1_CLK_PREPARE(x)	((x) << 24)
> > > -#define DSIM_PHYTIMING1_CLK_ZERO(x)	((x) << 16)
> > > -#define DSIM_PHYTIMING1_CLK_POST(x)	((x) << 8)
> > > -#define DSIM_PHYTIMING1_CLK_TRAIL(x)	((x) << 0)
> > > -
> > > -/* DSIM_PHYTIMING2 */
> > > -#define DSIM_PHYTIMING2_HS_PREPARE(x)	((x) << 16)
> > > -#define DSIM_PHYTIMING2_HS_ZERO(x)	((x) << 8)
> > > -#define DSIM_PHYTIMING2_HS_TRAIL(x)	((x) << 0)
> > > -
> > > -#define DSI_MAX_BUS_WIDTH		4
> > > -#define DSI_NUM_VIRTUAL_CHANNELS	4
> > > -#define DSI_TX_FIFO_SIZE		2048
> > > -#define DSI_RX_FIFO_SIZE		256
> > > -#define DSI_XFER_TIMEOUT_MS		100
> > > -#define DSI_RX_FIFO_EMPTY		0x30800002
> > > -
> > > -#define OLD_SCLK_MIPI_CLK_NAME "pll_clk"
> > > -
> > > -static const char *const clk_names[5] = { "bus_clk", "sclk_mipi",
> > > -	"phyclk_mipidphy0_bitclkdiv8", "phyclk_mipidphy0_rxclkesc0",
> > > -	"sclk_rgb_vclk_to_dsim0" };
> > > -
> > > -enum exynos_dsi_transfer_type {
> > > -	EXYNOS_DSI_TX,
> > > -	EXYNOS_DSI_RX,
> > > +enum {
> > > +	DSI_PORT_IN,
> > > +	DSI_PORT_OUT
> > >  };
> > >  
> > > -struct exynos_dsi_transfer {
> > > -	struct list_head list;
> > > -	struct completion completed;
> > > -	int result;
> > > -	struct mipi_dsi_packet packet;
> > > -	u16 flags;
> > > -	u16 tx_done;
> > > -
> > > -	u8 *rx_payload;
> > > -	u16 rx_len;
> > > -	u16 rx_done;
> > > -};
> > > -
> > > -#define DSIM_STATE_ENABLED		BIT(0)
> > > -#define DSIM_STATE_INITIALIZED		BIT(1)
> > > -#define DSIM_STATE_CMD_LPM		BIT(2)
> > > -#define DSIM_STATE_VIDOUT_AVAILABLE	BIT(3)
> > > -
> > >  struct exynos_dsi {
> > > -	struct drm_bridge bridge;
> > > -	struct mipi_dsi_host dsi_host;
> > > -	struct drm_connector connector;
> > > -	struct drm_panel *panel;
> > > -	struct list_head bridge_chain;
> > > -	struct drm_bridge *out_bridge;
> > > -	struct device *dev;
> > > -
> > > -	void __iomem *reg_base;
> > > -	struct phy *phy;
> > > -	struct clk **clks;
> > > -	struct regulator_bulk_data supplies[2];
> > > -	int irq;
> > > -	int te_gpio;
> > > -
> > > -	u32 pll_clk_rate;
> > > -	u32 burst_clk_rate;
> > > -	u32 esc_clk_rate;
> > > -	u32 lanes;
> > > -	u32 mode_flags;
> > > -	u32 format;
> > > -
> > > -	struct drm_display_mode mode;
> > > -
> > > -	int state;
> > > -	struct drm_property *brightness;
> > > -	struct completion completed;
> > > -
> > > -	spinlock_t transfer_lock; /* protects transfer_list */
> > > -	struct list_head transfer_list;
> > > -
> > > -	const struct exynos_dsi_driver_data *driver_data;
> > > +	struct samsung_dsim *dsi;
> > > +	struct drm_encoder encoder;
> > >  };
> > >  
> > > -#define host_to_dsi(host) container_of(host, struct exynos_dsi, dsi_host)
> > > -#define connector_to_dsi(c) container_of(c, struct exynos_dsi, connector)
> > > -
> > > -enum reg_idx {
> > > -	DSIM_STATUS_REG,	/* Status register */
> > > -	DSIM_SWRST_REG,		/* Software reset register */
> > > -	DSIM_CLKCTRL_REG,	/* Clock control register */
> > > -	DSIM_TIMEOUT_REG,	/* Time out register */
> > > -	DSIM_CONFIG_REG,	/* Configuration register */
> > > -	DSIM_ESCMODE_REG,	/* Escape mode register */
> > > -	DSIM_MDRESOL_REG,
> > > -	DSIM_MVPORCH_REG,	/* Main display Vporch register */
> > > -	DSIM_MHPORCH_REG,	/* Main display Hporch register */
> > > -	DSIM_MSYNC_REG,		/* Main display sync area register */
> > > -	DSIM_INTSRC_REG,	/* Interrupt source register */
> > > -	DSIM_INTMSK_REG,	/* Interrupt mask register */
> > > -	DSIM_PKTHDR_REG,	/* Packet Header FIFO register */
> > > -	DSIM_PAYLOAD_REG,	/* Payload FIFO register */
> > > -	DSIM_RXFIFO_REG,	/* Read FIFO register */
> > > -	DSIM_FIFOCTRL_REG,	/* FIFO status and control register */
> > > -	DSIM_PLLCTRL_REG,	/* PLL control register */
> > > -	DSIM_PHYCTRL_REG,
> > > -	DSIM_PHYTIMING_REG,
> > > -	DSIM_PHYTIMING1_REG,
> > > -	DSIM_PHYTIMING2_REG,
> > > -	NUM_REGS
> > > +static const unsigned int reg_values[] = {
> > > +	[RESET_TYPE] = DSIM_SWRST,
> > > +	[PLL_TIMER] = 500,
> > > +	[STOP_STATE_CNT] = 0xf,
> > > +	[PHYCTRL_ULPS_EXIT] = 0x0af,
> > > +	[PHYCTRL_VREG_LP] = 0,
> > > +	[PHYCTRL_SLEW_UP] = 0,
> > > +	[PHYTIMING_LPX] = 0x06,
> > > +	[PHYTIMING_HS_EXIT] = 0x0b,
> > > +	[PHYTIMING_CLK_PREPARE] = 0x07,
> > > +	[PHYTIMING_CLK_ZERO] = 0x27,
> > > +	[PHYTIMING_CLK_POST] = 0x0d,
> > > +	[PHYTIMING_CLK_TRAIL] = 0x08,
> > > +	[PHYTIMING_HS_PREPARE] = 0x09,
> > > +	[PHYTIMING_HS_ZERO] = 0x0d,
> > > +	[PHYTIMING_HS_TRAIL] = 0x0b,
> > >  };
> > >  
> > > -static const unsigned int exynos_reg_ofs[] = {
> > > -	[DSIM_STATUS_REG] =  0x00,
> > > -	[DSIM_SWRST_REG] =  0x04,
> > > -	[DSIM_CLKCTRL_REG] =  0x08,
> > > -	[DSIM_TIMEOUT_REG] =  0x0c,
> > > -	[DSIM_CONFIG_REG] =  0x10,
> > > -	[DSIM_ESCMODE_REG] =  0x14,
> > > -	[DSIM_MDRESOL_REG] =  0x18,
> > > -	[DSIM_MVPORCH_REG] =  0x1c,
> > > -	[DSIM_MHPORCH_REG] =  0x20,
> > > -	[DSIM_MSYNC_REG] =  0x24,
> > > -	[DSIM_INTSRC_REG] =  0x2c,
> > > -	[DSIM_INTMSK_REG] =  0x30,
> > > -	[DSIM_PKTHDR_REG] =  0x34,
> > > -	[DSIM_PAYLOAD_REG] =  0x38,
> > > -	[DSIM_RXFIFO_REG] =  0x3c,
> > > -	[DSIM_FIFOCTRL_REG] =  0x44,
> > > -	[DSIM_PLLCTRL_REG] =  0x4c,
> > > -	[DSIM_PHYCTRL_REG] =  0x5c,
> > > -	[DSIM_PHYTIMING_REG] =  0x64,
> > > -	[DSIM_PHYTIMING1_REG] =  0x68,
> > > -	[DSIM_PHYTIMING2_REG] =  0x6c,
> > > +static const unsigned int exynos5422_reg_values[] = {
> > > +	[RESET_TYPE] = DSIM_SWRST,
> > > +	[PLL_TIMER] = 500,
> > > +	[STOP_STATE_CNT] = 0xf,
> > > +	[PHYCTRL_ULPS_EXIT] = 0xaf,
> > > +	[PHYCTRL_VREG_LP] = 0,
> > > +	[PHYCTRL_SLEW_UP] = 0,
> > > +	[PHYTIMING_LPX] = 0x08,
> > > +	[PHYTIMING_HS_EXIT] = 0x0d,
> > > +	[PHYTIMING_CLK_PREPARE] = 0x09,
> > > +	[PHYTIMING_CLK_ZERO] = 0x30,
> > > +	[PHYTIMING_CLK_POST] = 0x0e,
> > > +	[PHYTIMING_CLK_TRAIL] = 0x0a,
> > > +	[PHYTIMING_HS_PREPARE] = 0x0c,
> > > +	[PHYTIMING_HS_ZERO] = 0x11,
> > > +	[PHYTIMING_HS_TRAIL] = 0x0d,
> > >  };
> > >  
> > > -static const unsigned int exynos5433_reg_ofs[] = {
> > > -	[DSIM_STATUS_REG] = 0x04,
> > > -	[DSIM_SWRST_REG] = 0x0C,
> > > -	[DSIM_CLKCTRL_REG] = 0x10,
> > > -	[DSIM_TIMEOUT_REG] = 0x14,
> > > -	[DSIM_CONFIG_REG] = 0x18,
> > > -	[DSIM_ESCMODE_REG] = 0x1C,
> > > -	[DSIM_MDRESOL_REG] = 0x20,
> > > -	[DSIM_MVPORCH_REG] = 0x24,
> > > -	[DSIM_MHPORCH_REG] = 0x28,
> > > -	[DSIM_MSYNC_REG] = 0x2C,
> > > -	[DSIM_INTSRC_REG] = 0x34,
> > > -	[DSIM_INTMSK_REG] = 0x38,
> > > -	[DSIM_PKTHDR_REG] = 0x3C,
> > > -	[DSIM_PAYLOAD_REG] = 0x40,
> > > -	[DSIM_RXFIFO_REG] = 0x44,
> > > -	[DSIM_FIFOCTRL_REG] = 0x4C,
> > > -	[DSIM_PLLCTRL_REG] = 0x94,
> > > -	[DSIM_PHYCTRL_REG] = 0xA4,
> > > -	[DSIM_PHYTIMING_REG] = 0xB4,
> > > -	[DSIM_PHYTIMING1_REG] = 0xB8,
> > > -	[DSIM_PHYTIMING2_REG] = 0xBC,
> > > +static const unsigned int exynos5433_reg_values[] = {
> > > +	[RESET_TYPE] = DSIM_FUNCRST,
> > > +	[PLL_TIMER] = 22200,
> > > +	[STOP_STATE_CNT] = 0xa,
> > > +	[PHYCTRL_ULPS_EXIT] = 0x190,
> > > +	[PHYCTRL_VREG_LP] = 1,
> > > +	[PHYCTRL_SLEW_UP] = 1,
> > > +	[PHYTIMING_LPX] = 0x07,
> > > +	[PHYTIMING_HS_EXIT] = 0x0c,
> > > +	[PHYTIMING_CLK_PREPARE] = 0x09,
> > > +	[PHYTIMING_CLK_ZERO] = 0x2d,
> > > +	[PHYTIMING_CLK_POST] = 0x0e,
> > > +	[PHYTIMING_CLK_TRAIL] = 0x09,
> > > +	[PHYTIMING_HS_PREPARE] = 0x0b,
> > > +	[PHYTIMING_HS_ZERO] = 0x10,
> > > +	[PHYTIMING_HS_TRAIL] = 0x0c,
> > >  };
> > >  
> > > -static inline void exynos_dsi_write(struct exynos_dsi *dsi, enum reg_idx idx,
> > > -				    u32 val)
> > > -{
> > > -	const unsigned int *reg_ofs;
> > > -
> > > -	if (dsi->driver_data->reg_ofs == EXYNOS5433_REG_OFS)
> > > -		reg_ofs = exynos5433_reg_ofs;
> > > -	else
> > > -		reg_ofs = exynos_reg_ofs;
> > > -
> > > -	writel(val, dsi->reg_base + reg_ofs[idx]);
> > > -}
> > > -
> > > -static inline u32 exynos_dsi_read(struct exynos_dsi *dsi, enum reg_idx idx)
> > > -{
> > > -	const unsigned int *reg_ofs;
> > > -
> > > -	if (dsi->driver_data->reg_ofs == EXYNOS5433_REG_OFS)
> > > -		reg_ofs = exynos5433_reg_ofs;
> > > -	else
> > > -		reg_ofs = exynos_reg_ofs;
> > > -
> > > -	return readl(dsi->reg_base + reg_ofs[idx]);
> > > -}
> > > -
> > > -static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi)
> > > -{
> > > -	if (wait_for_completion_timeout(&dsi->completed, msecs_to_jiffies(300)))
> > > -		return;
> > > -
> > > -	dev_err(dsi->dev, "timeout waiting for reset\n");
> > > -}
> > > -
> > > -static void exynos_dsi_reset(struct exynos_dsi *dsi)
> > > -{
> > > -	u32 reset_val = dsi->driver_data->reg_values[RESET_TYPE];
> > > -
> > > -	reinit_completion(&dsi->completed);
> > > -	exynos_dsi_write(dsi, DSIM_SWRST_REG, reset_val);
> > > -}
> > > -
> > > -#ifndef MHZ
> > > -#define MHZ	(1000*1000)
> > > -#endif
> > > -
> > > -static unsigned long exynos_dsi_pll_find_pms(struct exynos_dsi *dsi,
> > > -		unsigned long fin, unsigned long fout, u8 *p, u16 *m, u8 *s)
> > > -{
> > > -	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
> > > -	unsigned long best_freq = 0;
> > > -	u32 min_delta = 0xffffffff;
> > > -	u8 p_min, p_max;
> > > -	u8 _p, best_p;
> > > -	u16 _m, best_m;
> > > -	u8 _s, best_s;
> > > -
> > > -	p_min = DIV_ROUND_UP(fin, (12 * MHZ));
> > > -	p_max = fin / (6 * MHZ);
> > > -
> > > -	for (_p = p_min; _p <= p_max; ++_p) {
> > > -		for (_s = 0; _s <= 5; ++_s) {
> > > -			u64 tmp;
> > > -			u32 delta;
> > > -
> > > -			tmp = (u64)fout * (_p << _s);
> > > -			do_div(tmp, fin);
> > > -			_m = tmp;
> > > -			if (_m < 41 || _m > 125)
> > > -				continue;
> > > -
> > > -			tmp = (u64)_m * fin;
> > > -			do_div(tmp, _p);
> > > -			if (tmp < 500 * MHZ ||
> > > -					tmp > driver_data->max_freq * MHZ)
> > > -				continue;
> > > -
> > > -			tmp = (u64)_m * fin;
> > > -			do_div(tmp, _p << _s);
> > > -
> > > -			delta = abs(fout - tmp);
> > > -			if (delta < min_delta) {
> > > -				best_p = _p;
> > > -				best_m = _m;
> > > -				best_s = _s;
> > > -				min_delta = delta;
> > > -				best_freq = tmp;
> > > -			}
> > > -		}
> > > -	}
> > > -
> > > -	if (best_freq) {
> > > -		*p = best_p;
> > > -		*m = best_m;
> > > -		*s = best_s;
> > > -	}
> > > -
> > > -	return best_freq;
> > > -}
> > > -
> > > -static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi,
> > > -					unsigned long freq)
> > > -{
> > > -	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
> > > -	unsigned long fin, fout;
> > > -	int timeout;
> > > -	u8 p, s;
> > > -	u16 m;
> > > -	u32 reg;
> > > -
> > > -	fin = dsi->pll_clk_rate;
> > > -	fout = exynos_dsi_pll_find_pms(dsi, fin, freq, &p, &m, &s);
> > > -	if (!fout) {
> > > -		dev_err(dsi->dev,
> > > -			"failed to find PLL PMS for requested frequency\n");
> > > -		return 0;
> > > -	}
> > > -	dev_dbg(dsi->dev, "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s);
> > > -
> > > -	writel(driver_data->reg_values[PLL_TIMER],
> > > -			dsi->reg_base + driver_data->plltmr_reg);
> > > -
> > > -	reg = DSIM_PLL_EN | DSIM_PLL_P(p) | DSIM_PLL_M(m) | DSIM_PLL_S(s);
> > > -
> > > -	if (driver_data->has_freqband) {
> > > -		static const unsigned long freq_bands[] = {
> > > -			100 * MHZ, 120 * MHZ, 160 * MHZ, 200 * MHZ,
> > > -			270 * MHZ, 320 * MHZ, 390 * MHZ, 450 * MHZ,
> > > -			510 * MHZ, 560 * MHZ, 640 * MHZ, 690 * MHZ,
> > > -			770 * MHZ, 870 * MHZ, 950 * MHZ,
> > > -		};
> > > -		int band;
> > > -
> > > -		for (band = 0; band < ARRAY_SIZE(freq_bands); ++band)
> > > -			if (fout < freq_bands[band])
> > > -				break;
> > > -
> > > -		dev_dbg(dsi->dev, "band %d\n", band);
> > > -
> > > -		reg |= DSIM_FREQ_BAND(band);
> > > -	}
> > > -
> > > -	exynos_dsi_write(dsi, DSIM_PLLCTRL_REG, reg);
> > > -
> > > -	timeout = 1000;
> > > -	do {
> > > -		if (timeout-- == 0) {
> > > -			dev_err(dsi->dev, "PLL failed to stabilize\n");
> > > -			return 0;
> > > -		}
> > > -		reg = exynos_dsi_read(dsi, DSIM_STATUS_REG);
> > > -	} while ((reg & DSIM_PLL_STABLE) == 0);
> > > -
> > > -	return fout;
> > > -}
> > > -
> > > -static int exynos_dsi_enable_clock(struct exynos_dsi *dsi)
> > > -{
> > > -	unsigned long hs_clk, byte_clk, esc_clk;
> > > -	unsigned long esc_div;
> > > -	u32 reg;
> > > -
> > > -	hs_clk = exynos_dsi_set_pll(dsi, dsi->burst_clk_rate);
> > > -	if (!hs_clk) {
> > > -		dev_err(dsi->dev, "failed to configure DSI PLL\n");
> > > -		return -EFAULT;
> > > -	}
> > > -
> > > -	byte_clk = hs_clk / 8;
> > > -	esc_div = DIV_ROUND_UP(byte_clk, dsi->esc_clk_rate);
> > > -	esc_clk = byte_clk / esc_div;
> > > -
> > > -	if (esc_clk > 20 * MHZ) {
> > > -		++esc_div;
> > > -		esc_clk = byte_clk / esc_div;
> > > -	}
> > > -
> > > -	dev_dbg(dsi->dev, "hs_clk = %lu, byte_clk = %lu, esc_clk = %lu\n",
> > > -		hs_clk, byte_clk, esc_clk);
> > > -
> > > -	reg = exynos_dsi_read(dsi, DSIM_CLKCTRL_REG);
> > > -	reg &= ~(DSIM_ESC_PRESCALER_MASK | DSIM_LANE_ESC_CLK_EN_CLK
> > > -			| DSIM_LANE_ESC_CLK_EN_DATA_MASK | DSIM_PLL_BYPASS
> > > -			| DSIM_BYTE_CLK_SRC_MASK);
> > > -	reg |= DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN
> > > -			| DSIM_ESC_PRESCALER(esc_div)
> > > -			| DSIM_LANE_ESC_CLK_EN_CLK
> > > -			| DSIM_LANE_ESC_CLK_EN_DATA(BIT(dsi->lanes) - 1)
> > > -			| DSIM_BYTE_CLK_SRC(0)
> > > -			| DSIM_TX_REQUEST_HSCLK;
> > > -	exynos_dsi_write(dsi, DSIM_CLKCTRL_REG, reg);
> > > -
> > > -	return 0;
> > > -}
> > > -
> > > -static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi)
> > > -{
> > > -	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
> > > -	const unsigned int *reg_values = driver_data->reg_values;
> > > -	u32 reg;
> > > -
> > > -	if (driver_data->has_freqband)
> > > -		return;
> > > -
> > > -	/* B D-PHY: D-PHY Master & Slave Analog Block control */
> > > -	reg = DSIM_PHYCTRL_ULPS_EXIT(reg_values[PHYCTRL_ULPS_EXIT]);
> > > -	if (reg_values[PHYCTRL_VREG_LP])
> > > -		reg |= DSIM_PHYCTRL_B_DPHYCTL_VREG_LP;
> > > -	if (reg_values[PHYCTRL_SLEW_UP])
> > > -		reg |= DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP;
> > > -	exynos_dsi_write(dsi, DSIM_PHYCTRL_REG, reg);
> > > -
> > > -	/*
> > > -	 * T LPX: Transmitted length of any Low-Power state period
> > > -	 * T HS-EXIT: Time that the transmitter drives LP-11 following a HS
> > > -	 *	burst
> > > -	 */
> > > -	reg = DSIM_PHYTIMING_LPX(reg_values[PHYTIMING_LPX]) |
> > > -		DSIM_PHYTIMING_HS_EXIT(reg_values[PHYTIMING_HS_EXIT]);
> > > -	exynos_dsi_write(dsi, DSIM_PHYTIMING_REG, reg);
> > > -
> > > -	/*
> > > -	 * T CLK-PREPARE: Time that the transmitter drives the Clock Lane LP-00
> > > -	 *	Line state immediately before the HS-0 Line state starting the
> > > -	 *	HS transmission
> > > -	 * T CLK-ZERO: Time that the transmitter drives the HS-0 state prior to
> > > -	 *	transmitting the Clock.
> > > -	 * T CLK_POST: Time that the transmitter continues to send HS clock
> > > -	 *	after the last associated Data Lane has transitioned to LP Mode
> > > -	 *	Interval is defined as the period from the end of T HS-TRAIL to
> > > -	 *	the beginning of T CLK-TRAIL
> > > -	 * T CLK-TRAIL: Time that the transmitter drives the HS-0 state after
> > > -	 *	the last payload clock bit of a HS transmission burst
> > > -	 */
> > > -	reg = DSIM_PHYTIMING1_CLK_PREPARE(reg_values[PHYTIMING_CLK_PREPARE]) |
> > > -		DSIM_PHYTIMING1_CLK_ZERO(reg_values[PHYTIMING_CLK_ZERO]) |
> > > -		DSIM_PHYTIMING1_CLK_POST(reg_values[PHYTIMING_CLK_POST]) |
> > > -		DSIM_PHYTIMING1_CLK_TRAIL(reg_values[PHYTIMING_CLK_TRAIL]);
> > > -	exynos_dsi_write(dsi, DSIM_PHYTIMING1_REG, reg);
> > > -
> > > -	/*
> > > -	 * T HS-PREPARE: Time that the transmitter drives the Data Lane LP-00
> > > -	 *	Line state immediately before the HS-0 Line state starting the
> > > -	 *	HS transmission
> > > -	 * T HS-ZERO: Time that the transmitter drives the HS-0 state prior to
> > > -	 *	transmitting the Sync sequence.
> > > -	 * T HS-TRAIL: Time that the transmitter drives the flipped differential
> > > -	 *	state after last payload data bit of a HS transmission burst
> > > -	 */
> > > -	reg = DSIM_PHYTIMING2_HS_PREPARE(reg_values[PHYTIMING_HS_PREPARE]) |
> > > -		DSIM_PHYTIMING2_HS_ZERO(reg_values[PHYTIMING_HS_ZERO]) |
> > > -		DSIM_PHYTIMING2_HS_TRAIL(reg_values[PHYTIMING_HS_TRAIL]);
> > > -	exynos_dsi_write(dsi, DSIM_PHYTIMING2_REG, reg);
> > > -}
> > > -
> > > -static void exynos_dsi_disable_clock(struct exynos_dsi *dsi)
> > > -{
> > > -	u32 reg;
> > > -
> > > -	reg = exynos_dsi_read(dsi, DSIM_CLKCTRL_REG);
> > > -	reg &= ~(DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK
> > > -			| DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN);
> > > -	exynos_dsi_write(dsi, DSIM_CLKCTRL_REG, reg);
> > > -
> > > -	reg = exynos_dsi_read(dsi, DSIM_PLLCTRL_REG);
> > > -	reg &= ~DSIM_PLL_EN;
> > > -	exynos_dsi_write(dsi, DSIM_PLLCTRL_REG, reg);
> > > -}
> > > -
> > > -static void exynos_dsi_enable_lane(struct exynos_dsi *dsi, u32 lane)
> > > -{
> > > -	u32 reg = exynos_dsi_read(dsi, DSIM_CONFIG_REG);
> > > -	reg |= (DSIM_NUM_OF_DATA_LANE(dsi->lanes - 1) | DSIM_LANE_EN_CLK |
> > > -			DSIM_LANE_EN(lane));
> > > -	exynos_dsi_write(dsi, DSIM_CONFIG_REG, reg);
> > > -}
> > > -
> > > -static int exynos_dsi_init_link(struct exynos_dsi *dsi)
> > > +static int exynos_dsi_host_attach(struct device *dev,
> > > +				  struct mipi_dsi_device *device)
> > >  {
> > > -	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
> > > -	int timeout;
> > > -	u32 reg;
> > > -	u32 lanes_mask;
> > > -
> > > -	/* Initialize FIFO pointers */
> > > -	reg = exynos_dsi_read(dsi, DSIM_FIFOCTRL_REG);
> > > -	reg &= ~0x1f;
> > > -	exynos_dsi_write(dsi, DSIM_FIFOCTRL_REG, reg);
> > > -
> > > -	usleep_range(9000, 11000);
> > > -
> > > -	reg |= 0x1f;
> > > -	exynos_dsi_write(dsi, DSIM_FIFOCTRL_REG, reg);
> > > -	usleep_range(9000, 11000);
> > > +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> > > +	struct drm_device *drm = dsi->encoder.dev;
> > > +	struct exynos_drm_crtc *crtc;
> > >  
> > > -	/* DSI configuration */
> > > -	reg = 0;
> > > +	mutex_lock(&drm->mode_config.mutex);
> > > +	crtc = exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD);
> > > +	crtc->i80_mode = !(device->mode_flags & MIPI_DSI_MODE_VIDEO);
> > > +	mutex_unlock(&drm->mode_config.mutex);
> > >  
> > > -	/*
> > > -	 * The first bit of mode_flags specifies display configuration.
> > > -	 * If this bit is set[= MIPI_DSI_MODE_VIDEO], dsi will support video
> > > -	 * mode, otherwise it will support command mode.
> > > -	 */
> > > -	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> > > -		reg |= DSIM_VIDEO_MODE;
> > > -
> > > -		/*
> > > -		 * The user manual describes that following bits are ignored in
> > > -		 * command mode.
> > > -		 */
> > > -		if (!(dsi->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH))
> > > -			reg |= DSIM_MFLUSH_VS;
> > > -		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
> > > -			reg |= DSIM_SYNC_INFORM;
> > > -		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
> > > -			reg |= DSIM_BURST_MODE;
> > > -		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT)
> > > -			reg |= DSIM_AUTO_MODE;
> > > -		if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSE)
> > > -			reg |= DSIM_HSE_MODE;
> > > -		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HFP))
> > > -			reg |= DSIM_HFP_MODE;
> > > -		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HBP))
> > > -			reg |= DSIM_HBP_MODE;
> > > -		if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HSA))
> > > -			reg |= DSIM_HSA_MODE;
> > > -	}
> > > -
> > > -	if (!(dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET))
> > > -		reg |= DSIM_EOT_DISABLE;
> > > -
> > > -	switch (dsi->format) {
> > > -	case MIPI_DSI_FMT_RGB888:
> > > -		reg |= DSIM_MAIN_PIX_FORMAT_RGB888;
> > > -		break;
> > > -	case MIPI_DSI_FMT_RGB666:
> > > -		reg |= DSIM_MAIN_PIX_FORMAT_RGB666;
> > > -		break;
> > > -	case MIPI_DSI_FMT_RGB666_PACKED:
> > > -		reg |= DSIM_MAIN_PIX_FORMAT_RGB666_P;
> > > -		break;
> > > -	case MIPI_DSI_FMT_RGB565:
> > > -		reg |= DSIM_MAIN_PIX_FORMAT_RGB565;
> > > -		break;
> > > -	default:
> > > -		dev_err(dsi->dev, "invalid pixel format\n");
> > > -		return -EINVAL;
> > > -	}
> > > -
> > > -	/*
> > > -	 * Use non-continuous clock mode if the periparal wants and
> > > -	 * host controller supports
> > > -	 *
> > > -	 * In non-continous clock mode, host controller will turn off
> > > -	 * the HS clock between high-speed transmissions to reduce
> > > -	 * power consumption.
> > > -	 */
> > > -	if (driver_data->has_clklane_stop &&
> > > -			dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
> > > -		reg |= DSIM_CLKLANE_STOP;
> > > -	}
> > > -	exynos_dsi_write(dsi, DSIM_CONFIG_REG, reg);
> > > -
> > > -	lanes_mask = BIT(dsi->lanes) - 1;
> > > -	exynos_dsi_enable_lane(dsi, lanes_mask);
> > > -
> > > -	/* Check clock and data lane state are stop state */
> > > -	timeout = 100;
> > > -	do {
> > > -		if (timeout-- == 0) {
> > > -			dev_err(dsi->dev, "waiting for bus lanes timed out\n");
> > > -			return -EFAULT;
> > > -		}
> > > -
> > > -		reg = exynos_dsi_read(dsi, DSIM_STATUS_REG);
> > > -		if ((reg & DSIM_STOP_STATE_DAT(lanes_mask))
> > > -		    != DSIM_STOP_STATE_DAT(lanes_mask))
> > > -			continue;
> > > -	} while (!(reg & (DSIM_STOP_STATE_CLK | DSIM_TX_READY_HS_CLK)));
> > > -
> > > -	reg = exynos_dsi_read(dsi, DSIM_ESCMODE_REG);
> > > -	reg &= ~DSIM_STOP_STATE_CNT_MASK;
> > > -	reg |= DSIM_STOP_STATE_CNT(driver_data->reg_values[STOP_STATE_CNT]);
> > > -	exynos_dsi_write(dsi, DSIM_ESCMODE_REG, reg);
> > > -
> > > -	reg = DSIM_BTA_TIMEOUT(0xff) | DSIM_LPDR_TIMEOUT(0xffff);
> > > -	exynos_dsi_write(dsi, DSIM_TIMEOUT_REG, reg);
> > > +	if (drm->mode_config.poll_enabled)
> > > +		drm_kms_helper_hotplug_event(drm);
> > >  
> > >  	return 0;
> > >  }
> > >  
> > > -static void exynos_dsi_set_display_mode(struct exynos_dsi *dsi)
> > > -{
> > > -	struct drm_display_mode *m = &dsi->mode;
> > > -	unsigned int num_bits_resol = dsi->driver_data->num_bits_resol;
> > > -	u32 reg;
> > > -
> > > -	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
> > > -		reg = DSIM_CMD_ALLOW(0xf)
> > > -			| DSIM_STABLE_VFP(m->vsync_start - m->vdisplay)
> > > -			| DSIM_MAIN_VBP(m->vtotal - m->vsync_end);
> > > -		exynos_dsi_write(dsi, DSIM_MVPORCH_REG, reg);
> > > -
> > > -		reg = DSIM_MAIN_HFP(m->hsync_start - m->hdisplay)
> > > -			| DSIM_MAIN_HBP(m->htotal - m->hsync_end);
> > > -		exynos_dsi_write(dsi, DSIM_MHPORCH_REG, reg);
> > > -
> > > -		reg = DSIM_MAIN_VSA(m->vsync_end - m->vsync_start)
> > > -			| DSIM_MAIN_HSA(m->hsync_end - m->hsync_start);
> > > -		exynos_dsi_write(dsi, DSIM_MSYNC_REG, reg);
> > > -	}
> > > -	reg =  DSIM_MAIN_HRESOL(m->hdisplay, num_bits_resol) |
> > > -		DSIM_MAIN_VRESOL(m->vdisplay, num_bits_resol);
> > > -
> > > -	exynos_dsi_write(dsi, DSIM_MDRESOL_REG, reg);
> > > -
> > > -	dev_dbg(dsi->dev, "LCD size = %dx%d\n", m->hdisplay, m->vdisplay);
> > > -}
> > > -
> > > -static void exynos_dsi_set_display_enable(struct exynos_dsi *dsi, bool enable)
> > > -{
> > > -	u32 reg;
> > > -
> > > -	reg = exynos_dsi_read(dsi, DSIM_MDRESOL_REG);
> > > -	if (enable)
> > > -		reg |= DSIM_MAIN_STAND_BY;
> > > -	else
> > > -		reg &= ~DSIM_MAIN_STAND_BY;
> > > -	exynos_dsi_write(dsi, DSIM_MDRESOL_REG, reg);
> > > -}
> > > -
> > > -static int exynos_dsi_wait_for_hdr_fifo(struct exynos_dsi *dsi)
> > > -{
> > > -	int timeout = 2000;
> > > -
> > > -	do {
> > > -		u32 reg = exynos_dsi_read(dsi, DSIM_FIFOCTRL_REG);
> > > -
> > > -		if (!(reg & DSIM_SFR_HEADER_FULL))
> > > -			return 0;
> > > -
> > > -		if (!cond_resched())
> > > -			usleep_range(950, 1050);
> > > -	} while (--timeout);
> > > -
> > > -	return -ETIMEDOUT;
> > > -}
> > > -
> > > -static void exynos_dsi_set_cmd_lpm(struct exynos_dsi *dsi, bool lpm)
> > > -{
> > > -	u32 v = exynos_dsi_read(dsi, DSIM_ESCMODE_REG);
> > > -
> > > -	if (lpm)
> > > -		v |= DSIM_CMD_LPDT_LP;
> > > -	else
> > > -		v &= ~DSIM_CMD_LPDT_LP;
> > > -
> > > -	exynos_dsi_write(dsi, DSIM_ESCMODE_REG, v);
> > > -}
> > > -
> > > -static void exynos_dsi_force_bta(struct exynos_dsi *dsi)
> > > -{
> > > -	u32 v = exynos_dsi_read(dsi, DSIM_ESCMODE_REG);
> > > -	v |= DSIM_FORCE_BTA;
> > > -	exynos_dsi_write(dsi, DSIM_ESCMODE_REG, v);
> > > -}
> > > -
> > > -static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi,
> > > -					struct exynos_dsi_transfer *xfer)
> > > -{
> > > -	struct device *dev = dsi->dev;
> > > -	struct mipi_dsi_packet *pkt = &xfer->packet;
> > > -	const u8 *payload = pkt->payload + xfer->tx_done;
> > > -	u16 length = pkt->payload_length - xfer->tx_done;
> > > -	bool first = !xfer->tx_done;
> > > -	u32 reg;
> > > -
> > > -	dev_dbg(dev, "< xfer %pK: tx len %u, done %u, rx len %u, done %u\n",
> > > -		xfer, length, xfer->tx_done, xfer->rx_len, xfer->rx_done);
> > > -
> > > -	if (length > DSI_TX_FIFO_SIZE)
> > > -		length = DSI_TX_FIFO_SIZE;
> > > -
> > > -	xfer->tx_done += length;
> > > -
> > > -	/* Send payload */
> > > -	while (length >= 4) {
> > > -		reg = get_unaligned_le32(payload);
> > > -		exynos_dsi_write(dsi, DSIM_PAYLOAD_REG, reg);
> > > -		payload += 4;
> > > -		length -= 4;
> > > -	}
> > > -
> > > -	reg = 0;
> > > -	switch (length) {
> > > -	case 3:
> > > -		reg |= payload[2] << 16;
> > > -		fallthrough;
> > > -	case 2:
> > > -		reg |= payload[1] << 8;
> > > -		fallthrough;
> > > -	case 1:
> > > -		reg |= payload[0];
> > > -		exynos_dsi_write(dsi, DSIM_PAYLOAD_REG, reg);
> > > -		break;
> > > -	}
> > > -
> > > -	/* Send packet header */
> > > -	if (!first)
> > > -		return;
> > > -
> > > -	reg = get_unaligned_le32(pkt->header);
> > > -	if (exynos_dsi_wait_for_hdr_fifo(dsi)) {
> > > -		dev_err(dev, "waiting for header FIFO timed out\n");
> > > -		return;
> > > -	}
> > > -
> > > -	if (NEQV(xfer->flags & MIPI_DSI_MSG_USE_LPM,
> > > -		 dsi->state & DSIM_STATE_CMD_LPM)) {
> > > -		exynos_dsi_set_cmd_lpm(dsi, xfer->flags & MIPI_DSI_MSG_USE_LPM);
> > > -		dsi->state ^= DSIM_STATE_CMD_LPM;
> > > -	}
> > > -
> > > -	exynos_dsi_write(dsi, DSIM_PKTHDR_REG, reg);
> > > -
> > > -	if (xfer->flags & MIPI_DSI_MSG_REQ_ACK)
> > > -		exynos_dsi_force_bta(dsi);
> > > -}
> > > -
> > > -static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi,
> > > -					struct exynos_dsi_transfer *xfer)
> > > -{
> > > -	u8 *payload = xfer->rx_payload + xfer->rx_done;
> > > -	bool first = !xfer->rx_done;
> > > -	struct device *dev = dsi->dev;
> > > -	u16 length;
> > > -	u32 reg;
> > > -
> > > -	if (first) {
> > > -		reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG);
> > > -
> > > -		switch (reg & 0x3f) {
> > > -		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
> > > -		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
> > > -			if (xfer->rx_len >= 2) {
> > > -				payload[1] = reg >> 16;
> > > -				++xfer->rx_done;
> > > -			}
> > > -			fallthrough;
> > > -		case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
> > > -		case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
> > > -			payload[0] = reg >> 8;
> > > -			++xfer->rx_done;
> > > -			xfer->rx_len = xfer->rx_done;
> > > -			xfer->result = 0;
> > > -			goto clear_fifo;
> > > -		case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
> > > -			dev_err(dev, "DSI Error Report: 0x%04x\n",
> > > -				(reg >> 8) & 0xffff);
> > > -			xfer->result = 0;
> > > -			goto clear_fifo;
> > > -		}
> > > -
> > > -		length = (reg >> 8) & 0xffff;
> > > -		if (length > xfer->rx_len) {
> > > -			dev_err(dev,
> > > -				"response too long (%u > %u bytes), stripping\n",
> > > -				xfer->rx_len, length);
> > > -			length = xfer->rx_len;
> > > -		} else if (length < xfer->rx_len)
> > > -			xfer->rx_len = length;
> > > -	}
> > > -
> > > -	length = xfer->rx_len - xfer->rx_done;
> > > -	xfer->rx_done += length;
> > > -
> > > -	/* Receive payload */
> > > -	while (length >= 4) {
> > > -		reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG);
> > > -		payload[0] = (reg >>  0) & 0xff;
> > > -		payload[1] = (reg >>  8) & 0xff;
> > > -		payload[2] = (reg >> 16) & 0xff;
> > > -		payload[3] = (reg >> 24) & 0xff;
> > > -		payload += 4;
> > > -		length -= 4;
> > > -	}
> > > -
> > > -	if (length) {
> > > -		reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG);
> > > -		switch (length) {
> > > -		case 3:
> > > -			payload[2] = (reg >> 16) & 0xff;
> > > -			fallthrough;
> > > -		case 2:
> > > -			payload[1] = (reg >> 8) & 0xff;
> > > -			fallthrough;
> > > -		case 1:
> > > -			payload[0] = reg & 0xff;
> > > -		}
> > > -	}
> > > -
> > > -	if (xfer->rx_done == xfer->rx_len)
> > > -		xfer->result = 0;
> > > -
> > > -clear_fifo:
> > > -	length = DSI_RX_FIFO_SIZE / 4;
> > > -	do {
> > > -		reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG);
> > > -		if (reg == DSI_RX_FIFO_EMPTY)
> > > -			break;
> > > -	} while (--length);
> > > -}
> > > -
> > > -static void exynos_dsi_transfer_start(struct exynos_dsi *dsi)
> > > -{
> > > -	unsigned long flags;
> > > -	struct exynos_dsi_transfer *xfer;
> > > -	bool start = false;
> > > -
> > > -again:
> > > -	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > > -
> > > -	if (list_empty(&dsi->transfer_list)) {
> > > -		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > > -		return;
> > > -	}
> > > -
> > > -	xfer = list_first_entry(&dsi->transfer_list,
> > > -					struct exynos_dsi_transfer, list);
> > > -
> > > -	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > > -
> > > -	if (xfer->packet.payload_length &&
> > > -	    xfer->tx_done == xfer->packet.payload_length)
> > > -		/* waiting for RX */
> > > -		return;
> > > -
> > > -	exynos_dsi_send_to_fifo(dsi, xfer);
> > > -
> > > -	if (xfer->packet.payload_length || xfer->rx_len)
> > > -		return;
> > > -
> > > -	xfer->result = 0;
> > > -	complete(&xfer->completed);
> > > -
> > > -	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > > -
> > > -	list_del_init(&xfer->list);
> > > -	start = !list_empty(&dsi->transfer_list);
> > > -
> > > -	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > > -
> > > -	if (start)
> > > -		goto again;
> > > -}
> > > -
> > > -static bool exynos_dsi_transfer_finish(struct exynos_dsi *dsi)
> > > -{
> > > -	struct exynos_dsi_transfer *xfer;
> > > -	unsigned long flags;
> > > -	bool start = true;
> > > -
> > > -	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > > -
> > > -	if (list_empty(&dsi->transfer_list)) {
> > > -		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > > -		return false;
> > > -	}
> > > -
> > > -	xfer = list_first_entry(&dsi->transfer_list,
> > > -					struct exynos_dsi_transfer, list);
> > > -
> > > -	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > > -
> > > -	dev_dbg(dsi->dev,
> > > -		"> xfer %pK, tx_len %zu, tx_done %u, rx_len %u, rx_done %u\n",
> > > -		xfer, xfer->packet.payload_length, xfer->tx_done, xfer->rx_len,
> > > -		xfer->rx_done);
> > > -
> > > -	if (xfer->tx_done != xfer->packet.payload_length)
> > > -		return true;
> > > -
> > > -	if (xfer->rx_done != xfer->rx_len)
> > > -		exynos_dsi_read_from_fifo(dsi, xfer);
> > > -
> > > -	if (xfer->rx_done != xfer->rx_len)
> > > -		return true;
> > > -
> > > -	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > > -
> > > -	list_del_init(&xfer->list);
> > > -	start = !list_empty(&dsi->transfer_list);
> > > -
> > > -	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > > -
> > > -	if (!xfer->rx_len)
> > > -		xfer->result = 0;
> > > -	complete(&xfer->completed);
> > > -
> > > -	return start;
> > > -}
> > > -
> > > -static void exynos_dsi_remove_transfer(struct exynos_dsi *dsi,
> > > -					struct exynos_dsi_transfer *xfer)
> > > -{
> > > -	unsigned long flags;
> > > -	bool start;
> > > -
> > > -	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > > -
> > > -	if (!list_empty(&dsi->transfer_list) &&
> > > -	    xfer == list_first_entry(&dsi->transfer_list,
> > > -				     struct exynos_dsi_transfer, list)) {
> > > -		list_del_init(&xfer->list);
> > > -		start = !list_empty(&dsi->transfer_list);
> > > -		spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > > -		if (start)
> > > -			exynos_dsi_transfer_start(dsi);
> > > -		return;
> > > -	}
> > > -
> > > -	list_del_init(&xfer->list);
> > > -
> > > -	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > > -}
> > > -
> > > -static int exynos_dsi_transfer(struct exynos_dsi *dsi,
> > > -					struct exynos_dsi_transfer *xfer)
> > > -{
> > > -	unsigned long flags;
> > > -	bool stopped;
> > > -
> > > -	xfer->tx_done = 0;
> > > -	xfer->rx_done = 0;
> > > -	xfer->result = -ETIMEDOUT;
> > > -	init_completion(&xfer->completed);
> > > -
> > > -	spin_lock_irqsave(&dsi->transfer_lock, flags);
> > > -
> > > -	stopped = list_empty(&dsi->transfer_list);
> > > -	list_add_tail(&xfer->list, &dsi->transfer_list);
> > > -
> > > -	spin_unlock_irqrestore(&dsi->transfer_lock, flags);
> > > -
> > > -	if (stopped)
> > > -		exynos_dsi_transfer_start(dsi);
> > > -
> > > -	wait_for_completion_timeout(&xfer->completed,
> > > -				    msecs_to_jiffies(DSI_XFER_TIMEOUT_MS));
> > > -	if (xfer->result == -ETIMEDOUT) {
> > > -		struct mipi_dsi_packet *pkt = &xfer->packet;
> > > -		exynos_dsi_remove_transfer(dsi, xfer);
> > > -		dev_err(dsi->dev, "xfer timed out: %*ph %*ph\n", 4, pkt->header,
> > > -			(int)pkt->payload_length, pkt->payload);
> > > -		return -ETIMEDOUT;
> > > -	}
> > > -
> > > -	/* Also covers hardware timeout condition */
> > > -	return xfer->result;
> > > -}
> > > -
> > > -static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
> > > -{
> > > -	struct exynos_dsi *dsi = dev_id;
> > > -	u32 status;
> > > -
> > > -	status = exynos_dsi_read(dsi, DSIM_INTSRC_REG);
> > > -	if (!status) {
> > > -		static unsigned long int j;
> > > -		if (printk_timed_ratelimit(&j, 500))
> > > -			dev_warn(dsi->dev, "spurious interrupt\n");
> > > -		return IRQ_HANDLED;
> > > -	}
> > > -	exynos_dsi_write(dsi, DSIM_INTSRC_REG, status);
> > > -
> > > -	if (status & DSIM_INT_SW_RST_RELEASE) {
> > > -		u32 mask = ~(DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY |
> > > -			DSIM_INT_SFR_HDR_FIFO_EMPTY | DSIM_INT_RX_ECC_ERR |
> > > -			DSIM_INT_SW_RST_RELEASE);
> > > -		exynos_dsi_write(dsi, DSIM_INTMSK_REG, mask);
> > > -		complete(&dsi->completed);
> > > -		return IRQ_HANDLED;
> > > -	}
> > > -
> > > -	if (!(status & (DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY |
> > > -			DSIM_INT_PLL_STABLE)))
> > > -		return IRQ_HANDLED;
> > > -
> > > -	if (exynos_dsi_transfer_finish(dsi))
> > > -		exynos_dsi_transfer_start(dsi);
> > > -
> > > -	return IRQ_HANDLED;
> > > -}
> > > -
> > > -static irqreturn_t exynos_dsi_te_irq_handler(int irq, void *dev_id)
> > > -{
> > > -	struct exynos_dsi *dsi = dev_id;
> > > -	const struct exynos_dsi_host_ops *ops = dsi->driver_data->host_ops;
> > > -
> > > -	if (ops && ops->te_handler &&
> > > -	    (dsi->state & DSIM_STATE_VIDOUT_AVAILABLE))
> > > -		ops->te_handler(dsi->dsi_host.dev);
> > > -
> > > -	return IRQ_HANDLED;
> > > -}
> > > -
> > > -static void exynos_dsi_enable_irq(struct exynos_dsi *dsi)
> > > -{
> > > -	enable_irq(dsi->irq);
> > > -
> > > -	if (gpio_is_valid(dsi->te_gpio))
> > > -		enable_irq(gpio_to_irq(dsi->te_gpio));
> > > -}
> > > -
> > > -static void exynos_dsi_disable_irq(struct exynos_dsi *dsi)
> > > -{
> > > -	if (gpio_is_valid(dsi->te_gpio))
> > > -		disable_irq(gpio_to_irq(dsi->te_gpio));
> > > -
> > > -	disable_irq(dsi->irq);
> > > -}
> > > -
> > > -static int exynos_dsi_init(struct exynos_dsi *dsi)
> > > +static int exynos_dsi_host_detach(struct device *dev,
> > > +				  struct mipi_dsi_device *device)
> > >  {
> > > -	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
> > > -
> > > -	exynos_dsi_reset(dsi);
> > > -	exynos_dsi_enable_irq(dsi);
> > > +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> > > +	struct drm_device *drm = dsi->encoder.dev;
> > >  
> > > -	if (driver_data->reg_values[RESET_TYPE] == DSIM_FUNCRST)
> > > -		exynos_dsi_enable_lane(dsi, BIT(dsi->lanes) - 1);
> > > -
> > > -	exynos_dsi_enable_clock(dsi);
> > > -	if (driver_data->wait_for_reset)
> > > -		exynos_dsi_wait_for_reset(dsi);
> > > -	exynos_dsi_set_phy_ctrl(dsi);
> > > -	exynos_dsi_init_link(dsi);
> > > +	if (drm->mode_config.poll_enabled)
> > > +		drm_kms_helper_hotplug_event(drm);
> > >  
> > >  	return 0;
> > >  }
> > >  
> > > -static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi,
> > > -				      struct device *panel)
> > > +static void exynos_dsi_te_handler(struct device *dev)
> > >  {
> > > -	int ret;
> > > -	int te_gpio_irq;
> > > +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> > >  
> > > -	dsi->te_gpio = of_get_named_gpio(panel->of_node, "te-gpios", 0);
> > > -	if (dsi->te_gpio == -ENOENT)
> > > -		return 0;
> > > -
> > > -	if (!gpio_is_valid(dsi->te_gpio)) {
> > > -		ret = dsi->te_gpio;
> > > -		dev_err(dsi->dev, "cannot get te-gpios, %d\n", ret);
> > > -		goto out;
> > > -	}
> > > -
> > > -	ret = gpio_request(dsi->te_gpio, "te_gpio");
> > > -	if (ret) {
> > > -		dev_err(dsi->dev, "gpio request failed with %d\n", ret);
> > > -		goto out;
> > > -	}
> > > -
> > > -	te_gpio_irq = gpio_to_irq(dsi->te_gpio);
> > > -	irq_set_status_flags(te_gpio_irq, IRQ_NOAUTOEN);
> > > -
> > > -	ret = request_threaded_irq(te_gpio_irq, exynos_dsi_te_irq_handler, NULL,
> > > -					IRQF_TRIGGER_RISING, "TE", dsi);
> > > -	if (ret) {
> > > -		dev_err(dsi->dev, "request interrupt failed with %d\n", ret);
> > > -		gpio_free(dsi->te_gpio);
> > > -		goto out;
> > > -	}
> > > -
> > > -out:
> > > -	return ret;
> > > +	exynos_drm_crtc_te_handler(dsi->encoder.crtc);
> > >  }
> > >  
> > > -static void exynos_dsi_unregister_te_irq(struct exynos_dsi *dsi)
> > > -{
> > > -	if (gpio_is_valid(dsi->te_gpio)) {
> > > -		free_irq(gpio_to_irq(dsi->te_gpio), dsi);
> > > -		gpio_free(dsi->te_gpio);
> > > -		dsi->te_gpio = -ENOENT;
> > > -	}
> > > -}
> > > -
> > > -static void exynos_dsi_enable(struct exynos_dsi *dsi)
> > > -{
> > > -	struct drm_bridge *iter;
> > > -	int ret;
> > > -
> > > -	if (dsi->state & DSIM_STATE_ENABLED)
> > > -		return;
> > > -
> > > -	pm_runtime_get_sync(dsi->dev);
> > > -	dsi->state |= DSIM_STATE_ENABLED;
> > > -
> > > -	if (dsi->panel) {
> > > -		ret = drm_panel_prepare(dsi->panel);
> > > -		if (ret < 0)
> > > -			goto err_put_sync;
> > > -	} else {
> > > -		list_for_each_entry_reverse(iter, &dsi->bridge_chain,
> > > -					    chain_node) {
> > > -			if (iter->funcs->pre_enable)
> > > -				iter->funcs->pre_enable(iter);
> > > -		}
> > > -	}
> > > -
> > > -	exynos_dsi_set_display_mode(dsi);
> > > -	exynos_dsi_set_display_enable(dsi, true);
> > > -
> > > -	if (dsi->panel) {
> > > -		ret = drm_panel_enable(dsi->panel);
> > > -		if (ret < 0)
> > > -			goto err_display_disable;
> > > -	} else {
> > > -		list_for_each_entry(iter, &dsi->bridge_chain, chain_node) {
> > > -			if (iter->funcs->enable)
> > > -				iter->funcs->enable(iter);
> > > -		}
> > > -	}
> > > -
> > > -	dsi->state |= DSIM_STATE_VIDOUT_AVAILABLE;
> > > -	return;
> > > -
> > > -err_display_disable:
> > > -	exynos_dsi_set_display_enable(dsi, false);
> > > -	drm_panel_unprepare(dsi->panel);
> > > -
> > > -err_put_sync:
> > > -	dsi->state &= ~DSIM_STATE_ENABLED;
> > > -	pm_runtime_put(dsi->dev);
> > > -}
> > > -
> > > -static void exynos_dsi_disable(struct exynos_dsi *dsi)
> > > -{
> > > -	struct drm_bridge *iter;
> > > -
> > > -	if (!(dsi->state & DSIM_STATE_ENABLED))
> > > -		return;
> > > -
> > > -	dsi->state &= ~DSIM_STATE_VIDOUT_AVAILABLE;
> > > -
> > > -	drm_panel_disable(dsi->panel);
> > > -
> > > -	list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) {
> > > -		if (iter->funcs->disable)
> > > -			iter->funcs->disable(iter);
> > > -	}
> > > -
> > > -	exynos_dsi_set_display_enable(dsi, false);
> > > -	drm_panel_unprepare(dsi->panel);
> > > -
> > > -	list_for_each_entry(iter, &dsi->bridge_chain, chain_node) {
> > > -		if (iter->funcs->post_disable)
> > > -			iter->funcs->post_disable(iter);
> > > -	}
> > > -
> > > -	dsi->state &= ~DSIM_STATE_ENABLED;
> > > -	pm_runtime_put_sync(dsi->dev);
> > > -}
> > > -
> > > -static enum drm_connector_status
> > > -exynos_dsi_detect(struct drm_connector *connector, bool force)
> > > -{
> > > -	return connector->status;
> > > -}
> > > +static const struct samsung_dsim_host_ops exynos_dsi_host_ops = {
> > > +	.attach = exynos_dsi_host_attach,
> > > +	.detach = exynos_dsi_host_detach,
> > > +	.te_handler = exynos_dsi_te_handler,
> > > +};
> > >  
> > > -static void exynos_dsi_connector_destroy(struct drm_connector *connector)
> > > -{
> > > -	drm_connector_unregister(connector);
> > > -	drm_connector_cleanup(connector);
> > > -	connector->dev = NULL;
> > > -}
> > > +static const struct samsung_dsim_driver_data exynos3_dsi_driver_data = {
> > > +	.reg_ofs = EXYNOS_REG_OFS,
> > > +	.plltmr_reg = 0x50,
> > > +	.has_freqband = 1,
> > > +	.has_clklane_stop = 1,
> > > +	.num_clks = 2,
> > > +	.max_freq = 1000,
> > > +	.wait_for_reset = 1,
> > > +	.num_bits_resol = 11,
> > > +	.reg_values = reg_values,
> > > +	.host_ops = &exynos_dsi_host_ops,
> > > +};
> > >  
> > > -static const struct drm_connector_funcs exynos_dsi_connector_funcs = {
> > > -	.detect = exynos_dsi_detect,
> > > -	.fill_modes = drm_helper_probe_single_connector_modes,
> > > -	.destroy = exynos_dsi_connector_destroy,
> > > -	.reset = drm_atomic_helper_connector_reset,
> > > -	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> > > -	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> > > +static const struct samsung_dsim_driver_data exynos4_dsi_driver_data = {
> > > +	.reg_ofs = EXYNOS_REG_OFS,
> > > +	.plltmr_reg = 0x50,
> > > +	.has_freqband = 1,
> > > +	.has_clklane_stop = 1,
> > > +	.num_clks = 2,
> > > +	.max_freq = 1000,
> > > +	.wait_for_reset = 1,
> > > +	.num_bits_resol = 11,
> > > +	.reg_values = reg_values,
> > > +	.host_ops = &exynos_dsi_host_ops,
> > >  };
> > >  
> > > -static int exynos_dsi_get_modes(struct drm_connector *connector)
> > > -{
> > > -	struct exynos_dsi *dsi = connector_to_dsi(connector);
> > > +static const struct samsung_dsim_driver_data exynos5_dsi_driver_data = {
> > > +	.reg_ofs = EXYNOS_REG_OFS,
> > > +	.plltmr_reg = 0x58,
> > > +	.num_clks = 2,
> > > +	.max_freq = 1000,
> > > +	.wait_for_reset = 1,
> > > +	.num_bits_resol = 11,
> > > +	.reg_values = reg_values,
> > > +	.host_ops = &exynos_dsi_host_ops,
> > > +};
> > >  
> > > -	if (dsi->panel)
> > > -		return drm_panel_get_modes(dsi->panel, connector);
> > > +static const struct samsung_dsim_driver_data exynos5433_dsi_driver_data = {
> > > +	.reg_ofs = EXYNOS5433_REG_OFS,
> > > +	.plltmr_reg = 0xa0,
> > > +	.has_clklane_stop = 1,
> > > +	.num_clks = 5,
> > > +	.max_freq = 1500,
> > > +	.wait_for_reset = 0,
> > > +	.num_bits_resol = 12,
> > > +	.reg_values = exynos5433_reg_values,
> > > +	.host_ops = &exynos_dsi_host_ops,
> > > +};
> > >  
> > > -	return 0;
> > > -}
> > > +static const struct samsung_dsim_driver_data exynos5422_dsi_driver_data = {
> > > +	.reg_ofs = EXYNOS5433_REG_OFS,
> > > +	.plltmr_reg = 0xa0,
> > > +	.has_clklane_stop = 1,
> > > +	.num_clks = 2,
> > > +	.max_freq = 1500,
> > > +	.wait_for_reset = 1,
> > > +	.num_bits_resol = 12,
> > > +	.reg_values = exynos5422_reg_values,
> > > +	.host_ops = &exynos_dsi_host_ops,
> > > +};
> > >  
> > > -static const struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = {
> > > -	.get_modes = exynos_dsi_get_modes,
> > > +static const struct of_device_id exynos_dsi_of_match[] = {
> > > +	{ .compatible = "samsung,exynos3250-mipi-dsi",
> > > +	  .data = &exynos3_dsi_driver_data },
> > > +	{ .compatible = "samsung,exynos4210-mipi-dsi",
> > > +	  .data = &exynos4_dsi_driver_data },
> > > +	{ .compatible = "samsung,exynos5410-mipi-dsi",
> > > +	  .data = &exynos5_dsi_driver_data },
> > > +	{ .compatible = "samsung,exynos5422-mipi-dsi",
> > > +	  .data = &exynos5422_dsi_driver_data },
> > > +	{ .compatible = "samsung,exynos5433-mipi-dsi",
> > > +	  .data = &exynos5433_dsi_driver_data },
> > > +	{ }
> > >  };
> > >  
> > > -static int exynos_dsi_create_connector(struct exynos_dsi *dsi)
> > > +static int exynos_dsi_bind(struct device *dev,
> > > +			   struct device *master, void *data)
> > >  {
> > > -	struct drm_connector *connector = &dsi->connector;
> > > -	struct drm_device *drm = dsi->bridge.dev;
> > > +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> > > +	struct drm_encoder *encoder = &dsi->encoder;
> > > +	struct drm_device *drm_dev = data;
> > > +	struct device_node *in_bridge_node;
> > > +	struct drm_bridge *in_bridge;
> > >  	int ret;
> > >  
> > > -	connector->polled = DRM_CONNECTOR_POLL_HPD;
> > > +	drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_TMDS);
> > >  
> > > -	ret = drm_connector_init(drm, connector, &exynos_dsi_connector_funcs,
> > > -				 DRM_MODE_CONNECTOR_DSI);
> > > -	if (ret) {
> > > -		DRM_DEV_ERROR(dsi->dev,
> > > -			      "Failed to initialize connector with drm\n");
> > > +	ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
> > > +	if (ret < 0)
> > >  		return ret;
> > > -	}
> > > -
> > > -	connector->status = connector_status_disconnected;
> > > -	drm_connector_helper_add(connector, &exynos_dsi_connector_helper_funcs);
> > > -	drm_connector_attach_encoder(connector, dsi->bridge.encoder);
> > > -	if (!drm->registered)
> > > -		return 0;
> > > -
> > > -	connector->funcs->reset(connector);
> > > -	drm_connector_register(connector);
> > > -	return 0;
> > > -}
> > > -
> > > -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(dsi);
> > > -		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);
> > > -		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);
> > > +	in_bridge_node = of_graph_get_remote_node(dev->of_node, DSI_PORT_IN, 0);
> > > +	if (in_bridge_node) {
> > > +		in_bridge = of_drm_find_bridge(in_bridge_node);
> > > +		if (in_bridge)
> > > +			drm_bridge_attach(encoder, in_bridge, NULL, 0);
> > > +		of_node_put(in_bridge_node);
> > >  	}
> > > -}
> > > -
> > > -static void exynos_dsi_bridge_enable(struct drm_bridge *bridge)
> > > -{
> > > -	struct exynos_dsi *dsi = bridge->driver_private;
> > > -
> > > -	exynos_dsi_enable(dsi);
> > > -}
> > > -
> > > -static void exynos_dsi_bridge_disable(struct drm_bridge *bridge)
> > > -{
> > > -	struct exynos_dsi *dsi = bridge->driver_private;
> > > -
> > > -	exynos_dsi_disable(dsi);
> > > -}
> > >  
> > > -static void exynos_dsi_bridge_mode_set(struct drm_bridge *bridge,
> > > -				       const struct drm_display_mode *mode,
> > > -				       const struct drm_display_mode *adjusted_mode)
> > > -{
> > > -	struct exynos_dsi *dsi = bridge->driver_private;
> > > -
> > > -	/* The mode is set when actually enabling the device. */
> > > -	drm_mode_copy(&dsi->mode, adjusted_mode);
> > > -}
> > > -
> > > -static const struct drm_bridge_funcs exynos_dsi_bridge_funcs = {
> > > -	.attach = exynos_dsi_bridge_attach,
> > > -	.detach = exynos_dsi_bridge_detach,
> > > -	.enable = exynos_dsi_bridge_enable,
> > > -	.disable = exynos_dsi_bridge_disable,
> > > -	.mode_set = exynos_dsi_bridge_mode_set,
> > > -};
> > > -
> > > -static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
> > > -				  struct mipi_dsi_device *device)
> > > -{
> > > -	struct exynos_dsi *dsi = host_to_dsi(host);
> > > -	const struct exynos_dsi_host_ops *ops = dsi->driver_data->host_ops;
> > > -	struct drm_bridge *out_bridge;
> > > -
> > > -	out_bridge = of_drm_find_bridge(device->dev.of_node);
> > > -	if (out_bridge) {
> > > -		dsi->out_bridge = out_bridge;
> > > -	} else {
> > > -		dsi->panel = of_drm_find_panel(device->dev.of_node);
> > > -		if (IS_ERR(dsi->panel))
> > > -			dsi->panel = NULL;
> > > -		else
> > > -			dsi->connector.status = connector_status_connected;
> > > -	}
> > > -
> > > -	/*
> > > -	 * This is a temporary solution and should be made by more generic way.
> > > -	 *
> > > -	 * If attached panel device is for command mode one, dsi should register
> > > -	 * TE interrupt handler.
> > > -	 */
> > > -	if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO)) {
> > > -		int ret = exynos_dsi_register_te_irq(dsi, &device->dev);
> > > -		if (ret)
> > > -			return ret;
> > > -	}
> > > -
> > > -	dsi->lanes = device->lanes;
> > > -	dsi->format = device->format;
> > > -	dsi->mode_flags = device->mode_flags;
> > > -
> > > -	if (ops && ops->attach)
> > > -		ops->attach(dsi->dsi_host.dev, device);
> > > +	ret = samsung_dsim_bind(dsi->dsi, encoder);
> > > +	if (ret)
> > > +		goto err;
> > >  
> > >  	return 0;
> > > -}
> > > -
> > > -static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
> > > -				  struct mipi_dsi_device *device)
> > > -{
> > > -	struct exynos_dsi *dsi = host_to_dsi(host);
> > > -	const struct exynos_dsi_host_ops *ops = dsi->driver_data->host_ops;
> > >  
> > > -	if (ops && ops->detach)
> > > -		ops->detach(dsi->dsi_host.dev, device);
> > > -
> > > -	exynos_dsi_unregister_te_irq(dsi);
> > > -
> > > -	return 0;
> > > +err:
> > > +	drm_encoder_cleanup(encoder);
> > > +	return ret;
> > >  }
> > >  
> > > -static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host,
> > > -					 const struct mipi_dsi_msg *msg)
> > > +static void exynos_dsi_unbind(struct device *dev,
> > > +			      struct device *master, void *data)
> > >  {
> > > -	struct exynos_dsi *dsi = host_to_dsi(host);
> > > -	struct exynos_dsi_transfer xfer;
> > > -	int ret;
> > > +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> > > +	struct drm_encoder *encoder = &dsi->encoder;
> > >  
> > > -	if (!(dsi->state & DSIM_STATE_ENABLED))
> > > -		return -EINVAL;
> > > -
> > > -	if (!(dsi->state & DSIM_STATE_INITIALIZED)) {
> > > -		ret = exynos_dsi_init(dsi);
> > > -		if (ret)
> > > -			return ret;
> > > -		dsi->state |= DSIM_STATE_INITIALIZED;
> > > -	}
> > > -
> > > -	ret = mipi_dsi_create_packet(&xfer.packet, msg);
> > > -	if (ret < 0)
> > > -		return ret;
> > > +	samsung_dsim_unbind(dsi->dsi);
> > >  
> > > -	xfer.rx_len = msg->rx_len;
> > > -	xfer.rx_payload = msg->rx_buf;
> > > -	xfer.flags = msg->flags;
> > > -
> > > -	ret = exynos_dsi_transfer(dsi, &xfer);
> > > -	return (ret < 0) ? ret : xfer.rx_done;
> > > +	drm_encoder_cleanup(encoder);
> > >  }
> > >  
> > > -static const struct mipi_dsi_host_ops exynos_dsi_ops = {
> > > -	.attach = exynos_dsi_host_attach,
> > > -	.detach = exynos_dsi_host_detach,
> > > -	.transfer = exynos_dsi_host_transfer,
> > > +static const struct component_ops exynos_dsi_component_ops = {
> > > +	.bind	= exynos_dsi_bind,
> > > +	.unbind	= exynos_dsi_unbind,
> > >  };
> > >  
> > > -static int exynos_dsi_of_read_u32(const struct device_node *np,
> > > -				  const char *propname, u32 *out_value)
> > > -{
> > > -	int ret = of_property_read_u32(np, propname, out_value);
> > > -
> > > -	if (ret < 0)
> > > -		pr_err("%pOF: failed to get '%s' property\n", np, propname);
> > > -
> > > -	return ret;
> > > -}
> > > -
> > > -static int exynos_dsi_parse_dt(struct exynos_dsi *dsi)
> > > -{
> > > -	struct device *dev = dsi->dev;
> > > -	struct device_node *node = dev->of_node;
> > > -	int ret;
> > > -
> > > -	ret = exynos_dsi_of_read_u32(node, "samsung,pll-clock-frequency",
> > > -				     &dsi->pll_clk_rate);
> > > -	if (ret < 0)
> > > -		return ret;
> > > -
> > > -	ret = exynos_dsi_of_read_u32(node, "samsung,burst-clock-frequency",
> > > -				     &dsi->burst_clk_rate);
> > > -	if (ret < 0)
> > > -		return ret;
> > > -
> > > -	ret = exynos_dsi_of_read_u32(node, "samsung,esc-clock-frequency",
> > > -				     &dsi->esc_clk_rate);
> > > -	if (ret < 0)
> > > -		return ret;
> > > -
> > > -	return 0;
> > > -}
> > > -
> > > -static struct exynos_dsi *__exynos_dsi_probe(struct platform_device *pdev)
> > > +static int 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;
> > > +	struct device *dev = &pdev->dev;
> > > +	int ret;
> > >  
> > >  	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
> > >  	if (!dsi)
> > > -		return ERR_PTR(-ENOMEM);
> > > +		return -ENOMEM;
> > > +	platform_set_drvdata(pdev, dsi);
> > >  
> > > -	/* To be checked as invalid one */
> > > -	dsi->te_gpio = -ENOENT;
> > > +	dsi->dsi = samsung_dsim_probe(pdev);
> > > +	if (IS_ERR(dsi->dsi))
> > > +		return PTR_ERR(dsi->dsi);
> > >  
> > > -	init_completion(&dsi->completed);
> > > -	spin_lock_init(&dsi->transfer_lock);
> > > -	INIT_LIST_HEAD(&dsi->transfer_list);
> > > -	INIT_LIST_HEAD(&dsi->bridge_chain);
> > > +	pm_runtime_enable(dev);
> > >  
> > > -	dsi->dsi_host.ops = &exynos_dsi_ops;
> > > -	dsi->dsi_host.dev = dev;
> > > -
> > > -	dsi->dev = dev;
> > > -	dsi->driver_data = of_device_get_match_data(dev);
> > > -
> > > -	dsi->supplies[0].supply = "vddcore";
> > > -	dsi->supplies[1].supply = "vddio";
> > > -	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(dsi->supplies),
> > > -				      dsi->supplies);
> > > -	if (ret) {
> > > -		if (ret != -EPROBE_DEFER)
> > > -			dev_info(dev, "failed to get regulators: %d\n", ret);
> > > -		return ERR_PTR(ret);
> > > -	}
> > > -
> > > -	dsi->clks = devm_kcalloc(dev,
> > > -			dsi->driver_data->num_clks, sizeof(*dsi->clks),
> > > -			GFP_KERNEL);
> > > -	if (!dsi->clks)
> > > -		return ERR_PTR(-ENOMEM);
> > > -
> > > -	for (i = 0; i < dsi->driver_data->num_clks; i++) {
> > > -		dsi->clks[i] = devm_clk_get(dev, clk_names[i]);
> > > -		if (IS_ERR(dsi->clks[i])) {
> > > -			if (strcmp(clk_names[i], "sclk_mipi") == 0) {
> > > -				dsi->clks[i] = devm_clk_get(dev,
> > > -							OLD_SCLK_MIPI_CLK_NAME);
> > > -				if (!IS_ERR(dsi->clks[i]))
> > > -					continue;
> > > -			}
> > > -
> > > -			dev_info(dev, "failed to get the clock: %s\n",
> > > -					clk_names[i]);
> > > -			return ERR_PTR(PTR_ERR(dsi->clks[i]));
> > > -		}
> > > -	}
> > > -
> > > -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > > -	dsi->reg_base = devm_ioremap_resource(dev, res);
> > > -	if (IS_ERR(dsi->reg_base)) {
> > > -		dev_err(dev, "failed to remap io region\n");
> > > -		return dsi->reg_base;
> > > -	}
> > > -
> > > -	dsi->phy = devm_phy_get(dev, "dsim");
> > > -	if (IS_ERR(dsi->phy)) {
> > > -		dev_info(dev, "failed to get dsim phy\n");
> > > -		return ERR_PTR(PTR_ERR(dsi->phy));
> > > -	}
> > > -
> > > -	dsi->irq = platform_get_irq(pdev, 0);
> > > -	if (dsi->irq < 0)
> > > -		return ERR_PTR(dsi->irq);
> > > -
> > > -	irq_set_status_flags(dsi->irq, IRQ_NOAUTOEN);
> > > -	ret = devm_request_threaded_irq(dev, dsi->irq, NULL,
> > > -					exynos_dsi_irq, IRQF_ONESHOT,
> > > -					dev_name(dev), dsi);
> > > -	if (ret) {
> > > -		dev_err(dev, "failed to request dsi irq\n");
> > > -		return ERR_PTR(ret);
> > > -	}
> > > -
> > > -	ret = exynos_dsi_parse_dt(dsi);
> > > +	ret = component_add(dev, &exynos_dsi_component_ops);
> > >  	if (ret)
> > > -		return ERR_PTR(ret);
> > > +		goto err_disable_runtime;
> > >  
> > > -	ret = mipi_dsi_host_register(&dsi->dsi_host);
> > > -	if (ret)
> > > -		return ERR_PTR(ret);
> > > +	return 0;
> > >  
> > > -	bridge = &dsi->bridge;
> > > -	bridge->driver_private = dsi;
> > > -	bridge->funcs = &exynos_dsi_bridge_funcs;
> > > -	bridge->of_node = dev->of_node;
> > > -	drm_bridge_add(bridge);
> > > +err_disable_runtime:
> > > +	pm_runtime_disable(dev);
> > >  
> > > -	return dsi;
> > > +	return ret;
> > >  }
> > >  
> > > -static void __exynos_dsi_remove(struct exynos_dsi *dsi)
> > > +static int exynos_dsi_remove(struct platform_device *pdev)
> > >  {
> > > -	drm_bridge_remove(&dsi->bridge);
> > > +	struct exynos_dsi *dsi = platform_get_drvdata(pdev);
> > >  
> > > -	mipi_dsi_host_unregister(&dsi->dsi_host);
> > > -}
> > > -
> > > -/*
> > > - * Probe/remove API, used from platforms based on the DRM bridge API.
> > > - */
> > > -struct exynos_dsi *exynos_dsi_probe(struct platform_device *pdev)
> > > -{
> > > -	return __exynos_dsi_probe(pdev);
> > > -}
> > > +	pm_runtime_disable(&pdev->dev);
> > >  
> > > -void exynos_dsi_remove(struct exynos_dsi *dsi)
> > > -{
> > > -	return __exynos_dsi_remove(dsi);
> > > -}
> > > +	samsung_dsim_remove(dsi->dsi);
> > >  
> > > -/*
> > > - * Bind/unbind API, used from platforms based on the component framework.
> > > - */
> > > -int exynos_dsi_bind(struct exynos_dsi *dsi, struct drm_encoder *encoder)
> > > -{
> > > -	struct drm_bridge *previous = drm_bridge_chain_get_first_bridge(encoder);
> > > +	component_del(&pdev->dev, &exynos_dsi_component_ops);
> > >  
> > > -	return drm_bridge_attach(encoder, &dsi->bridge, previous, 0);
> > > -}
> > > -
> > > -void exynos_dsi_unbind(struct exynos_dsi *dsi)
> > > -{
> > > -	exynos_dsi_disable(dsi);
> > > +	return 0;
> > >  }
> > >  
> > > -int exynos_dsi_suspend(struct exynos_dsi *dsi)
> > > +static int __maybe_unused exynos_dsi_suspend(struct device *dev)
> > >  {
> > > -	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
> > > -	int ret, i;
> > > -
> > > -	usleep_range(10000, 20000);
> > > -
> > > -	if (dsi->state & DSIM_STATE_INITIALIZED) {
> > > -		dsi->state &= ~DSIM_STATE_INITIALIZED;
> > > -
> > > -		exynos_dsi_disable_clock(dsi);
> > > -
> > > -		exynos_dsi_disable_irq(dsi);
> > > -	}
> > > -
> > > -	dsi->state &= ~DSIM_STATE_CMD_LPM;
> > > -
> > > -	phy_power_off(dsi->phy);
> > > -
> > > -	for (i = driver_data->num_clks - 1; i > -1; i--)
> > > -		clk_disable_unprepare(dsi->clks[i]);
> > > -
> > > -	ret = regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
> > > -	if (ret < 0)
> > > -		dev_err(dsi->dev, "cannot disable regulators %d\n", ret);
> > > +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> > >  
> > > -	return 0;
> > > +	return samsung_dsim_suspend(dsi->dsi);
> > >  }
> > >  
> > > -int exynos_dsi_resume(struct exynos_dsi *dsi)
> > > +static int __maybe_unused exynos_dsi_resume(struct device *dev)
> > >  {
> > > -	const struct exynos_dsi_driver_data *driver_data = dsi->driver_data;
> > > -	int ret, i;
> > > -
> > > -	ret = regulator_bulk_enable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
> > > -	if (ret < 0) {
> > > -		dev_err(dsi->dev, "cannot enable regulators %d\n", ret);
> > > -		return ret;
> > > -	}
> > > -
> > > -	for (i = 0; i < driver_data->num_clks; i++) {
> > > -		ret = clk_prepare_enable(dsi->clks[i]);
> > > -		if (ret < 0)
> > > -			goto err_clk;
> > > -	}
> > > -
> > > -	ret = phy_power_on(dsi->phy);
> > > -	if (ret < 0) {
> > > -		dev_err(dsi->dev, "cannot enable phy %d\n", ret);
> > > -		goto err_clk;
> > > -	}
> > > +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
> > >  
> > > -	return 0;
> > > +	return samsung_dsim_resume(dsi->dsi);
> > > +}
> > >  
> > > -err_clk:
> > > -	while (--i > -1)
> > > -		clk_disable_unprepare(dsi->clks[i]);
> > > -	regulator_bulk_disable(ARRAY_SIZE(dsi->supplies), dsi->supplies);
> > > +static const struct dev_pm_ops exynos_dsi_pm_ops = {
> > > +	SET_RUNTIME_PM_OPS(exynos_dsi_suspend, exynos_dsi_resume, NULL)
> > > +	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> > > +				pm_runtime_force_resume)
> > > +};
> > >  
> > > -	return ret;
> > > -}
> > > +struct platform_driver dsi_driver = {
> > > +	.probe = exynos_dsi_probe,
> > > +	.remove = exynos_dsi_remove,
> > > +	.driver = {
> > > +		   .name = "exynos-dsi",
> > > +		   .owner = THIS_MODULE,
> > > +		   .pm = &exynos_dsi_pm_ops,
> > > +		   .of_match_table = exynos_dsi_of_match,
> > > +	},
> > > +};
> > >  
> > >  MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
> > >  MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
> > > diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c b/drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c
> > > deleted file mode 100644
> > > index 79d9ec6ade45..000000000000
> > > --- a/drivers/gpu/drm/exynos/exynos_drm_dsi_pltfm.c
> > > +++ /dev/null
> > > @@ -1,333 +0,0 @@
> > > -// SPDX-License-Identifier: GPL-2.0-only
> > > -/*
> > > - * Samsung SoC MIPI DSI Master driver.
> > > - *
> > > - * Copyright (c) 2014 Samsung Electronics Co., Ltd
> > > - *
> > > - * Contacts: Tomasz Figa <t.figa@samsung.com>
> > > - */
> > > -
> > > -#include <linux/component.h>
> > > -#include <linux/of_device.h>
> > > -#include <linux/of_graph.h>
> > > -#include <linux/pm_runtime.h>
> > > -
> > > -#include <drm/drm_bridge.h>
> > > -#include <drm/drm_encoder.h>
> > > -#include <drm/drm_mipi_dsi.h>
> > > -#include <drm/drm_probe_helper.h>
> > > -#include <drm/drm_simple_kms_helper.h>
> > > -
> > > -#include "exynos_drm_crtc.h"
> > > -#include "exynos_drm_drv.h"
> > > -#include "exynos_drm_dsi.h"
> > > -
> > > -enum {
> > > -	DSI_PORT_IN,
> > > -	DSI_PORT_OUT
> > > -};
> > > -
> > > -struct exynos_dsi_pltfm {
> > > -	struct exynos_dsi *dsi;
> > > -	struct drm_encoder encoder;
> > > -};
> > > -
> > > -static const unsigned int reg_values[] = {
> > > -	[RESET_TYPE] = DSIM_SWRST,
> > > -	[PLL_TIMER] = 500,
> > > -	[STOP_STATE_CNT] = 0xf,
> > > -	[PHYCTRL_ULPS_EXIT] = 0x0af,
> > > -	[PHYCTRL_VREG_LP] = 0,
> > > -	[PHYCTRL_SLEW_UP] = 0,
> > > -	[PHYTIMING_LPX] = 0x06,
> > > -	[PHYTIMING_HS_EXIT] = 0x0b,
> > > -	[PHYTIMING_CLK_PREPARE] = 0x07,
> > > -	[PHYTIMING_CLK_ZERO] = 0x27,
> > > -	[PHYTIMING_CLK_POST] = 0x0d,
> > > -	[PHYTIMING_CLK_TRAIL] = 0x08,
> > > -	[PHYTIMING_HS_PREPARE] = 0x09,
> > > -	[PHYTIMING_HS_ZERO] = 0x0d,
> > > -	[PHYTIMING_HS_TRAIL] = 0x0b,
> > > -};
> > > -
> > > -static const unsigned int exynos5422_reg_values[] = {
> > > -	[RESET_TYPE] = DSIM_SWRST,
> > > -	[PLL_TIMER] = 500,
> > > -	[STOP_STATE_CNT] = 0xf,
> > > -	[PHYCTRL_ULPS_EXIT] = 0xaf,
> > > -	[PHYCTRL_VREG_LP] = 0,
> > > -	[PHYCTRL_SLEW_UP] = 0,
> > > -	[PHYTIMING_LPX] = 0x08,
> > > -	[PHYTIMING_HS_EXIT] = 0x0d,
> > > -	[PHYTIMING_CLK_PREPARE] = 0x09,
> > > -	[PHYTIMING_CLK_ZERO] = 0x30,
> > > -	[PHYTIMING_CLK_POST] = 0x0e,
> > > -	[PHYTIMING_CLK_TRAIL] = 0x0a,
> > > -	[PHYTIMING_HS_PREPARE] = 0x0c,
> > > -	[PHYTIMING_HS_ZERO] = 0x11,
> > > -	[PHYTIMING_HS_TRAIL] = 0x0d,
> > > -};
> > > -
> > > -static const unsigned int exynos5433_reg_values[] = {
> > > -	[RESET_TYPE] = DSIM_FUNCRST,
> > > -	[PLL_TIMER] = 22200,
> > > -	[STOP_STATE_CNT] = 0xa,
> > > -	[PHYCTRL_ULPS_EXIT] = 0x190,
> > > -	[PHYCTRL_VREG_LP] = 1,
> > > -	[PHYCTRL_SLEW_UP] = 1,
> > > -	[PHYTIMING_LPX] = 0x07,
> > > -	[PHYTIMING_HS_EXIT] = 0x0c,
> > > -	[PHYTIMING_CLK_PREPARE] = 0x09,
> > > -	[PHYTIMING_CLK_ZERO] = 0x2d,
> > > -	[PHYTIMING_CLK_POST] = 0x0e,
> > > -	[PHYTIMING_CLK_TRAIL] = 0x09,
> > > -	[PHYTIMING_HS_PREPARE] = 0x0b,
> > > -	[PHYTIMING_HS_ZERO] = 0x10,
> > > -	[PHYTIMING_HS_TRAIL] = 0x0c,
> > > -};
> > > -
> > > -static int __exynos_dsi_host_attach(struct device *dev,
> > > -				    struct mipi_dsi_device *device)
> > > -{
> > > -	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
> > > -	struct drm_device *drm = dsi->encoder.dev;
> > > -	struct exynos_drm_crtc *crtc;
> > > -
> > > -	mutex_lock(&drm->mode_config.mutex);
> > > -	crtc = exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD);
> > > -	crtc->i80_mode = !(device->mode_flags & MIPI_DSI_MODE_VIDEO);
> > > -	mutex_unlock(&drm->mode_config.mutex);
> > > -
> > > -	if (drm->mode_config.poll_enabled)
> > > -		drm_kms_helper_hotplug_event(drm);
> > > -
> > > -	return 0;
> > > -}
> > > -
> > > -static int __exynos_dsi_host_detach(struct device *dev,
> > > -				    struct mipi_dsi_device *device)
> > > -{
> > > -	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
> > > -	struct drm_device *drm = dsi->encoder.dev;
> > > -
> > > -	if (drm->mode_config.poll_enabled)
> > > -		drm_kms_helper_hotplug_event(drm);
> > > -
> > > -	return 0;
> > > -}
> > > -
> > > -static void __exynos_dsi_te_handler(struct device *dev)
> > > -{
> > > -	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
> > > -
> > > -	exynos_drm_crtc_te_handler(dsi->encoder.crtc);
> > > -}
> > > -
> > > -static const struct exynos_dsi_host_ops exynos_dsi_host_ops = {
> > > -	.attach = __exynos_dsi_host_attach,
> > > -	.detach = __exynos_dsi_host_detach,
> > > -	.te_handler = __exynos_dsi_te_handler,
> > > -};
> > > -
> > > -static const struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
> > > -	.reg_ofs = EXYNOS_REG_OFS,
> > > -	.plltmr_reg = 0x50,
> > > -	.has_freqband = 1,
> > > -	.has_clklane_stop = 1,
> > > -	.num_clks = 2,
> > > -	.max_freq = 1000,
> > > -	.wait_for_reset = 1,
> > > -	.num_bits_resol = 11,
> > > -	.reg_values = reg_values,
> > > -	.host_ops = &exynos_dsi_host_ops,
> > > -};
> > > -
> > > -static const struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
> > > -	.reg_ofs = EXYNOS_REG_OFS,
> > > -	.plltmr_reg = 0x50,
> > > -	.has_freqband = 1,
> > > -	.has_clklane_stop = 1,
> > > -	.num_clks = 2,
> > > -	.max_freq = 1000,
> > > -	.wait_for_reset = 1,
> > > -	.num_bits_resol = 11,
> > > -	.reg_values = reg_values,
> > > -	.host_ops = &exynos_dsi_host_ops,
> > > -};
> > > -
> > > -static const struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
> > > -	.reg_ofs = EXYNOS_REG_OFS,
> > > -	.plltmr_reg = 0x58,
> > > -	.num_clks = 2,
> > > -	.max_freq = 1000,
> > > -	.wait_for_reset = 1,
> > > -	.num_bits_resol = 11,
> > > -	.reg_values = reg_values,
> > > -	.host_ops = &exynos_dsi_host_ops,
> > > -};
> > > -
> > > -static const struct exynos_dsi_driver_data exynos5433_dsi_driver_data = {
> > > -	.reg_ofs = EXYNOS5433_REG_OFS,
> > > -	.plltmr_reg = 0xa0,
> > > -	.has_clklane_stop = 1,
> > > -	.num_clks = 5,
> > > -	.max_freq = 1500,
> > > -	.wait_for_reset = 0,
> > > -	.num_bits_resol = 12,
> > > -	.reg_values = exynos5433_reg_values,
> > > -	.host_ops = &exynos_dsi_host_ops,
> > > -};
> > > -
> > > -static const struct exynos_dsi_driver_data exynos5422_dsi_driver_data = {
> > > -	.reg_ofs = EXYNOS5433_REG_OFS,
> > > -	.plltmr_reg = 0xa0,
> > > -	.has_clklane_stop = 1,
> > > -	.num_clks = 2,
> > > -	.max_freq = 1500,
> > > -	.wait_for_reset = 1,
> > > -	.num_bits_resol = 12,
> > > -	.reg_values = exynos5422_reg_values,
> > > -	.host_ops = &exynos_dsi_host_ops,
> > > -};
> > > -
> > > -static const struct of_device_id exynos_dsi_of_match[] = {
> > > -	{ .compatible = "samsung,exynos3250-mipi-dsi",
> > > -	  .data = &exynos3_dsi_driver_data },
> > > -	{ .compatible = "samsung,exynos4210-mipi-dsi",
> > > -	  .data = &exynos4_dsi_driver_data },
> > > -	{ .compatible = "samsung,exynos5410-mipi-dsi",
> > > -	  .data = &exynos5_dsi_driver_data },
> > > -	{ .compatible = "samsung,exynos5422-mipi-dsi",
> > > -	  .data = &exynos5422_dsi_driver_data },
> > > -	{ .compatible = "samsung,exynos5433-mipi-dsi",
> > > -	  .data = &exynos5433_dsi_driver_data },
> > > -	{ }
> > > -};
> > > -
> > > -static int exynos_dsi_pltfm_bind(struct device *dev, struct device *master, void *data)
> > > -{
> > > -	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
> > > -	struct drm_encoder *encoder = &dsi->encoder;
> > > -	struct drm_device *drm_dev = data;
> > > -	struct device_node *in_bridge_node;
> > > -	struct drm_bridge *in_bridge;
> > > -	int ret;
> > > -
> > > -	drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_TMDS);
> > > -
> > > -	ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_LCD);
> > > -	if (ret < 0)
> > > -		return ret;
> > > -
> > > -	in_bridge_node = of_graph_get_remote_node(dev->of_node, DSI_PORT_IN, 0);
> > > -	if (in_bridge_node) {
> > > -		in_bridge = of_drm_find_bridge(in_bridge_node);
> > > -		if (in_bridge)
> > > -			drm_bridge_attach(encoder, in_bridge, NULL, 0);
> > > -		of_node_put(in_bridge_node);
> > > -	}
> > > -
> > > -	ret = exynos_dsi_bind(dsi->dsi, encoder);
> > > -	if (ret)
> > > -		goto err;
> > > -
> > > -	return 0;
> > > -
> > > -err:
> > > -	drm_encoder_cleanup(encoder);
> > > -	return ret;
> > > -}
> > > -
> > > -static void exynos_dsi_pltfm_unbind(struct device *dev, struct device *master,
> > > -				    void *data)
> > > -{
> > > -	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
> > > -	struct drm_encoder *encoder = &dsi->encoder;
> > > -
> > > -	exynos_dsi_unbind(dsi->dsi);
> > > -
> > > -	drm_encoder_cleanup(encoder);
> > > -}
> > > -
> > > -static const struct component_ops exynos_dsi_pltfm_component_ops = {
> > > -	.bind	= exynos_dsi_pltfm_bind,
> > > -	.unbind	= exynos_dsi_pltfm_unbind,
> > > -};
> > > -
> > > -static int exynos_dsi_pltfm_probe(struct platform_device *pdev)
> > > -{
> > > -	struct exynos_dsi_pltfm *dsi;
> > > -	struct device *dev = &pdev->dev;
> > > -	int ret;
> > > -
> > > -	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
> > > -	if (!dsi)
> > > -		return -ENOMEM;
> > > -	platform_set_drvdata(pdev, dsi);
> > > -
> > > -	dsi->dsi = exynos_dsi_probe(pdev);
> > > -	if (IS_ERR(dsi->dsi))
> > > -		return PTR_ERR(dsi->dsi);
> > > -
> > > -	pm_runtime_enable(dev);
> > > -
> > > -	ret = component_add(dev, &exynos_dsi_pltfm_component_ops);
> > > -	if (ret)
> > > -		goto err_disable_runtime;
> > > -
> > > -	return 0;
> > > -
> > > -err_disable_runtime:
> > > -	pm_runtime_disable(dev);
> > > -
> > > -	return ret;
> > > -}
> > > -
> > > -static int exynos_dsi_pltfm_remove(struct platform_device *pdev)
> > > -{
> > > -	struct exynos_dsi_pltfm *dsi = platform_get_drvdata(pdev);
> > > -
> > > -	pm_runtime_disable(&pdev->dev);
> > > -
> > > -	exynos_dsi_remove(dsi->dsi);
> > > -
> > > -	component_del(&pdev->dev, &exynos_dsi_pltfm_component_ops);
> > > -
> > > -	return 0;
> > > -}
> > > -
> > > -static int __maybe_unused exynos_dsi_pltfm_suspend(struct device *dev)
> > > -{
> > > -	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
> > > -
> > > -	return exynos_dsi_suspend(dsi->dsi);
> > > -}
> > > -
> > > -static int __maybe_unused exynos_dsi_pltfm_resume(struct device *dev)
> > > -{
> > > -	struct exynos_dsi_pltfm *dsi = dev_get_drvdata(dev);
> > > -
> > > -	return exynos_dsi_resume(dsi->dsi);
> > > -}
> > > -
> > > -static const struct dev_pm_ops exynos_dsi_pm_ops = {
> > > -	SET_RUNTIME_PM_OPS(exynos_dsi_pltfm_suspend, exynos_dsi_pltfm_resume, NULL)
> > > -	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> > > -				pm_runtime_force_resume)
> > > -};
> > > -
> > > -struct platform_driver dsi_driver = {
> > > -	.probe = exynos_dsi_pltfm_probe,
> > > -	.remove = exynos_dsi_pltfm_remove,
> > > -	.driver = {
> > > -		   .name = "exynos-dsi",
> > > -		   .owner = THIS_MODULE,
> > > -		   .pm = &exynos_dsi_pm_ops,
> > > -		   .of_match_table = exynos_dsi_of_match,
> > > -	},
> > > -};
> > > -
> > > -MODULE_AUTHOR("Tomasz Figa <t.figa@samsung.com>");
> > > -MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
> > > -MODULE_DESCRIPTION("Samsung SoC MIPI DSI Master");
> > > -MODULE_LICENSE("GPL v2");
> > > diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.h b/include/drm/bridge/samsung-dsim.h
> > > similarity index 69%
> > > rename from drivers/gpu/drm/exynos/exynos_drm_dsi.h
> > > rename to include/drm/bridge/samsung-dsim.h
> > > index 8fa3276889de..be8b4913aa9c 100644
> > > --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.h
> > > +++ b/include/drm/bridge/samsung-dsim.h
> > > @@ -3,7 +3,7 @@
> > >  #define __EXYNOS_DRM_DSI__
> > >  
> > >  struct drm_encoder;
> > > -struct exynos_dsi;
> > > +struct samsung_dsim;
> > >  struct platform_device;
> > >  struct mipi_dsi_device;
> > >  
> > > @@ -12,13 +12,13 @@ enum exynos_reg_offset {
> > >  	EXYNOS5433_REG_OFS
> > >  };
> > >  
> > > -struct exynos_dsi *exynos_dsi_probe(struct platform_device *pdev);
> > > -void exynos_dsi_remove(struct exynos_dsi *dsi);
> > > -int exynos_dsi_bind(struct exynos_dsi *dsi, struct drm_encoder *encoder);
> > > -void exynos_dsi_unbind(struct exynos_dsi *dsi);
> > > +struct samsung_dsim *samsung_dsim_probe(struct platform_device *pdev);
> > > +void samsung_dsim_remove(struct samsung_dsim *dsi);
> > > +int samsung_dsim_bind(struct samsung_dsim *dsi, struct drm_encoder *encoder);
> > > +void samsung_dsim_unbind(struct samsung_dsim *dsi);
> > >  
> > > -int exynos_dsi_suspend(struct exynos_dsi *dsi);
> > > -int exynos_dsi_resume(struct exynos_dsi *dsi);
> > > +int samsung_dsim_suspend(struct samsung_dsim *dsi);
> > > +int samsung_dsim_resume(struct samsung_dsim *dsi);
> > >  
> > >  enum reg_value_idx {
> > >  	RESET_TYPE,
> > > @@ -42,13 +42,13 @@ enum reg_value_idx {
> > >  #define DSIM_FUNCRST			(1 << 16)
> > >  #define DSIM_SWRST			(1 << 0)
> > >  
> > > -struct exynos_dsi_host_ops {
> > > +struct samsung_dsim_host_ops {
> > >  	int (*attach)(struct device *dev, struct mipi_dsi_device *device);
> > >  	int (*detach)(struct device *dev, struct mipi_dsi_device *device);
> > >  	void (*te_handler)(struct device *dev);
> > >  };
> > >  
> > > -struct exynos_dsi_driver_data {
> > > +struct samsung_dsim_driver_data {
> > >  	enum exynos_reg_offset reg_ofs;
> > >  	unsigned int plltmr_reg;
> > >  	unsigned int has_freqband:1;
> > > @@ -58,7 +58,7 @@ struct exynos_dsi_driver_data {
> > >  	unsigned int wait_for_reset;
> > >  	unsigned int num_bits_resol;
> > >  	const unsigned int *reg_values;
> > > -	const struct exynos_dsi_host_ops *host_ops;
> > > +	const struct samsung_dsim_host_ops *host_ops;
> > >  };
> > >  
> > >  #endif /* __EXYNOS_DRM_DSI__ */
> > > -- 
> > > 2.20.1
> > > 
> > > _______________________________________________
> > > dri-devel mailing list
> > > dri-devel@lists.freedesktop.org
> > > https://lists.freedesktop.org/mailman/listinfo/dri-devel
> > 
> > -- 
> > Daniel Vetter
> > Software Engineer, Intel Corporation
> > http://blog.ffwll.ch
> > 
> 
> -- 
> Pengutronix e.K.                           | Michael Tretter             |
> Steuerwalder Str. 21                       | https://www.pengutronix.de/ |
> 31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
> Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 08/16] drm/exynos: add host_ops callback for platform drivers
  2020-09-15 18:02       ` Michael Tretter
@ 2020-09-16 22:01         ` Andrzej Hajda
  0 siblings, 0 replies; 61+ messages in thread
From: Andrzej Hajda @ 2020-09-16 22:01 UTC (permalink / raw)
  To: Michael Tretter
  Cc: linux-samsung-soc, jy0922.shim, b.zolnierkie, narmstrong,
	sw0312.kim, dri-devel, krzk, kernel, sylvester.nawrocki,
	Laurent.pinchart


W dniu 15.09.2020 o 20:02, Michael Tretter pisze:
> On Tue, 15 Sep 2020 19:07:59 +0200, Andrzej Hajda wrote:
>> W dniu 11.09.2020 o 15:54, Michael Tretter pisze:
>>> Platform drivers need to be aware if a mipi dsi device attaches or
>>> detaches. Add host_ops to the driver_data for attach and detach
>>> callbacks and move the i80 mode selection and the hotplug handling into
>>> the callback, because these depend on the drm driver.
>>>
>>> Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
>>> ---
>>> v2:
>>> - new patch
>>> ---
>>>    drivers/gpu/drm/exynos/exynos_drm_dsi.c | 64 ++++++++++++++++++++-----
>>>    1 file changed, 53 insertions(+), 11 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
>>> index 1a15ae71205d..684a2fbef08a 100644
>>> --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
>>> +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
>>> @@ -239,6 +239,12 @@ struct exynos_dsi_transfer {
>>>    #define DSIM_STATE_CMD_LPM		BIT(2)
>>>    #define DSIM_STATE_VIDOUT_AVAILABLE	BIT(3)
>>>    
>>> +struct exynos_dsi;
>>> +struct exynos_dsi_host_ops {
>>> +	int (*attach)(struct device *dev, struct mipi_dsi_device *device);
>>> +	int (*detach)(struct device *dev, struct mipi_dsi_device *device);
>>> +};
>>> +
>>>    enum exynos_reg_offset {
>>>    	EXYNOS_REG_OFS,
>>>    	EXYNOS5433_REG_OFS
>>> @@ -254,6 +260,7 @@ struct exynos_dsi_driver_data {
>>>    	unsigned int wait_for_reset;
>>>    	unsigned int num_bits_resol;
>>>    	const unsigned int *reg_values;
>>> +	const struct exynos_dsi_host_ops *host_ops;
>>>    };
>>>    
>>>    struct exynos_dsi {
>>> @@ -467,6 +474,41 @@ static const unsigned int exynos5433_reg_values[] = {
>>>    	[PHYTIMING_HS_TRAIL] = 0x0c,
>>>    };
>>>    
>>> +static int __exynos_dsi_host_attach(struct device *dev,
>>> +				    struct mipi_dsi_device *device)
>>> +{
>>> +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
>>> +	struct drm_device *drm = dsi->encoder.dev;
>>
>> As I wrote yesterday drm device was guaranteed to be present here only
>> if mipi dsi host registration was performed in component bind. In case
>> it is performed in probe it will be always NULL, and the code does not
>> make sense.
> Correct, but if the driver is used as a drm bridge, there won't be an encoder
> until bridge_attach. Mipi dsi devices defer their probe until the mipi dsi
> host is available. If I move the mipi dsi host registration into
> bridge_attach, the driver does not know if the next device is another bridge
> or a panel during bridge_attach, but the driver cannot successfully return
> from bridge_attach, because then the drm driver expects a connector.


Hmm, I am not sure why do you think driver expects connector on return 
of briddge_attach.


>
> I guess that the encoder should be initialized before registering the mipi dsi
> host during probe instead of bind.


But you should have already drm dev on encoder init which in probe is 
unavailable.



> The probe won't be rolled back on
> PROBE_DEFER during bind and the encoder will be available in host_attach.
> When splitting the driver into the exynos platform specific code and the more
> generic code, there won't be an encoder during host_attach in the generic
> code, but the host_ops callback could (and will) use the platform specific
> encoder, which is available before bridge_attach.
>
> Does this make sense to you?


Nope :) But maybe I am missing sth.

Generally I see two ways (which I have already described in different 
e-mail, in different words):

A. Static - we wait for every part of display stack to be probed, then 
create drm_dev - typical approach, but slow (deferred probe causes late 
drm creation), and racy - only(?) component framework and DSI bus have 
possibility to signal driver unbind, so we can react on it properly.

B. Dynamic - drm framework requires only crtcs and encoders to be 
attached to drm on init, connectors, and hidden parts (drm_bridges, 
drm_panels) can be created/destroyed and attached/detached at any time 
(almost), so lets take advantage of it - create drm_dev ASAP and attach 
other parts when they become available, the only issue is that there is 
no generic way to be notified when given parts becomes available - in 
interesting area only mipi devices have such notifications via attach 
callbacks.


So either we convert exynos_dsi to A either we continue B approach. In 
second case we should assure mipi_dsi host is created if drm_dev is 
available.


Regards

Andrzej



>
> Michael
>
>>
>> Regards
>>
>> Andrzej
>>
>>
>>
>>> +	struct exynos_drm_crtc *crtc;
>>> +
>>> +	mutex_lock(&drm->mode_config.mutex);
>>> +	crtc = exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD);
>>> +	crtc->i80_mode = !(device->mode_flags & MIPI_DSI_MODE_VIDEO);
>>> +	mutex_unlock(&drm->mode_config.mutex);
>>> +
>>> +	if (drm->mode_config.poll_enabled)
>>> +		drm_kms_helper_hotplug_event(drm);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int __exynos_dsi_host_detach(struct device *dev,
>>> +				     struct mipi_dsi_device *device)
>>> +{
>>> +	struct exynos_dsi *dsi = dev_get_drvdata(dev);
>>> +	struct drm_device *drm = dsi->encoder.dev;
>>> +
>>> +	if (drm->mode_config.poll_enabled)
>>> +		drm_kms_helper_hotplug_event(drm);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static const struct exynos_dsi_host_ops exynos_dsi_host_ops = {
>>> +	.attach = __exynos_dsi_host_attach,
>>> +	.detach = __exynos_dsi_host_detach,
>>> +};
>>> +
>>>    static const struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
>>>    	.reg_ofs = EXYNOS_REG_OFS,
>>>    	.plltmr_reg = 0x50,
>>> @@ -477,6 +519,7 @@ static const struct exynos_dsi_driver_data exynos3_dsi_driver_data = {
>>>    	.wait_for_reset = 1,
>>>    	.num_bits_resol = 11,
>>>    	.reg_values = reg_values,
>>> +	.host_ops = &exynos_dsi_host_ops,
>>>    };
>>>    
>>>    static const struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
>>> @@ -489,6 +532,7 @@ static const struct exynos_dsi_driver_data exynos4_dsi_driver_data = {
>>>    	.wait_for_reset = 1,
>>>    	.num_bits_resol = 11,
>>>    	.reg_values = reg_values,
>>> +	.host_ops = &exynos_dsi_host_ops,
>>>    };
>>>    
>>>    static const struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
>>> @@ -499,6 +543,7 @@ static const struct exynos_dsi_driver_data exynos5_dsi_driver_data = {
>>>    	.wait_for_reset = 1,
>>>    	.num_bits_resol = 11,
>>>    	.reg_values = reg_values,
>>> +	.host_ops = &exynos_dsi_host_ops,
>>>    };
>>>    
>>>    static const struct exynos_dsi_driver_data exynos5433_dsi_driver_data = {
>>> @@ -510,6 +555,7 @@ static const struct exynos_dsi_driver_data exynos5433_dsi_driver_data = {
>>>    	.wait_for_reset = 0,
>>>    	.num_bits_resol = 12,
>>>    	.reg_values = exynos5433_reg_values,
>>> +	.host_ops = &exynos_dsi_host_ops,
>>>    };
>>>    
>>>    static const struct exynos_dsi_driver_data exynos5422_dsi_driver_data = {
>>> @@ -521,6 +567,7 @@ static const struct exynos_dsi_driver_data exynos5422_dsi_driver_data = {
>>>    	.wait_for_reset = 1,
>>>    	.num_bits_resol = 12,
>>>    	.reg_values = exynos5422_reg_values,
>>> +	.host_ops = &exynos_dsi_host_ops,
>>>    };
>>>    
>>>    static const struct of_device_id exynos_dsi_of_match[] = {
>>> @@ -1551,8 +1598,8 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
>>>    				  struct mipi_dsi_device *device)
>>>    {
>>>    	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_device *drm = encoder->dev;
>>>    	struct drm_bridge *out_bridge;
>>>    
>>>    	out_bridge  = of_drm_find_bridge(device->dev.of_node);
>>> @@ -1590,18 +1637,12 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
>>>    			return ret;
>>>    	}
>>>    
>>> -	mutex_lock(&drm->mode_config.mutex);
>>> -
>>>    	dsi->lanes = device->lanes;
>>>    	dsi->format = device->format;
>>>    	dsi->mode_flags = device->mode_flags;
>>> -	exynos_drm_crtc_get_by_type(drm, EXYNOS_DISPLAY_TYPE_LCD)->i80_mode =
>>> -			!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO);
>>>    
>>> -	mutex_unlock(&drm->mode_config.mutex);
>>> -
>>> -	if (drm->mode_config.poll_enabled)
>>> -		drm_kms_helper_hotplug_event(drm);
>>> +	if (ops && ops->attach)
>>> +		ops->attach(dsi->dsi_host.dev, device);
>>>    
>>>    	return 0;
>>>    }
>>> @@ -1610,6 +1651,7 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
>>>    				  struct mipi_dsi_device *device)
>>>    {
>>>    	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) {
>>> @@ -1625,8 +1667,8 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
>>>    		INIT_LIST_HEAD(&dsi->bridge_chain);
>>>    	}
>>>    
>>> -	if (drm->mode_config.poll_enabled)
>>> -		drm_kms_helper_hotplug_event(drm);
>>> +	if (ops && ops->detach)
>>> +		ops->detach(dsi->dsi_host.dev, device);
>>>    
>>>    	exynos_dsi_unregister_te_irq(dsi);
>>>    
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://protect2.fireeye.com/v1/url?k=80c78954-dd15920c-80c6021b-0cc47a31c8b4-c39fad41cb70b194&q=1&e=b51d6682-ba72-48c0-b0c9-013866ba39ab&u=https%3A%2F%2Flists.freedesktop.org%2Fmailman%2Flistinfo%2Fdri-devel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 01/16] drm/encoder: remove obsolete documentation of bridge
  2020-09-11 13:53   ` [PATCH v2 01/16] drm/encoder: remove obsolete documentation of bridge Michael Tretter
@ 2020-11-07 15:07     ` Adam Ford
  2020-11-10  8:46       ` Michael Tretter
  2020-11-07 22:17     ` Sam Ravnborg
  1 sibling, 1 reply; 61+ messages in thread
From: Adam Ford @ 2020-11-07 15:07 UTC (permalink / raw)
  To: Michael Tretter
  Cc: linux-samsung-soc, jy0922.shim, Neil Armstrong,
	Bartlomiej Zolnierkiewicz, sw0312.kim, dri-devel, a.hajda,
	Krzysztof Kozlowski, Sascha Hauer, sylvester.nawrocki,
	Laurent Pinchart

On Fri, Sep 11, 2020 at 8:54 AM Michael Tretter
<m.tretter@pengutronix.de> wrote:
>
> In commit 05193dc38197 ("drm/bridge: Make the bridge chain a
> double-linked list") the bridge has been removed and replaced by a
> private field. Remove the leftover documentation of the removed field.
>
> Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

What is the status of this series?  I know of at least one other patch
series depending on this.

adam

> ---
> v2: none
> ---
>  include/drm/drm_encoder.h | 1 -
>  1 file changed, 1 deletion(-)
>
> diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h
> index a60f5f1555ac..5dfa5f7a80a7 100644
> --- a/include/drm/drm_encoder.h
> +++ b/include/drm/drm_encoder.h
> @@ -89,7 +89,6 @@ struct drm_encoder_funcs {
>   * @head: list management
>   * @base: base KMS object
>   * @name: human readable name, can be overwritten by the driver
> - * @bridge: bridge associated to the encoder
>   * @funcs: control functions
>   * @helper_private: mid-layer private data
>   *
> --
> 2.20.1
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 01/16] drm/encoder: remove obsolete documentation of bridge
  2020-09-11 13:53   ` [PATCH v2 01/16] drm/encoder: remove obsolete documentation of bridge Michael Tretter
  2020-11-07 15:07     ` Adam Ford
@ 2020-11-07 22:17     ` Sam Ravnborg
  1 sibling, 0 replies; 61+ messages in thread
From: Sam Ravnborg @ 2020-11-07 22:17 UTC (permalink / raw)
  To: Michael Tretter
  Cc: linux-samsung-soc, jy0922.shim, narmstrong, b.zolnierkie,
	sw0312.kim, dri-devel, a.hajda, krzk, kernel, sylvester.nawrocki,
	Laurent Pinchart

On Fri, Sep 11, 2020 at 03:53:58PM +0200, Michael Tretter wrote:
> In commit 05193dc38197 ("drm/bridge: Make the bridge chain a
> double-linked list") the bridge has been removed and replaced by a
> private field. Remove the leftover documentation of the removed field.
> 
> Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

Hi Michael.

Applied to drm-misc-next, thanks.
The rest of this patch-set is exynos stuff, that the maintainer needs to
deal with but I will try to take a look at some of the patches.

	Sam
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 02/16] drm/exynos: remove in_bridge_node from exynos_dsi
  2020-09-11 13:53   ` [PATCH v2 02/16] drm/exynos: remove in_bridge_node from exynos_dsi Michael Tretter
@ 2020-11-07 22:19     ` Sam Ravnborg
  2020-11-09  2:24       ` Inki Dae
  0 siblings, 1 reply; 61+ messages in thread
From: Sam Ravnborg @ 2020-11-07 22:19 UTC (permalink / raw)
  To: Michael Tretter
  Cc: linux-samsung-soc, jy0922.shim, narmstrong, b.zolnierkie,
	sw0312.kim, dri-devel, a.hajda, krzk, kernel, sylvester.nawrocki,
	Laurent.pinchart

On Fri, Sep 11, 2020 at 03:53:59PM +0200, Michael Tretter wrote:
> We do not need to keep a reference to the in_bridge_node, but we can
> simply drop it, once we found and attached the previous bridge.
> 
> Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>

Note: I expect exynos people to pick it up.

	Sam
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 03/16] drm/exynos: use exynos_dsi as drvdata
  2020-09-11 13:54   ` [PATCH v2 03/16] drm/exynos: use exynos_dsi as drvdata Michael Tretter
@ 2020-11-07 22:24     ` Sam Ravnborg
  2020-11-09  2:24       ` Inki Dae
  0 siblings, 1 reply; 61+ messages in thread
From: Sam Ravnborg @ 2020-11-07 22:24 UTC (permalink / raw)
  To: Michael Tretter
  Cc: linux-samsung-soc, jy0922.shim, narmstrong, b.zolnierkie,
	sw0312.kim, dri-devel, a.hajda, krzk, kernel, sylvester.nawrocki,
	Laurent.pinchart

On Fri, Sep 11, 2020 at 03:54:00PM +0200, Michael Tretter wrote:
> Use the exynos_dsi as drvdata instead of the encoder to further decouple
> the driver from the encoder.
> 
> Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>

Likewise, the exynos people are expected to pick this up.

	Sam
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 04/16] drm/exynos: extract helper functions for probe
  2020-09-11 13:54   ` [PATCH v2 04/16] drm/exynos: extract helper functions for probe Michael Tretter
@ 2020-11-07 22:27     ` Sam Ravnborg
  2020-11-09  2:52       ` Inki Dae
  0 siblings, 1 reply; 61+ messages in thread
From: Sam Ravnborg @ 2020-11-07 22:27 UTC (permalink / raw)
  To: Michael Tretter
  Cc: linux-samsung-soc, jy0922.shim, narmstrong, b.zolnierkie,
	sw0312.kim, dri-devel, a.hajda, krzk, kernel, sylvester.nawrocki,
	Laurent.pinchart

On Fri, Sep 11, 2020 at 03:54:01PM +0200, Michael Tretter wrote:
> As the driver shall be usable with drivers that use the component
> framework and drivers that don't, split the common probing code into a
> separate function that can be called from different locations.
> 
> Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 06/16] drm/exynos: shift register values to fields on write
  2020-09-11 13:54   ` [PATCH v2 06/16] drm/exynos: shift register values to fields on write Michael Tretter
@ 2020-11-07 22:39     ` Sam Ravnborg
  2020-11-10  8:28       ` Michael Tretter
  0 siblings, 1 reply; 61+ messages in thread
From: Sam Ravnborg @ 2020-11-07 22:39 UTC (permalink / raw)
  To: Michael Tretter
  Cc: linux-samsung-soc, jy0922.shim, narmstrong, Adrian Ratiu,
	b.zolnierkie, sw0312.kim, dri-devel, a.hajda, krzk, kernel,
	sylvester.nawrocki, Laurent.pinchart

Hi Michael.

On Fri, Sep 11, 2020 at 03:54:03PM +0200, Michael Tretter wrote:
> The phy timings are already shifted to the field position. If the driver
> is reused on multiple platforms, this exposes the field positions to the
> platform code.
> 
> Store only the timing values in the platform data and shift the value to
> the field when writing the fields to the registers.
> 
> Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>

This and the following patch smells like the regmap functionality is
partly open coded. regmaps supports defining different register layouts
and select the correct layout at runtime.

See for example:
https://www.collabora.com/news-and-blog/blog/2020/05/27/using-regmaps-to-make-linux-drivers-more-generic/
or
https://www.youtube.com/watch?v=0RPDGANArFc

Some parts is not a perfect fit - but using regmaps will make it better
as a general and well-known solution is used.

@Adrian - see https://lore.kernel.org/dri-devel/20200911135413.3654800-1-m.tretter@pengutronix.de/T/#m8e211c8cce915168cf2b8c4eef1c7ec9b8447af8
for the original patch.

	Sam
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 02/16] drm/exynos: remove in_bridge_node from exynos_dsi
  2020-11-07 22:19     ` Sam Ravnborg
@ 2020-11-09  2:24       ` Inki Dae
  0 siblings, 0 replies; 61+ messages in thread
From: Inki Dae @ 2020-11-09  2:24 UTC (permalink / raw)
  To: dri-devel



20. 11. 8. 오전 7:19에 Sam Ravnborg 이(가) 쓴 글:
> On Fri, Sep 11, 2020 at 03:53:59PM +0200, Michael Tretter wrote:
>> We do not need to keep a reference to the in_bridge_node, but we can
>> simply drop it, once we found and attached the previous bridge.
>>
>> Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
> Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
> 
> Note: I expect exynos people to pick it up.

Cleanup patch so picked it up.

Thanks,
Inki Dae

> 
> 	Sam
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://protect2.fireeye.com/v1/url?k=0ba8a77f-54339e66-0ba92c30-000babff3793-ff3020eaa6c16149&q=1&e=eadf6490-86ef-461e-b39a-892e3aec0194&u=https%3A%2F%2Flists.freedesktop.org%2Fmailman%2Flistinfo%2Fdri-devel
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 03/16] drm/exynos: use exynos_dsi as drvdata
  2020-11-07 22:24     ` Sam Ravnborg
@ 2020-11-09  2:24       ` Inki Dae
  0 siblings, 0 replies; 61+ messages in thread
From: Inki Dae @ 2020-11-09  2:24 UTC (permalink / raw)
  To: Sam Ravnborg, Michael Tretter
  Cc: linux-samsung-soc, jy0922.shim, narmstrong, b.zolnierkie,
	sw0312.kim, dri-devel, a.hajda, krzk, kernel, sylvester.nawrocki,
	Laurent.pinchart



20. 11. 8. 오전 7:24에 Sam Ravnborg 이(가) 쓴 글:
> On Fri, Sep 11, 2020 at 03:54:00PM +0200, Michael Tretter wrote:
>> Use the exynos_dsi as drvdata instead of the encoder to further decouple
>> the driver from the encoder.
>>
>> Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
> Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
> 
> Likewise, the exynos people are expected to pick this up.

Cleanup patch so picked it up.

Thanks,
Inki Dae


> 
> 	Sam
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 04/16] drm/exynos: extract helper functions for probe
  2020-11-07 22:27     ` Sam Ravnborg
@ 2020-11-09  2:52       ` Inki Dae
  0 siblings, 0 replies; 61+ messages in thread
From: Inki Dae @ 2020-11-09  2:52 UTC (permalink / raw)
  To: Sam Ravnborg, Michael Tretter
  Cc: linux-samsung-soc, jy0922.shim, narmstrong, b.zolnierkie,
	sw0312.kim, dri-devel, a.hajda, krzk, kernel, sylvester.nawrocki,
	Laurent.pinchart



20. 11. 8. 오전 7:27에 Sam Ravnborg 이(가) 쓴 글:
> On Fri, Sep 11, 2020 at 03:54:01PM +0200, Michael Tretter wrote:
>> As the driver shall be usable with drivers that use the component
>> framework and drivers that don't, split the common probing code into a
>> separate function that can be called from different locations.
>>
>> Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
> Reviewed-by: Sam Ravnborg <sam@ravnborg.org>

This patch and other are related to what this patch series do. However, this patch makes Exynos board not working yet.
So excepting patches 2 to 3, I will wait for that this patch series will be fixed and reviewed more.

Thanks,
Inki Dae

> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge
  2020-09-11 13:53 ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Michael Tretter
                     ` (15 preceding siblings ...)
  2020-09-11 13:54   ` [PATCH v2 16/16] drm/exynos: move bridge driver to bridges Michael Tretter
@ 2020-11-09  3:15   ` Inki Dae
  2020-11-10  8:13     ` Michael Tretter
  16 siblings, 1 reply; 61+ messages in thread
From: Inki Dae @ 2020-11-09  3:15 UTC (permalink / raw)
  To: Michael Tretter, dri-devel, linux-samsung-soc
  Cc: jy0922.shim, b.zolnierkie, narmstrong, sw0312.kim, krzk, a.hajda,
	Laurent.pinchart, kernel, sylvester.nawrocki

Hi Michael,

Thanks for your contribution.

20. 9. 11. 오후 10:53에 Michael Tretter 이(가) 쓴 글:
> This is v2 of the series to convert the Exynos MIPI DSI driver into a drm
> bridge and make it usable with other drivers. Although the driver is
> converted, it still supports the component framework API to stay compliant
> with the Exynos DRM driver.
> 
> The Exynos MIPI DSI Phy is also found on the i.MX8M Mini. However, on the
> i.MX8M Mini, the bridge is driven by an LCDIF display controller instead of
> the Exynos Decon. The driver for the LCDIF does not use the component
> framework, but uses drm bridges.
> 
> I don't have any Exynos SoC to actually test the series. I build a dummy to
> test the bridge with a component driver, to make sure that at least the
> initialization is working. Furthermore, tested the driver as a bridge with a
> few additional unfinished patches on the i.MX8M Mini EVK. However, somebody
> should verify that the driver is still working on Exynos hardware.
> 
> I also changed the order of the patches to first make the driver more platform
> independent (patches 2 to 8), then convert to a drm bridge driver (patches 10

Just a fundamental question,
A MIPI-DSI(Display Serial Interface) bus device would be one of an encoder type of devices not bridge such as DSI to LVDS and LVDS to DSI bridge devices, and also image enhancer and image compressor in case of Exynos.
Why do you want to convert such MIPI-DSI driver to bridge type of driver? Seems not sensible. The reason would be just to share MIPI-DSI phy driver for Exynos with i.MX8M Mini?

Thanks,
Inki Dae


> to 13) and finally expose the API, split the code and move the platform
> independent driver to the bridges (patches 14 - 16). Hopefully this simplifies
> testing/bisecting and helps me to understand potential error reports.
> 
> Also I added host_ops for attach/detach and the tearing effect handler to make
> the calls into the platform code more visible.
> 
> Furthermore, the series should now apply to linux-next and correctly build the
> exynos_defconfig.
> 
> Thanks,
> 
> Michael
> 
> Changelog:
> 
> v2:
> - rebase on linux-next
> - verify with exynos_defconfig
> - fix crashes reported by Marek Szyprowski Exynos3250-based Rinato
> - reorder patches
> - add host_ops for platform specific code
> - roughly test component framework integration with dummy
> 
> Michael Tretter (16):
>   drm/encoder: remove obsolete documentation of bridge
>   drm/exynos: remove in_bridge_node from exynos_dsi
>   drm/exynos: use exynos_dsi as drvdata
>   drm/exynos: extract helper functions for probe
>   drm/exynos: move dsi host registration to probe
>   drm/exynos: shift register values to fields on write
>   drm/exynos: use identifier instead of register offsets
>   drm/exynos: add host_ops callback for platform drivers
>   drm/exynos: add callback for tearing effect handler
>   drm/exynos: implement a drm bridge
>   drm/exynos: convert encoder functions to bridge function
>   drm/exynos: configure mode on drm bridge
>   drm/exynos: get encoder from bridge whenever possible
>   drm/exynos: add API functions for platform drivers
>   drm/exynos: split out platform specific code
>   drm/exynos: move bridge driver to bridges
> 
>  drivers/gpu/drm/bridge/Kconfig          |    9 +
>  drivers/gpu/drm/bridge/Makefile         |    1 +
>  drivers/gpu/drm/bridge/samsung-dsim.c   | 1790 +++++++++++++++++++++
>  drivers/gpu/drm/exynos/Kconfig          |    5 +-
>  drivers/gpu/drm/exynos/exynos_drm_dsi.c | 1927 ++---------------------
>  include/drm/bridge/samsung-dsim.h       |   64 +
>  include/drm/drm_encoder.h               |    1 -
>  7 files changed, 2027 insertions(+), 1770 deletions(-)
>  create mode 100644 drivers/gpu/drm/bridge/samsung-dsim.c
>  create mode 100644 include/drm/bridge/samsung-dsim.h
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge
  2020-11-09  3:15   ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Inki Dae
@ 2020-11-10  8:13     ` Michael Tretter
  2020-11-10 12:34       ` Marek Szyprowski
  2020-11-11  3:04       ` Inki Dae
  0 siblings, 2 replies; 61+ messages in thread
From: Michael Tretter @ 2020-11-10  8:13 UTC (permalink / raw)
  To: Inki Dae
  Cc: linux-samsung-soc, jy0922.shim, narmstrong, b.zolnierkie,
	sw0312.kim, krzk, a.hajda, dri-devel, kernel, sylvester.nawrocki,
	Laurent.pinchart

On Mon, 09 Nov 2020 12:15:39 +0900, Inki Dae wrote:
> 20. 9. 11. 오후 10:53에 Michael Tretter 이(가) 쓴 글:
> > This is v2 of the series to convert the Exynos MIPI DSI driver into a drm
> > bridge and make it usable with other drivers. Although the driver is
> > converted, it still supports the component framework API to stay compliant
> > with the Exynos DRM driver.
> > 
> > The Exynos MIPI DSI Phy is also found on the i.MX8M Mini. However, on the
> > i.MX8M Mini, the bridge is driven by an LCDIF display controller instead of
> > the Exynos Decon. The driver for the LCDIF does not use the component
> > framework, but uses drm bridges.
> > 
> > I don't have any Exynos SoC to actually test the series. I build a dummy to
> > test the bridge with a component driver, to make sure that at least the
> > initialization is working. Furthermore, tested the driver as a bridge with a
> > few additional unfinished patches on the i.MX8M Mini EVK. However, somebody
> > should verify that the driver is still working on Exynos hardware.
> > 
> > I also changed the order of the patches to first make the driver more platform
> > independent (patches 2 to 8), then convert to a drm bridge driver (patches 10
> 
> Just a fundamental question, A MIPI-DSI(Display Serial Interface) bus device
> would be one of an encoder type of devices not bridge such as DSI to LVDS
> and LVDS to DSI bridge devices, and also image enhancer and image compressor
> in case of Exynos.

I don't understand, why the MIPI-DSI bus device would be an encoder type and
DSI to LVDS or MIPI-DSI to HDMI would be bridges. For example, the device tree
documentation for the DSIM states that the DSIM receives RGB video as input
and produces MIPI-DSI as output. Thus, the DSIM is basically a parallel RGB to
MIPI-DSI bridge and the encoder is the LCD controller that encodes the video
data as parallel RGB.

On the i.MX8MM, the LCDIF is already the encoder. On Exynos, the series
implements the encoder in the platform glue, but in the end the encoder can
probably be moved to the DECON.

> Why do you want to convert such MIPI-DSI driver to bridge type of driver?
> Seems not sensible. The reason would be just to share MIPI-DSI phy driver
> for Exynos with i.MX8M Mini?

Yes, the reason is that the driver should be shared between Exynos and
i.MX8MM. It is the same IP and I don't see a reason why we should introduce
another driver for the same IP if the driver works for both SoCs.

Michael
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 06/16] drm/exynos: shift register values to fields on write
  2020-11-07 22:39     ` Sam Ravnborg
@ 2020-11-10  8:28       ` Michael Tretter
  0 siblings, 0 replies; 61+ messages in thread
From: Michael Tretter @ 2020-11-10  8:28 UTC (permalink / raw)
  To: Sam Ravnborg
  Cc: linux-samsung-soc, jy0922.shim, narmstrong, Adrian Ratiu,
	b.zolnierkie, sw0312.kim, dri-devel, a.hajda, krzk, kernel,
	sylvester.nawrocki, Laurent.pinchart

On Sat, 07 Nov 2020 23:39:30 +0100, Sam Ravnborg wrote:
> On Fri, Sep 11, 2020 at 03:54:03PM +0200, Michael Tretter wrote:
> > The phy timings are already shifted to the field position. If the driver
> > is reused on multiple platforms, this exposes the field positions to the
> > platform code.
> > 
> > Store only the timing values in the platform data and shift the value to
> > the field when writing the fields to the registers.
> > 
> > Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
> 
> This and the following patch smells like the regmap functionality is
> partly open coded. regmaps supports defining different register layouts
> and select the correct layout at runtime.
> 
> See for example:
> https://www.collabora.com/news-and-blog/blog/2020/05/27/using-regmaps-to-make-linux-drivers-more-generic/
> or
> https://www.youtube.com/watch?v=0RPDGANArFc
> 
> Some parts is not a perfect fit - but using regmaps will make it better
> as a general and well-known solution is used.

I considered using regmaps, but there was something that didn't work out.
Unfortunately, I don't remember, what it actually was. Therefore, it is
probably best to use regmaps here.

Michael

> 
> @Adrian - see https://lore.kernel.org/dri-devel/20200911135413.3654800-1-m.tretter@pengutronix.de/T/#m8e211c8cce915168cf2b8c4eef1c7ec9b8447af8
> for the original patch.
> 
> 	Sam
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 01/16] drm/encoder: remove obsolete documentation of bridge
  2020-11-07 15:07     ` Adam Ford
@ 2020-11-10  8:46       ` Michael Tretter
  0 siblings, 0 replies; 61+ messages in thread
From: Michael Tretter @ 2020-11-10  8:46 UTC (permalink / raw)
  To: Adam Ford
  Cc: linux-samsung-soc, jy0922.shim, Neil Armstrong,
	Bartlomiej Zolnierkiewicz, sw0312.kim, dri-devel, a.hajda,
	Krzysztof Kozlowski, Sascha Hauer, sylvester.nawrocki,
	Laurent Pinchart

On Sat, 07 Nov 2020 09:07:19 -0600, Adam Ford wrote:
> On Fri, Sep 11, 2020 at 8:54 AM Michael Tretter
> <m.tretter@pengutronix.de> wrote:
> >
> > In commit 05193dc38197 ("drm/bridge: Make the bridge chain a
> > double-linked list") the bridge has been removed and replaced by a
> > private field. Remove the leftover documentation of the removed field.
> >
> > Signed-off-by: Michael Tretter <m.tretter@pengutronix.de>
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> 
> What is the status of this series?  I know of at least one other patch
> series depending on this.

Sorry for that. I lately didn't have time to work on the series.

There are two big open TODOs:

- How should this bridge react if there is no out bridge available, yet? This
  series implements a static approach by returning EPROBE_DEFER from
  bridge_attach if there isn't a next bridge, connector or display. Andrezej
  suggested a dynamic approach which allows to attach this bridge without a
  next bridge and dynamically add further bridges/panels. The latter approach
  didn't work with the mxsfb driver, but I didn't have time to look into this.

- The component framework stuff that allows to use the bridge with the Exynos
  driver should not go into the bridge driver, but stay in the platform part.

Michael

> 
> adam
> 
> > ---
> > v2: none
> > ---
> >  include/drm/drm_encoder.h | 1 -
> >  1 file changed, 1 deletion(-)
> >
> > diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h
> > index a60f5f1555ac..5dfa5f7a80a7 100644
> > --- a/include/drm/drm_encoder.h
> > +++ b/include/drm/drm_encoder.h
> > @@ -89,7 +89,6 @@ struct drm_encoder_funcs {
> >   * @head: list management
> >   * @base: base KMS object
> >   * @name: human readable name, can be overwritten by the driver
> > - * @bridge: bridge associated to the encoder
> >   * @funcs: control functions
> >   * @helper_private: mid-layer private data
> >   *
> > --
> > 2.20.1
> >
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge
  2020-11-10  8:13     ` Michael Tretter
@ 2020-11-10 12:34       ` Marek Szyprowski
  2020-11-11  3:04       ` Inki Dae
  1 sibling, 0 replies; 61+ messages in thread
From: Marek Szyprowski @ 2020-11-10 12:34 UTC (permalink / raw)
  To: Michael Tretter, Inki Dae
  Cc: linux-samsung-soc, jy0922.shim, narmstrong, b.zolnierkie,
	sw0312.kim, krzk, a.hajda, dri-devel, kernel, sylvester.nawrocki,
	Laurent.pinchart

Hi Michael,

On 10.11.2020 09:13, Michael Tretter wrote:
> On Mon, 09 Nov 2020 12:15:39 +0900, Inki Dae wrote:
>> 20. 9. 11. 오후 10:53에 Michael Tretter 이(가) 쓴 글:
>>> This is v2 of the series to convert the Exynos MIPI DSI driver into a drm
>>> bridge and make it usable with other drivers. Although the driver is
>>> converted, it still supports the component framework API to stay compliant
>>> with the Exynos DRM driver.
>>>
>>> The Exynos MIPI DSI Phy is also found on the i.MX8M Mini. However, on the
>>> i.MX8M Mini, the bridge is driven by an LCDIF display controller instead of
>>> the Exynos Decon. The driver for the LCDIF does not use the component
>>> framework, but uses drm bridges.
>>>
>>> I don't have any Exynos SoC to actually test the series. I build a dummy to
>>> test the bridge with a component driver, to make sure that at least the
>>> initialization is working. Furthermore, tested the driver as a bridge with a
>>> few additional unfinished patches on the i.MX8M Mini EVK. However, somebody
>>> should verify that the driver is still working on Exynos hardware.
>>>
>>> I also changed the order of the patches to first make the driver more platform
>>> independent (patches 2 to 8), then convert to a drm bridge driver (patches 10
>> Just a fundamental question, A MIPI-DSI(Display Serial Interface) bus device
>> would be one of an encoder type of devices not bridge such as DSI to LVDS
>> and LVDS to DSI bridge devices, and also image enhancer and image compressor
>> in case of Exynos.
> I don't understand, why the MIPI-DSI bus device would be an encoder type and
> DSI to LVDS or MIPI-DSI to HDMI would be bridges. For example, the device tree
> documentation for the DSIM states that the DSIM receives RGB video as input
> and produces MIPI-DSI as output. Thus, the DSIM is basically a parallel RGB to
> MIPI-DSI bridge and the encoder is the LCD controller that encodes the video
> data as parallel RGB.
>
> On the i.MX8MM, the LCDIF is already the encoder. On Exynos, the series
> implements the encoder in the platform glue, but in the end the encoder can
> probably be moved to the DECON.

This is probably the historical decision. That time when Exynos DSI 
driver has been implemented, support for DRM bridges wasn't ready enough 
to use to for such purpose.

Frankly, I'm still not convinced that the current DRM bridge framework 
provides everything needed to reimplement the Exynos DSI driver with all 
its features. There are a lots of corner cases and order-specific bits 
in turning on/off the display pipeline, which don't map nicely to the 
bridge pre_enable (called in post-order) and enable (called in 
pre-order) callbacks. Especially if you consider that there might be 
another bridge before and after.

I think that Andrzej Hajda already pointed those drawbacks of the 
current design. Last week I've spent some significant amount of time 
playing with exynos dsi code to check how to match its operations 
(especially the runtime power management) to this design with the 
current boards (Arndale with additional DSI->LVDS bridge and panel, 
Trats2 with DSI panel and TM2e with MIC 'in-bridge' and DSI panel), but 
without a success.

>> Why do you want to convert such MIPI-DSI driver to bridge type of driver?
>> Seems not sensible. The reason would be just to share MIPI-DSI phy driver
>> for Exynos with i.MX8M Mini?
> Yes, the reason is that the driver should be shared between Exynos and
> i.MX8MM. It is the same IP and I don't see a reason why we should introduce
> another driver for the same IP if the driver works for both SoCs.

I think that the easiest way to share this driver between Exynos and iMX 
would be to extract the low-level code (the code that plays with 
hardware registers) to the common plane, while keeping the separate DRM 
glue, device component and platform device parts. This way the Exynos 
will continue to use it in the current form, while iMX can start using 
it as DRM bridge. A bit similar approach is used with exynos_dp driver 
and analogix_dp bridge shared with other SoCs.

I hope that later, when the bridge related issue are resolved, the 
Exynos can be converted too, so the encoder creation moved to FIMD and 
Decon.

Best regards

-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge
  2020-11-10  8:13     ` Michael Tretter
  2020-11-10 12:34       ` Marek Szyprowski
@ 2020-11-11  3:04       ` Inki Dae
  2020-11-11  3:11         ` Inki Dae
  1 sibling, 1 reply; 61+ messages in thread
From: Inki Dae @ 2020-11-11  3:04 UTC (permalink / raw)
  To: Michael Tretter
  Cc: linux-samsung-soc, jy0922.shim, narmstrong, b.zolnierkie,
	sw0312.kim, krzk, a.hajda, dri-devel, kernel, sylvester.nawrocki,
	Laurent.pinchart



20. 11. 10. 오후 5:13에 Michael Tretter 이(가) 쓴 글:
> On Mon, 09 Nov 2020 12:15:39 +0900, Inki Dae wrote:
>> 20. 9. 11. 오후 10:53에 Michael Tretter 이(가) 쓴 글:
>>> This is v2 of the series to convert the Exynos MIPI DSI driver into a drm
>>> bridge and make it usable with other drivers. Although the driver is
>>> converted, it still supports the component framework API to stay compliant
>>> with the Exynos DRM driver.
>>>
>>> The Exynos MIPI DSI Phy is also found on the i.MX8M Mini. However, on the
>>> i.MX8M Mini, the bridge is driven by an LCDIF display controller instead of
>>> the Exynos Decon. The driver for the LCDIF does not use the component
>>> framework, but uses drm bridges.
>>>
>>> I don't have any Exynos SoC to actually test the series. I build a dummy to
>>> test the bridge with a component driver, to make sure that at least the
>>> initialization is working. Furthermore, tested the driver as a bridge with a
>>> few additional unfinished patches on the i.MX8M Mini EVK. However, somebody
>>> should verify that the driver is still working on Exynos hardware.
>>>
>>> I also changed the order of the patches to first make the driver more platform
>>> independent (patches 2 to 8), then convert to a drm bridge driver (patches 10
>>
>> Just a fundamental question, A MIPI-DSI(Display Serial Interface) bus device
>> would be one of an encoder type of devices not bridge such as DSI to LVDS
>> and LVDS to DSI bridge devices, and also image enhancer and image compressor
>> in case of Exynos.
> 
> I don't understand, why the MIPI-DSI bus device would be an encoder type and
> DSI to LVDS or MIPI-DSI to HDMI would be bridges. For example, the device tree
> documentation for the DSIM states that the DSIM receives RGB video as input
> and produces MIPI-DSI as output. Thus, the DSIM is basically a parallel RGB to

MIPI-DSI receives RGB video as input and encodes it to MIPI packet and then transfers the packet to MIPI panel.
And finally, MIPI panel decodes the packet and updates it - RGB data - on its SRAM.

MIPI-DSI driver programs how the RGB video should be made as MIPI packet. For more detail, you could refer to MIPI-DSI spec.
This would be why we handle MIPI-DSI driver as an encoder like other ARM SoC DRM drivers did.

> MIPI-DSI bridge and the encoder is the LCD controller that encodes the video
> data as parallel RGB.
> 
> On the i.MX8MM, the LCDIF is already the encoder. On Exynos, the series
> implements the encoder in the platform glue, but in the end the encoder can
> probably be moved to the DECON.

As you know, Display controller can transfer RGB video to various devices such as RGB panel, CPU panel, LVDS panel via LVDS bridge, MIPI panel via MIPI-DSI bus device, and so on like below,

Display Controller --> RGB panel or CPU panel.
Display Controller --> LVDS bridge --> LVDS panel.
Display Controller --> MIPI DSI bus device --> MIPI Panel.
...

Display controller drivers such as FIMD and DECON series in case of Exynos don't create an encoder driver-internally instead of it depends on Display pipeline configuration - what kind of Display panel is used.
In fact, if the Display pipeline uses RGB panel or CPU panel without Display bus device such as MIPI-DSI, then FIMD and DECON drivers create an encoder for it internally - just we separated the code to consider other type of panels.

On the I.MX8MM, could you share how to handle an encoder if some board has only MIPI-DSI panel, and in this case, where is an encoder for it created? I looked into I.MX8MM DRM driver but didn't find MIPI-DSI driver.
Seems that they support only parallel display, HDMI and LVDS panel.

Thanks,
Inki Dae

> 
>> Why do you want to convert such MIPI-DSI driver to bridge type of driver?
>> Seems not sensible. The reason would be just to share MIPI-DSI phy driver
>> for Exynos with i.MX8M Mini?
> 
> Yes, the reason is that the driver should be shared between Exynos and
> i.MX8MM. It is the same IP and I don't see a reason why we should introduce
> another driver for the same IP if the driver works for both SoCs.
> 
> Michael
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge
  2020-11-11  3:04       ` Inki Dae
@ 2020-11-11  3:11         ` Inki Dae
  2020-11-11 10:18           ` Michael Tretter
  0 siblings, 1 reply; 61+ messages in thread
From: Inki Dae @ 2020-11-11  3:11 UTC (permalink / raw)
  To: Michael Tretter
  Cc: linux-samsung-soc, jy0922.shim, narmstrong, b.zolnierkie,
	sw0312.kim, krzk, a.hajda, dri-devel, kernel, sylvester.nawrocki,
	Laurent.pinchart



20. 11. 11. 오후 12:04에 Inki Dae 이(가) 쓴 글:
> 
> 
> 20. 11. 10. 오후 5:13에 Michael Tretter 이(가) 쓴 글:
>> On Mon, 09 Nov 2020 12:15:39 +0900, Inki Dae wrote:
>>> 20. 9. 11. 오후 10:53에 Michael Tretter 이(가) 쓴 글:
>>>> This is v2 of the series to convert the Exynos MIPI DSI driver into a drm
>>>> bridge and make it usable with other drivers. Although the driver is
>>>> converted, it still supports the component framework API to stay compliant
>>>> with the Exynos DRM driver.
>>>>
>>>> The Exynos MIPI DSI Phy is also found on the i.MX8M Mini. However, on the
>>>> i.MX8M Mini, the bridge is driven by an LCDIF display controller instead of
>>>> the Exynos Decon. The driver for the LCDIF does not use the component
>>>> framework, but uses drm bridges.
>>>>
>>>> I don't have any Exynos SoC to actually test the series. I build a dummy to
>>>> test the bridge with a component driver, to make sure that at least the
>>>> initialization is working. Furthermore, tested the driver as a bridge with a
>>>> few additional unfinished patches on the i.MX8M Mini EVK. However, somebody
>>>> should verify that the driver is still working on Exynos hardware.
>>>>
>>>> I also changed the order of the patches to first make the driver more platform
>>>> independent (patches 2 to 8), then convert to a drm bridge driver (patches 10
>>>
>>> Just a fundamental question, A MIPI-DSI(Display Serial Interface) bus device
>>> would be one of an encoder type of devices not bridge such as DSI to LVDS
>>> and LVDS to DSI bridge devices, and also image enhancer and image compressor
>>> in case of Exynos.
>>
>> I don't understand, why the MIPI-DSI bus device would be an encoder type and
>> DSI to LVDS or MIPI-DSI to HDMI would be bridges. For example, the device tree
>> documentation for the DSIM states that the DSIM receives RGB video as input
>> and produces MIPI-DSI as output. Thus, the DSIM is basically a parallel RGB to
> 
> MIPI-DSI receives RGB video as input and encodes it to MIPI packet and then transfers the packet to MIPI panel.
> And finally, MIPI panel decodes the packet and updates it - RGB data - on its SRAM.
> 
> MIPI-DSI driver programs how the RGB video should be made as MIPI packet. For more detail, you could refer to MIPI-DSI spec.
> This would be why we handle MIPI-DSI driver as an encoder like other ARM SoC DRM drivers did.
> 
>> MIPI-DSI bridge and the encoder is the LCD controller that encodes the video
>> data as parallel RGB.
>>
>> On the i.MX8MM, the LCDIF is already the encoder. On Exynos, the series
>> implements the encoder in the platform glue, but in the end the encoder can
>> probably be moved to the DECON.
> 
> As you know, Display controller can transfer RGB video to various devices such as RGB panel, CPU panel, LVDS panel via LVDS bridge, MIPI panel via MIPI-DSI bus device, and so on like below,
> 
> Display Controller --> RGB panel or CPU panel.
> Display Controller --> LVDS bridge --> LVDS panel.
> Display Controller --> MIPI DSI bus device --> MIPI Panel.
> ...
> 
> Display controller drivers such as FIMD and DECON series in case of Exynos don't create an encoder driver-internally instead of it depends on Display pipeline configuration - what kind of Display panel is used.
> In fact, if the Display pipeline uses RGB panel or CPU panel without Display bus device such as MIPI-DSI, then FIMD and DECON drivers create an encoder for it internally - just we separated the code to consider other type of panels.
> 
> On the I.MX8MM, could you share how to handle an encoder if some board has only MIPI-DSI panel, and in this case, where is an encoder for it created? I looked into I.MX8MM DRM driver but didn't find MIPI-DSI driver.
> Seems that they support only parallel display, HDMI and LVDS panel.

One more thing, If I saw correctly, the LVDS driver of IMX DRM - lmx_ldb - creates an encoder internally like MIPI-DSI driver of Exynos DRM did.

> 
> Thanks,
> Inki Dae
> 
>>
>>> Why do you want to convert such MIPI-DSI driver to bridge type of driver?
>>> Seems not sensible. The reason would be just to share MIPI-DSI phy driver
>>> for Exynos with i.MX8M Mini?
>>
>> Yes, the reason is that the driver should be shared between Exynos and
>> i.MX8MM. It is the same IP and I don't see a reason why we should introduce
>> another driver for the same IP if the driver works for both SoCs.
>>
>> Michael
>>
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge
  2020-11-11  3:11         ` Inki Dae
@ 2020-11-11 10:18           ` Michael Tretter
  2020-11-13  9:34             ` Inki Dae
  0 siblings, 1 reply; 61+ messages in thread
From: Michael Tretter @ 2020-11-11 10:18 UTC (permalink / raw)
  To: Inki Dae
  Cc: linux-samsung-soc, jy0922.shim, narmstrong, b.zolnierkie,
	sw0312.kim, krzk, a.hajda, dri-devel, kernel, sylvester.nawrocki,
	Laurent.pinchart

On Wed, 11 Nov 2020 12:11:15 +0900, Inki Dae wrote:
> 20. 11. 11. 오후 12:04에 Inki Dae 이(가) 쓴 글:
> > 20. 11. 10. 오후 5:13에 Michael Tretter 이(가) 쓴 글:
> >> On Mon, 09 Nov 2020 12:15:39 +0900, Inki Dae wrote:
> >>> 20. 9. 11. 오후 10:53에 Michael Tretter 이(가) 쓴 글:
> >>>> This is v2 of the series to convert the Exynos MIPI DSI driver into a drm
> >>>> bridge and make it usable with other drivers. Although the driver is
> >>>> converted, it still supports the component framework API to stay compliant
> >>>> with the Exynos DRM driver.
> >>>>
> >>>> The Exynos MIPI DSI Phy is also found on the i.MX8M Mini. However, on the
> >>>> i.MX8M Mini, the bridge is driven by an LCDIF display controller instead of
> >>>> the Exynos Decon. The driver for the LCDIF does not use the component
> >>>> framework, but uses drm bridges.
> >>>>
> >>>> I don't have any Exynos SoC to actually test the series. I build a dummy to
> >>>> test the bridge with a component driver, to make sure that at least the
> >>>> initialization is working. Furthermore, tested the driver as a bridge with a
> >>>> few additional unfinished patches on the i.MX8M Mini EVK. However, somebody
> >>>> should verify that the driver is still working on Exynos hardware.
> >>>>
> >>>> I also changed the order of the patches to first make the driver more platform
> >>>> independent (patches 2 to 8), then convert to a drm bridge driver (patches 10
> >>>
> >>> Just a fundamental question, A MIPI-DSI(Display Serial Interface) bus device
> >>> would be one of an encoder type of devices not bridge such as DSI to LVDS
> >>> and LVDS to DSI bridge devices, and also image enhancer and image compressor
> >>> in case of Exynos.
> >>
> >> I don't understand, why the MIPI-DSI bus device would be an encoder type and
> >> DSI to LVDS or MIPI-DSI to HDMI would be bridges. For example, the device tree
> >> documentation for the DSIM states that the DSIM receives RGB video as input
> >> and produces MIPI-DSI as output. Thus, the DSIM is basically a parallel RGB to
> > 
> > MIPI-DSI receives RGB video as input and encodes it to MIPI packet and then transfers the packet to MIPI panel.
> > And finally, MIPI panel decodes the packet and updates it - RGB data - on its SRAM.
> > 
> > MIPI-DSI driver programs how the RGB video should be made as MIPI packet. For more detail, you could refer to MIPI-DSI spec.
> > This would be why we handle MIPI-DSI driver as an encoder like other ARM SoC DRM drivers did.
> > 
> >> MIPI-DSI bridge and the encoder is the LCD controller that encodes the video
> >> data as parallel RGB.
> >>
> >> On the i.MX8MM, the LCDIF is already the encoder. On Exynos, the series
> >> implements the encoder in the platform glue, but in the end the encoder can
> >> probably be moved to the DECON.
> > 
> > As you know, Display controller can transfer RGB video to various devices such as RGB panel, CPU panel, LVDS panel via LVDS bridge, MIPI panel via MIPI-DSI bus device, and so on like below,
> > 
> > Display Controller --> RGB panel or CPU panel.
> > Display Controller --> LVDS bridge --> LVDS panel.
> > Display Controller --> MIPI DSI bus device --> MIPI Panel.
> > ...
> > 
> > Display controller drivers such as FIMD and DECON series in case of Exynos don't create an encoder driver-internally instead of it depends on Display pipeline configuration - what kind of Display panel is used.
> > In fact, if the Display pipeline uses RGB panel or CPU panel without Display bus device such as MIPI-DSI, then FIMD and DECON drivers create an encoder for it internally - just we separated the code to consider other type of panels.

What happens if I add a MIPI-DSI --> HDMI bridge to the Display pipeline? Then
the Pipeline is

Display Controller --> MIPI DSI bus device --> HDMI bridge --> HDMI Panel

If the type of the Display panel decides which part of the pipeline provides
the encoder, the HDMI bridge driver would be responsible for creating the
encoder, right? Thus, the MIPI-DSI driver would not be responsible for
creating the encoder and would also get the encoder from another driver.
Therefore, I prefer to think of the Display Controller as the encoder and
other bridges are just bridges.

BTW, this is exactly the Display pipeline that is used on the i.MX8MM EVK.

> > 
> > On the I.MX8MM, could you share how to handle an encoder if some board has only MIPI-DSI panel, and in this case, where is an encoder for it created? I looked into I.MX8MM DRM driver but didn't find MIPI-DSI driver.
> > Seems that they support only parallel display, HDMI and LVDS panel.
> 
> One more thing, If I saw correctly, the LVDS driver of IMX DRM - lmx_ldb - creates an encoder internally like MIPI-DSI driver of Exynos DRM did.

Yes, but the IMX DRM driver is not used on the i.MX8MM. The i.MX8MM uses the
LCDIF display controller instead of the IPU of the i.MX6. The driver for the
LCDIF is the mxsfb driver, which in turn uses the drm_simple_display_pipe,
which already provides the encoder. Therefore, to use a bridge driver with a
driver based on a drm_simple_display_pipe, the bridge driver must accept other
encoders.

Michael

> 
> > 
> > Thanks,
> > Inki Dae
> > 
> >>
> >>> Why do you want to convert such MIPI-DSI driver to bridge type of driver?
> >>> Seems not sensible. The reason would be just to share MIPI-DSI phy driver
> >>> for Exynos with i.MX8M Mini?
> >>
> >> Yes, the reason is that the driver should be shared between Exynos and
> >> i.MX8MM. It is the same IP and I don't see a reason why we should introduce
> >> another driver for the same IP if the driver works for both SoCs.
> >>
> >> Michael
> >>
> > 
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge
  2020-11-11 10:18           ` Michael Tretter
@ 2020-11-13  9:34             ` Inki Dae
  0 siblings, 0 replies; 61+ messages in thread
From: Inki Dae @ 2020-11-13  9:34 UTC (permalink / raw)
  To: Michael Tretter
  Cc: linux-samsung-soc, jy0922.shim, narmstrong, b.zolnierkie,
	sw0312.kim, krzk, a.hajda, dri-devel, kernel, sylvester.nawrocki,
	Laurent.pinchart



20. 11. 11. 오후 7:18에 Michael Tretter 이(가) 쓴 글:
> On Wed, 11 Nov 2020 12:11:15 +0900, Inki Dae wrote:
>> 20. 11. 11. 오후 12:04에 Inki Dae 이(가) 쓴 글:
>>> 20. 11. 10. 오후 5:13에 Michael Tretter 이(가) 쓴 글:
>>>> On Mon, 09 Nov 2020 12:15:39 +0900, Inki Dae wrote:
>>>>> 20. 9. 11. 오후 10:53에 Michael Tretter 이(가) 쓴 글:
>>>>>> This is v2 of the series to convert the Exynos MIPI DSI driver into a drm
>>>>>> bridge and make it usable with other drivers. Although the driver is
>>>>>> converted, it still supports the component framework API to stay compliant
>>>>>> with the Exynos DRM driver.
>>>>>>
>>>>>> The Exynos MIPI DSI Phy is also found on the i.MX8M Mini. However, on the
>>>>>> i.MX8M Mini, the bridge is driven by an LCDIF display controller instead of
>>>>>> the Exynos Decon. The driver for the LCDIF does not use the component
>>>>>> framework, but uses drm bridges.
>>>>>>
>>>>>> I don't have any Exynos SoC to actually test the series. I build a dummy to
>>>>>> test the bridge with a component driver, to make sure that at least the
>>>>>> initialization is working. Furthermore, tested the driver as a bridge with a
>>>>>> few additional unfinished patches on the i.MX8M Mini EVK. However, somebody
>>>>>> should verify that the driver is still working on Exynos hardware.
>>>>>>
>>>>>> I also changed the order of the patches to first make the driver more platform
>>>>>> independent (patches 2 to 8), then convert to a drm bridge driver (patches 10
>>>>>
>>>>> Just a fundamental question, A MIPI-DSI(Display Serial Interface) bus device
>>>>> would be one of an encoder type of devices not bridge such as DSI to LVDS
>>>>> and LVDS to DSI bridge devices, and also image enhancer and image compressor
>>>>> in case of Exynos.
>>>>
>>>> I don't understand, why the MIPI-DSI bus device would be an encoder type and
>>>> DSI to LVDS or MIPI-DSI to HDMI would be bridges. For example, the device tree
>>>> documentation for the DSIM states that the DSIM receives RGB video as input
>>>> and produces MIPI-DSI as output. Thus, the DSIM is basically a parallel RGB to
>>>
>>> MIPI-DSI receives RGB video as input and encodes it to MIPI packet and then transfers the packet to MIPI panel.
>>> And finally, MIPI panel decodes the packet and updates it - RGB data - on its SRAM.
>>>
>>> MIPI-DSI driver programs how the RGB video should be made as MIPI packet. For more detail, you could refer to MIPI-DSI spec.
>>> This would be why we handle MIPI-DSI driver as an encoder like other ARM SoC DRM drivers did.
>>>
>>>> MIPI-DSI bridge and the encoder is the LCD controller that encodes the video
>>>> data as parallel RGB.
>>>>
>>>> On the i.MX8MM, the LCDIF is already the encoder. On Exynos, the series
>>>> implements the encoder in the platform glue, but in the end the encoder can
>>>> probably be moved to the DECON.
>>>
>>> As you know, Display controller can transfer RGB video to various devices such as RGB panel, CPU panel, LVDS panel via LVDS bridge, MIPI panel via MIPI-DSI bus device, and so on like below,
>>>
>>> Display Controller --> RGB panel or CPU panel.
>>> Display Controller --> LVDS bridge --> LVDS panel.
>>> Display Controller --> MIPI DSI bus device --> MIPI Panel.
>>> ...
>>>
>>> Display controller drivers such as FIMD and DECON series in case of Exynos don't create an encoder driver-internally instead of it depends on Display pipeline configuration - what kind of Display panel is used.
>>> In fact, if the Display pipeline uses RGB panel or CPU panel without Display bus device such as MIPI-DSI, then FIMD and DECON drivers create an encoder for it internally - just we separated the code to consider other type of panels.
> 
> What happens if I add a MIPI-DSI --> HDMI bridge to the Display pipeline? Then
> the Pipeline is
> 
> Display Controller --> MIPI DSI bus device --> HDMI bridge --> HDMI Panel
> 
> If the type of the Display panel decides which part of the pipeline provides
> the encoder, the HDMI bridge driver would be responsible for creating the

HDMI bridge driver will create only an bridge because this is not encoder device but bridge device such as LVDS bridge.

Display Controller(CRTC) --> MIPI DSI bus device(Encoder) --> HDMI bridge(Bridge) --> HDMI Panel(Connector)

> encoder, right? Thus, the MIPI-DSI driver would not be responsible for
> creating the encoder and would also get the encoder from another driver.

I think MIPI-DSI is an encoder device as I mentioned earlier so MIPI-DSI driver would be responsible for creating the encoder.

> Therefore, I prefer to think of the Display Controller as the encoder and
> other bridges are just bridges.

So crtc driver will create an encoder internally in case of parallel RGB Panel *because encoder device isn't used*. On the other hand, each encoder driver will be an encoder in case of other type of panel - which is needed for Display bus device such as MIPI-DSI device.

Thanks,
Inki Dae

> 
> BTW, this is exactly the Display pipeline that is used on the i.MX8MM EVK.
> 
>>>
>>> On the I.MX8MM, could you share how to handle an encoder if some board has only MIPI-DSI panel, and in this case, where is an encoder for it created? I looked into I.MX8MM DRM driver but didn't find MIPI-DSI driver.
>>> Seems that they support only parallel display, HDMI and LVDS panel.
>>
>> One more thing, If I saw correctly, the LVDS driver of IMX DRM - lmx_ldb - creates an encoder internally like MIPI-DSI driver of Exynos DRM did.
> 
> Yes, but the IMX DRM driver is not used on the i.MX8MM. The i.MX8MM uses the
> LCDIF display controller instead of the IPU of the i.MX6. The driver for the
> LCDIF is the mxsfb driver, which in turn uses the drm_simple_display_pipe,
> which already provides the encoder. Therefore, to use a bridge driver with a
> driver based on a drm_simple_display_pipe, the bridge driver must accept other
> encoders.
> 
> Michael
> 
>>
>>>
>>> Thanks,
>>> Inki Dae
>>>
>>>>
>>>>> Why do you want to convert such MIPI-DSI driver to bridge type of driver?
>>>>> Seems not sensible. The reason would be just to share MIPI-DSI phy driver
>>>>> for Exynos with i.MX8M Mini?
>>>>
>>>> Yes, the reason is that the driver should be shared between Exynos and
>>>> i.MX8MM. It is the same IP and I don't see a reason why we should introduce
>>>> another driver for the same IP if the driver works for both SoCs.
>>>>
>>>> Michael
>>>>
>>>
>>
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 10/16] drm/exynos: implement a drm bridge
  2020-09-15 19:40             ` Andrzej Hajda
@ 2021-02-01 16:33               ` Michael Tretter
  2021-02-03 20:31                 ` Michael Tretter
  0 siblings, 1 reply; 61+ messages in thread
From: Michael Tretter @ 2021-02-01 16:33 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: aisheng.dong, linux-samsung-soc, Laurent.pinchart, jy0922.shim,
	narmstrong, shawnguo, abel.vesa, b.zolnierkie, sw0312.kim,
	linux-imx, krzk, frieder.schrempf, dri-devel, kernel, aford173,
	sylvester.nawrocki, ch, Marek Szyprowski

On Tue, 15 Sep 2020 21:40:40 +0200, Andrzej Hajda wrote:
> W dniu 14.09.2020 o 23:19, Andrzej Hajda pisze:
> > On 14.09.2020 22:01, Michael Tretter wrote:
> >> On Mon, 14 Sep 2020 14:31:19 +0200, Marek Szyprowski wrote:
> >>> On 14.09.2020 10:29, Marek Szyprowski wrote:
> >>>> On 11.09.2020 15:54, Michael Tretter wrote:
> >>>>> 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@pengutronix.de>
> >>>> This one (and the whole series applied) still fails on Exynos boards:
> >>>>
> >>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping 
> >>>> operations
> >>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> >>>> OF: graph: no port node found in /soc/dsi@11c80000
> >>>> 8<--- cut here ---
> >>>> Unable to handle kernel NULL pointer dereference at virtual address 
> >>>> 00000084
> >>>> pgd = (ptrval)
> >>>> [00000084] *pgd=00000000
> >>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> >>>> Modules linked in:
> >>>> CPU: 1 PID: 1 Comm: swapper/0 Not tainted
> >>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec #1608
> >>>> Hardware name: Samsung Exynos (Flattened Device Tree)
> >>>> PC is at drm_bridge_attach+0x18/0x164
> >>>> LR is at exynos_dsi_bind+0x88/0xa8
> >>>> pc : [<c0628c08>]    lr : [<c064d560>]    psr: 20000013
> >>>> sp : ef0dfca8  ip : 00000002  fp : c13190e0
> >>>> r10: 00000000  r9 : ee46d580  r8 : c13190e0
> >>>> r7 : ee438800  r6 : 00000018  r5 : ef253810  r4 : ef39e840
> >>>> r3 : 00000000  r2 : 00000018  r1 : ef39e888  r0 : ef39e840
> >>>> Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> >>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> >>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> >>>> Stack: (0xef0dfca8 to 0xef0e0000)
> >>>> ...
> >>>> [<c0628c08>] (drm_bridge_attach) from [<c064d560>]
> >>>> (exynos_dsi_bind+0x88/0xa8)
> >>>> [<c064d560>] (exynos_dsi_bind) from [<c066a800>]
> >>>> (component_bind_all+0xfc/0x290)
> >>>> [<c066a800>] (component_bind_all) from [<c0649dc0>]
> >>>> (exynos_drm_bind+0xe4/0x19c)
> >>>> [<c0649dc0>] (exynos_drm_bind) from [<c066ad74>]
> >>>> (try_to_bring_up_master+0x1e4/0x2c4)
> >>>> [<c066ad74>] (try_to_bring_up_master) from [<c066b2b4>]
> >>>> (component_master_add_with_match+0xd4/0x108)
> >>>> [<c066b2b4>] (component_master_add_with_match) from [<c0649ae8>]
> >>>> (exynos_drm_platform_probe+0xe4/0x110)
> >>>> [<c0649ae8>] (exynos_drm_platform_probe) from [<c0674e6c>]
> >>>> (platform_drv_probe+0x6c/0xa4)
> >>>> [<c0674e6c>] (platform_drv_probe) from [<c067242c>]
> >>>> (really_probe+0x200/0x4fc)
> >>>> [<c067242c>] (really_probe) from [<c06728f0>]
> >>>> (driver_probe_device+0x78/0x1fc)
> >>>> [<c06728f0>] (driver_probe_device) from [<c0672cd8>]
> >>>> (device_driver_attach+0x58/0x60)
> >>>> [<c0672cd8>] (device_driver_attach) from [<c0672dbc>]
> >>>> (__driver_attach+0xdc/0x174)
> >>>> [<c0672dbc>] (__driver_attach) from [<c06701b4>]
> >>>> (bus_for_each_dev+0x68/0xb4)
> >>>> [<c06701b4>] (bus_for_each_dev) from [<c06714e8>]
> >>>> (bus_add_driver+0x158/0x214)
> >>>> [<c06714e8>] (bus_add_driver) from [<c0673c1c>] 
> >>>> (driver_register+0x78/0x110)
> >>>> [<c0673c1c>] (driver_register) from [<c0649ca8>]
> >>>> (exynos_drm_init+0xe4/0x118)
> >>>> [<c0649ca8>] (exynos_drm_init) from [<c0102484>]
> >>>> (do_one_initcall+0x8c/0x42c)
> >>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
> >>>> (kernel_init_freeable+0x190/0x1dc)
> >>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7880>]
> >>>> (kernel_init+0x8/0x118)
> >>>> [<c0af7880>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> >>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
> >>>> ...
> >>>> ---[ end trace ee27f313f9ed9da1 ]---
> >>>>
> >>>> # arm-linux-gnueabi-addr2line -e vmlinux c0628c08
> >>>> drivers/gpu/drm/drm_bridge.c:184 (discriminator 1)
> >>>>
> >>>> I will try to debug it a bit more today.
> >>> The above crash has been caused by lack of in_bridge initialization to
> >>> NULL in exynos_dsi_bind() in this patch. However, fixing it reveals
> >>> another issue:
> >>>
> >>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
> >>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> >>> OF: graph: no port node found in /soc/dsi@11c80000
> >>> 8<--- cut here ---
> >>> Unable to handle kernel NULL pointer dereference at virtual address 
> >>> 00000280
> >>> pgd = (ptrval)
> >>> [00000280] *pgd=00000000
> >>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> >>> Modules linked in:
> >>> CPU: 0 PID: 1 Comm: swapper/0 Not tainted
> >>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec-dirty #1613
> >>> Hardware name: Samsung Exynos (Flattened Device Tree)
> >>> PC is at __mutex_lock+0x54/0xb18
> >>> LR is at lock_is_held_type+0x80/0x138
> >>> pc : [<c0afc920>]    lr : [<c0af63e8>]    psr: 60000013
> >>> sp : ef0dfd30  ip : 33937b74  fp : c13193c8
> >>> r10: c1208eec  r9 : 00000000  r8 : ee45f808
> >>> r7 : c19561a4  r6 : 00000000  r5 : 00000000  r4 : 0000024c
> >>> r3 : 00000000  r2 : 00204140  r1 : c124f13c  r0 : 00000000
> >>> Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> >>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> >>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> >>> Stack: (0xef0dfd30 to 0xef0e0000)
> >>> ...
> >>> [<c0afc920>] (__mutex_lock) from [<c0afd400>] 
> >>> (mutex_lock_nested+0x1c/0x24)
> >>> [<c0afd400>] (mutex_lock_nested) from [<c064d4b8>]
> >>> (__exynos_dsi_host_attach+0x20/0x6c)
> >>> [<c064d4b8>] (__exynos_dsi_host_attach) from [<c064d914>]
> >>> (exynos_dsi_host_attach+0x70/0x194)
> >>> [<c064d914>] (exynos_dsi_host_attach) from [<c0656b64>]
> >>> (s6e8aa0_probe+0x1b0/0x218)
> >>> [<c0656b64>] (s6e8aa0_probe) from [<c0672530>] 
> >>> (really_probe+0x200/0x4fc)
> >>> [<c0672530>] (really_probe) from [<c06729f4>]
> >>> (driver_probe_device+0x78/0x1fc)
> >>> [<c06729f4>] (driver_probe_device) from [<c0672ddc>]
> >>> (device_driver_attach+0x58/0x60)
> >>> [<c0672ddc>] (device_driver_attach) from [<c0672ec0>]
> >>> (__driver_attach+0xdc/0x174)
> >>> [<c0672ec0>] (__driver_attach) from [<c06702b8>]
> >>> (bus_for_each_dev+0x68/0xb4)
> >>> [<c06702b8>] (bus_for_each_dev) from [<c06715ec>]
> >>> (bus_add_driver+0x158/0x214)
> >>> [<c06715ec>] (bus_add_driver) from [<c0673d20>] 
> >>> (driver_register+0x78/0x110)
> >>> [<c0673d20>] (driver_register) from [<c0102484>]
> >>> (do_one_initcall+0x8c/0x42c)
> >>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
> >>> (kernel_init_freeable+0x190/0x1dc)
> >>> [<c11011c0>] (kernel_init_freeable) from [<c0af7988>]
> >>> (kernel_init+0x8/0x118)
> >>> [<c0af7988>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> >>> Exception stack(0xef0dffb0 to 0xef0dfff8)
> >>> ...
> >>> ---[ end trace c06e996ec2e8234d ]---
> >>>
> >>> This means that dsi->encoder.dev is not initialized in
> >>> __exynos_dsi_host_attach().
> >>>
> >>> This happens, because drm_bridge_attach() in exynos_dsi_bind() returned
> >>> earlier -517 (deferred probe), what causes cleanup of encoder and
> >>> release of all drm resources.
> >>>
> >>> Then however, the panel tries to register itself and
> >>> exynos_dsi_host_attach() tries to access the released encoder (which is
> >>> zeroed in drm_encoder_release) and rest of resources, what causes 
> >>> failure.
> >>>
> >>> It looks that something is missing. Maybe mipi host has to be 
> >>> registered
> >>> later, when bridge is ready? I have no idea how it is handled before
> >>> this patch. Andrzej, could you comment it a bit?
> >> I intentionally changed the order, because if another bridge follows 
> >> in the
> >> pipeline, the probe of the drm driver has to be deferred until some 
> >> bridge
> >> provides a connector. The next bridge registers itself via the 
> >> host_attach
> >> function and the deferral is ensured via the bind for the bind/unbind 
> >> API or
> >> the bridge_attach function otherwise.
> >>
> >> On the other hand, the bridge does not have an encoder until the mipi 
> >> device
> >> has been attached.
> >>
> >> As a solution, the exynos dsi driver must initialize the encoder in
> >> exynos_dsi_probe instead of in exynos_dsi_bind and access the encoder 
> >> via
> >> exynos_dsi instead of the bridge.
> >>
> >> Can you try to move everything except samsung_dsim_bind from 
> >> exynos_dsi_bind
> >> to exynos_dsi_probe (respectively for unbind) and report if it fixes the
> >> crash.
> >
> >
> > The original behaviour is that encoder (exynos_dsi) is registered 
> > regardless of sink presence (initially panel, later also bridge) - it 
> > avoids multiple issues with deferred probe, device driver bind/unbind 
> > and module load/unload. Appearance or disappearance of sink is 
> > reported to host nicely via DSI attach/detach callbacks - and it is 
> > reflected in drm world as change state of the connector.
> >
> > Registering DSI host in bind and unregistering in unbind assures that 
> > if mipi_dsi device is attached/detached the drm device is always 
> > present - it makes device/driver binding race free and allows to avoid 
> > additional locking.
> >
> > Moving DSI host registration to probe changes everything, for sure it 
> > breaks the nice feature of DSI attach/detach callbacks and apparently 
> > can cause different issues depending on device bind order.
> >
> > I will try to look at the patches tomorrow and maybe I can find more 
> > constructive comments :)
> 
> 
> As I said yesterday, exynos_dsi driver uses dsi host attach/detach 
> callbacks to control appearance/disappearance of downstream device. It 
> allows to:
> 
> 1. Safely bind/unbind different device drivers at any time and at any 
> order, without killing exynos_drm and/or crashing system.
> 
> 2. Avoid issues with late drm init - on some platforms exynos_drm device 
> appeared too late, due to deferred probe, and resulted in black screen 
> in userspace.
> 
> 
> Now if we want to convert exynos_dsi to drm_bridge I see following options:
> 
> A. Forgot about callbacks and make the exynos_drm to defer probing until 
> exynos_dsi bridge is available, probably it will cause later exynos_drm 
> appearance, thus probably black screen on some targets. So for sure it 
> will be suboptimal. Making it bridge unbind safe would be another 
> problem, but most developers do not care about it so why should we? :)
> 
> B. Try to mimic current behaviour - exynos_dsi register bridge ASAP, 
> even if downstream devices are not yet attached, on attach/detach notify 
> drm about it via connector status change, for this dsi_host registration 
> should be performed from drm_bridge attach, I guess.
> 
> 
> Option A is more standard, but is unsafe and causes other issues.
> 
> Option B keeps current behaviour.

Maybe we can have both, but I am not sure, if I am missing something:

I still prefer option A for the samsung-dsim driver, because it is more
standard, simpler and avoids issues with encoders, connectors or handling
hotplug.

The idea is to use two bridges in the exynos-dsi driver: One bridge in the
samsung-dsim driver which implements option A and defers probing of the drm
driver until the next bridge is attached. And a second bridge in the
exynos_dsi that attaches to the first bridge (thus, allowing the exynos_drm
device to appear) and implements the hotplug handling for notifying drm via
connector status change.

The driver for the i.MX8M would use the samsung-dsim bridge without an
additional bridge.

This allows the samsung-dsim driver to expose the standard behavior while the
exynos_dsi may stick to the existing behavior for the exynos_drm driver.

I hope this makes sense and does not sound too crazy. It might be difficult to
get the probing and mipi host/device registration correct, but I will try, if
this can work.

Michael
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 10/16] drm/exynos: implement a drm bridge
  2021-02-01 16:33               ` Michael Tretter
@ 2021-02-03 20:31                 ` Michael Tretter
  2021-02-04 10:17                   ` Daniel Vetter
  0 siblings, 1 reply; 61+ messages in thread
From: Michael Tretter @ 2021-02-03 20:31 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: ch, narmstrong, dri-devel, frieder.schrempf, Laurent.pinchart,
	Marek Szyprowski, marex, linux-samsung-soc, jy0922.shim, krzk,
	linux-imx, b.zolnierkie, sylvester.nawrocki, aford173, abel.vesa,
	aisheng.dong, sw0312.kim, kernel, shawnguo

On Mon, 01 Feb 2021 17:33:14 +0100, Michael Tretter wrote:
> On Tue, 15 Sep 2020 21:40:40 +0200, Andrzej Hajda wrote:
> > W dniu 14.09.2020 o 23:19, Andrzej Hajda pisze:
> > > On 14.09.2020 22:01, Michael Tretter wrote:
> > >> On Mon, 14 Sep 2020 14:31:19 +0200, Marek Szyprowski wrote:
> > >>> On 14.09.2020 10:29, Marek Szyprowski wrote:
> > >>>> On 11.09.2020 15:54, Michael Tretter wrote:
> > >>>>> 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@pengutronix.de>
> > >>>> This one (and the whole series applied) still fails on Exynos boards:
> > >>>>
> > >>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping 
> > >>>> operations
> > >>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> > >>>> OF: graph: no port node found in /soc/dsi@11c80000
> > >>>> 8<--- cut here ---
> > >>>> Unable to handle kernel NULL pointer dereference at virtual address 
> > >>>> 00000084
> > >>>> pgd = (ptrval)
> > >>>> [00000084] *pgd=00000000
> > >>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> > >>>> Modules linked in:
> > >>>> CPU: 1 PID: 1 Comm: swapper/0 Not tainted
> > >>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec #1608
> > >>>> Hardware name: Samsung Exynos (Flattened Device Tree)
> > >>>> PC is at drm_bridge_attach+0x18/0x164
> > >>>> LR is at exynos_dsi_bind+0x88/0xa8
> > >>>> pc : [<c0628c08>]    lr : [<c064d560>]    psr: 20000013
> > >>>> sp : ef0dfca8  ip : 00000002  fp : c13190e0
> > >>>> r10: 00000000  r9 : ee46d580  r8 : c13190e0
> > >>>> r7 : ee438800  r6 : 00000018  r5 : ef253810  r4 : ef39e840
> > >>>> r3 : 00000000  r2 : 00000018  r1 : ef39e888  r0 : ef39e840
> > >>>> Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> > >>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> > >>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> > >>>> Stack: (0xef0dfca8 to 0xef0e0000)
> > >>>> ...
> > >>>> [<c0628c08>] (drm_bridge_attach) from [<c064d560>]
> > >>>> (exynos_dsi_bind+0x88/0xa8)
> > >>>> [<c064d560>] (exynos_dsi_bind) from [<c066a800>]
> > >>>> (component_bind_all+0xfc/0x290)
> > >>>> [<c066a800>] (component_bind_all) from [<c0649dc0>]
> > >>>> (exynos_drm_bind+0xe4/0x19c)
> > >>>> [<c0649dc0>] (exynos_drm_bind) from [<c066ad74>]
> > >>>> (try_to_bring_up_master+0x1e4/0x2c4)
> > >>>> [<c066ad74>] (try_to_bring_up_master) from [<c066b2b4>]
> > >>>> (component_master_add_with_match+0xd4/0x108)
> > >>>> [<c066b2b4>] (component_master_add_with_match) from [<c0649ae8>]
> > >>>> (exynos_drm_platform_probe+0xe4/0x110)
> > >>>> [<c0649ae8>] (exynos_drm_platform_probe) from [<c0674e6c>]
> > >>>> (platform_drv_probe+0x6c/0xa4)
> > >>>> [<c0674e6c>] (platform_drv_probe) from [<c067242c>]
> > >>>> (really_probe+0x200/0x4fc)
> > >>>> [<c067242c>] (really_probe) from [<c06728f0>]
> > >>>> (driver_probe_device+0x78/0x1fc)
> > >>>> [<c06728f0>] (driver_probe_device) from [<c0672cd8>]
> > >>>> (device_driver_attach+0x58/0x60)
> > >>>> [<c0672cd8>] (device_driver_attach) from [<c0672dbc>]
> > >>>> (__driver_attach+0xdc/0x174)
> > >>>> [<c0672dbc>] (__driver_attach) from [<c06701b4>]
> > >>>> (bus_for_each_dev+0x68/0xb4)
> > >>>> [<c06701b4>] (bus_for_each_dev) from [<c06714e8>]
> > >>>> (bus_add_driver+0x158/0x214)
> > >>>> [<c06714e8>] (bus_add_driver) from [<c0673c1c>] 
> > >>>> (driver_register+0x78/0x110)
> > >>>> [<c0673c1c>] (driver_register) from [<c0649ca8>]
> > >>>> (exynos_drm_init+0xe4/0x118)
> > >>>> [<c0649ca8>] (exynos_drm_init) from [<c0102484>]
> > >>>> (do_one_initcall+0x8c/0x42c)
> > >>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
> > >>>> (kernel_init_freeable+0x190/0x1dc)
> > >>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7880>]
> > >>>> (kernel_init+0x8/0x118)
> > >>>> [<c0af7880>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> > >>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
> > >>>> ...
> > >>>> ---[ end trace ee27f313f9ed9da1 ]---
> > >>>>
> > >>>> # arm-linux-gnueabi-addr2line -e vmlinux c0628c08
> > >>>> drivers/gpu/drm/drm_bridge.c:184 (discriminator 1)
> > >>>>
> > >>>> I will try to debug it a bit more today.
> > >>> The above crash has been caused by lack of in_bridge initialization to
> > >>> NULL in exynos_dsi_bind() in this patch. However, fixing it reveals
> > >>> another issue:
> > >>>
> > >>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
> > >>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> > >>> OF: graph: no port node found in /soc/dsi@11c80000
> > >>> 8<--- cut here ---
> > >>> Unable to handle kernel NULL pointer dereference at virtual address 
> > >>> 00000280
> > >>> pgd = (ptrval)
> > >>> [00000280] *pgd=00000000
> > >>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> > >>> Modules linked in:
> > >>> CPU: 0 PID: 1 Comm: swapper/0 Not tainted
> > >>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec-dirty #1613
> > >>> Hardware name: Samsung Exynos (Flattened Device Tree)
> > >>> PC is at __mutex_lock+0x54/0xb18
> > >>> LR is at lock_is_held_type+0x80/0x138
> > >>> pc : [<c0afc920>]    lr : [<c0af63e8>]    psr: 60000013
> > >>> sp : ef0dfd30  ip : 33937b74  fp : c13193c8
> > >>> r10: c1208eec  r9 : 00000000  r8 : ee45f808
> > >>> r7 : c19561a4  r6 : 00000000  r5 : 00000000  r4 : 0000024c
> > >>> r3 : 00000000  r2 : 00204140  r1 : c124f13c  r0 : 00000000
> > >>> Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> > >>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> > >>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> > >>> Stack: (0xef0dfd30 to 0xef0e0000)
> > >>> ...
> > >>> [<c0afc920>] (__mutex_lock) from [<c0afd400>] 
> > >>> (mutex_lock_nested+0x1c/0x24)
> > >>> [<c0afd400>] (mutex_lock_nested) from [<c064d4b8>]
> > >>> (__exynos_dsi_host_attach+0x20/0x6c)
> > >>> [<c064d4b8>] (__exynos_dsi_host_attach) from [<c064d914>]
> > >>> (exynos_dsi_host_attach+0x70/0x194)
> > >>> [<c064d914>] (exynos_dsi_host_attach) from [<c0656b64>]
> > >>> (s6e8aa0_probe+0x1b0/0x218)
> > >>> [<c0656b64>] (s6e8aa0_probe) from [<c0672530>] 
> > >>> (really_probe+0x200/0x4fc)
> > >>> [<c0672530>] (really_probe) from [<c06729f4>]
> > >>> (driver_probe_device+0x78/0x1fc)
> > >>> [<c06729f4>] (driver_probe_device) from [<c0672ddc>]
> > >>> (device_driver_attach+0x58/0x60)
> > >>> [<c0672ddc>] (device_driver_attach) from [<c0672ec0>]
> > >>> (__driver_attach+0xdc/0x174)
> > >>> [<c0672ec0>] (__driver_attach) from [<c06702b8>]
> > >>> (bus_for_each_dev+0x68/0xb4)
> > >>> [<c06702b8>] (bus_for_each_dev) from [<c06715ec>]
> > >>> (bus_add_driver+0x158/0x214)
> > >>> [<c06715ec>] (bus_add_driver) from [<c0673d20>] 
> > >>> (driver_register+0x78/0x110)
> > >>> [<c0673d20>] (driver_register) from [<c0102484>]
> > >>> (do_one_initcall+0x8c/0x42c)
> > >>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
> > >>> (kernel_init_freeable+0x190/0x1dc)
> > >>> [<c11011c0>] (kernel_init_freeable) from [<c0af7988>]
> > >>> (kernel_init+0x8/0x118)
> > >>> [<c0af7988>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> > >>> Exception stack(0xef0dffb0 to 0xef0dfff8)
> > >>> ...
> > >>> ---[ end trace c06e996ec2e8234d ]---
> > >>>
> > >>> This means that dsi->encoder.dev is not initialized in
> > >>> __exynos_dsi_host_attach().
> > >>>
> > >>> This happens, because drm_bridge_attach() in exynos_dsi_bind() returned
> > >>> earlier -517 (deferred probe), what causes cleanup of encoder and
> > >>> release of all drm resources.
> > >>>
> > >>> Then however, the panel tries to register itself and
> > >>> exynos_dsi_host_attach() tries to access the released encoder (which is
> > >>> zeroed in drm_encoder_release) and rest of resources, what causes 
> > >>> failure.
> > >>>
> > >>> It looks that something is missing. Maybe mipi host has to be 
> > >>> registered
> > >>> later, when bridge is ready? I have no idea how it is handled before
> > >>> this patch. Andrzej, could you comment it a bit?
> > >> I intentionally changed the order, because if another bridge follows 
> > >> in the
> > >> pipeline, the probe of the drm driver has to be deferred until some 
> > >> bridge
> > >> provides a connector. The next bridge registers itself via the 
> > >> host_attach
> > >> function and the deferral is ensured via the bind for the bind/unbind 
> > >> API or
> > >> the bridge_attach function otherwise.
> > >>
> > >> On the other hand, the bridge does not have an encoder until the mipi 
> > >> device
> > >> has been attached.
> > >>
> > >> As a solution, the exynos dsi driver must initialize the encoder in
> > >> exynos_dsi_probe instead of in exynos_dsi_bind and access the encoder 
> > >> via
> > >> exynos_dsi instead of the bridge.
> > >>
> > >> Can you try to move everything except samsung_dsim_bind from 
> > >> exynos_dsi_bind
> > >> to exynos_dsi_probe (respectively for unbind) and report if it fixes the
> > >> crash.
> > >
> > >
> > > The original behaviour is that encoder (exynos_dsi) is registered 
> > > regardless of sink presence (initially panel, later also bridge) - it 
> > > avoids multiple issues with deferred probe, device driver bind/unbind 
> > > and module load/unload. Appearance or disappearance of sink is 
> > > reported to host nicely via DSI attach/detach callbacks - and it is 
> > > reflected in drm world as change state of the connector.
> > >
> > > Registering DSI host in bind and unregistering in unbind assures that 
> > > if mipi_dsi device is attached/detached the drm device is always 
> > > present - it makes device/driver binding race free and allows to avoid 
> > > additional locking.
> > >
> > > Moving DSI host registration to probe changes everything, for sure it 
> > > breaks the nice feature of DSI attach/detach callbacks and apparently 
> > > can cause different issues depending on device bind order.
> > >
> > > I will try to look at the patches tomorrow and maybe I can find more 
> > > constructive comments :)
> > 
> > 
> > As I said yesterday, exynos_dsi driver uses dsi host attach/detach 
> > callbacks to control appearance/disappearance of downstream device. It 
> > allows to:
> > 
> > 1. Safely bind/unbind different device drivers at any time and at any 
> > order, without killing exynos_drm and/or crashing system.
> > 
> > 2. Avoid issues with late drm init - on some platforms exynos_drm device 
> > appeared too late, due to deferred probe, and resulted in black screen 
> > in userspace.
> > 
> > 
> > Now if we want to convert exynos_dsi to drm_bridge I see following options:
> > 
> > A. Forgot about callbacks and make the exynos_drm to defer probing until 
> > exynos_dsi bridge is available, probably it will cause later exynos_drm 
> > appearance, thus probably black screen on some targets. So for sure it 
> > will be suboptimal. Making it bridge unbind safe would be another 
> > problem, but most developers do not care about it so why should we? :)
> > 
> > B. Try to mimic current behaviour - exynos_dsi register bridge ASAP, 
> > even if downstream devices are not yet attached, on attach/detach notify 
> > drm about it via connector status change, for this dsi_host registration 
> > should be performed from drm_bridge attach, I guess.
> > 
> > 
> > Option A is more standard, but is unsafe and causes other issues.
> > 
> > Option B keeps current behaviour.
> 
> Maybe we can have both, but I am not sure, if I am missing something:
> 
> I still prefer option A for the samsung-dsim driver, because it is more
> standard, simpler and avoids issues with encoders, connectors or handling
> hotplug.
> 
> The idea is to use two bridges in the exynos-dsi driver: One bridge in the
> samsung-dsim driver which implements option A and defers probing of the drm
> driver until the next bridge is attached. And a second bridge in the
> exynos_dsi that attaches to the first bridge (thus, allowing the exynos_drm
> device to appear) and implements the hotplug handling for notifying drm via
> connector status change.
> 
> The driver for the i.MX8M would use the samsung-dsim bridge without an
> additional bridge.
> 
> This allows the samsung-dsim driver to expose the standard behavior while the
> exynos_dsi may stick to the existing behavior for the exynos_drm driver.
> 
> I hope this makes sense and does not sound too crazy. It might be difficult to
> get the probing and mipi host/device registration correct, but I will try, if
> this can work.

Adding two bridges for being able to support hotplugging adds many special
cases to the bridge driver and still requires more custom API to correctly add
the second bridge. I don't think that this a viable path to go.

This leaves us with:

Option A) Standard drm_bridge behavior, which is currently implemented, but
incompatible with the currently expected behavior of exynos_drm.

Option B) Creating the drm device without all bridges being attached, which
would work with the exynos_drm driver, but breaks for the standard drm_bridge
behavior, especially, if the encoder/connector is created at the beginning of
the pipeline and passed downwards when the bridges are attached.

Option C) Extracting only low level register accesses into shared code, adding
a custom interface and implementing the drm_bridge handling in the platform
specific code.

None of the options really convinces me.

Michael
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 10/16] drm/exynos: implement a drm bridge
  2021-02-03 20:31                 ` Michael Tretter
@ 2021-02-04 10:17                   ` Daniel Vetter
  2021-02-04 10:56                     ` Michael Tretter
  0 siblings, 1 reply; 61+ messages in thread
From: Daniel Vetter @ 2021-02-04 10:17 UTC (permalink / raw)
  To: Michael Tretter
  Cc: Marek Vasut, aisheng.dong, linux-samsung-soc, ch, Neil Armstrong,
	Shawn Guo, dri-devel, Krzysztof Kozlowski, Seung-Woo Kim,
	dl-linux-imx, frieder.schrempf, abel.vesa, Andrzej Hajda,
	Bartlomiej Zolnierkiewicz, Laurent Pinchart, Sascha Hauer,
	Joonyoung Shim, sylvester.nawrocki, aford173, Marek Szyprowski

On Wed, Feb 3, 2021 at 9:32 PM Michael Tretter <m.tretter@pengutronix.de> wrote:
>
> On Mon, 01 Feb 2021 17:33:14 +0100, Michael Tretter wrote:
> > On Tue, 15 Sep 2020 21:40:40 +0200, Andrzej Hajda wrote:
> > > W dniu 14.09.2020 o 23:19, Andrzej Hajda pisze:
> > > > On 14.09.2020 22:01, Michael Tretter wrote:
> > > >> On Mon, 14 Sep 2020 14:31:19 +0200, Marek Szyprowski wrote:
> > > >>> On 14.09.2020 10:29, Marek Szyprowski wrote:
> > > >>>> On 11.09.2020 15:54, Michael Tretter wrote:
> > > >>>>> 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@pengutronix.de>
> > > >>>> This one (and the whole series applied) still fails on Exynos boards:
> > > >>>>
> > > >>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping
> > > >>>> operations
> > > >>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> > > >>>> OF: graph: no port node found in /soc/dsi@11c80000
> > > >>>> 8<--- cut here ---
> > > >>>> Unable to handle kernel NULL pointer dereference at virtual address
> > > >>>> 00000084
> > > >>>> pgd = (ptrval)
> > > >>>> [00000084] *pgd=00000000
> > > >>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> > > >>>> Modules linked in:
> > > >>>> CPU: 1 PID: 1 Comm: swapper/0 Not tainted
> > > >>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec #1608
> > > >>>> Hardware name: Samsung Exynos (Flattened Device Tree)
> > > >>>> PC is at drm_bridge_attach+0x18/0x164
> > > >>>> LR is at exynos_dsi_bind+0x88/0xa8
> > > >>>> pc : [<c0628c08>]    lr : [<c064d560>]    psr: 20000013
> > > >>>> sp : ef0dfca8  ip : 00000002  fp : c13190e0
> > > >>>> r10: 00000000  r9 : ee46d580  r8 : c13190e0
> > > >>>> r7 : ee438800  r6 : 00000018  r5 : ef253810  r4 : ef39e840
> > > >>>> r3 : 00000000  r2 : 00000018  r1 : ef39e888  r0 : ef39e840
> > > >>>> Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> > > >>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> > > >>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> > > >>>> Stack: (0xef0dfca8 to 0xef0e0000)
> > > >>>> ...
> > > >>>> [<c0628c08>] (drm_bridge_attach) from [<c064d560>]
> > > >>>> (exynos_dsi_bind+0x88/0xa8)
> > > >>>> [<c064d560>] (exynos_dsi_bind) from [<c066a800>]
> > > >>>> (component_bind_all+0xfc/0x290)
> > > >>>> [<c066a800>] (component_bind_all) from [<c0649dc0>]
> > > >>>> (exynos_drm_bind+0xe4/0x19c)
> > > >>>> [<c0649dc0>] (exynos_drm_bind) from [<c066ad74>]
> > > >>>> (try_to_bring_up_master+0x1e4/0x2c4)
> > > >>>> [<c066ad74>] (try_to_bring_up_master) from [<c066b2b4>]
> > > >>>> (component_master_add_with_match+0xd4/0x108)
> > > >>>> [<c066b2b4>] (component_master_add_with_match) from [<c0649ae8>]
> > > >>>> (exynos_drm_platform_probe+0xe4/0x110)
> > > >>>> [<c0649ae8>] (exynos_drm_platform_probe) from [<c0674e6c>]
> > > >>>> (platform_drv_probe+0x6c/0xa4)
> > > >>>> [<c0674e6c>] (platform_drv_probe) from [<c067242c>]
> > > >>>> (really_probe+0x200/0x4fc)
> > > >>>> [<c067242c>] (really_probe) from [<c06728f0>]
> > > >>>> (driver_probe_device+0x78/0x1fc)
> > > >>>> [<c06728f0>] (driver_probe_device) from [<c0672cd8>]
> > > >>>> (device_driver_attach+0x58/0x60)
> > > >>>> [<c0672cd8>] (device_driver_attach) from [<c0672dbc>]
> > > >>>> (__driver_attach+0xdc/0x174)
> > > >>>> [<c0672dbc>] (__driver_attach) from [<c06701b4>]
> > > >>>> (bus_for_each_dev+0x68/0xb4)
> > > >>>> [<c06701b4>] (bus_for_each_dev) from [<c06714e8>]
> > > >>>> (bus_add_driver+0x158/0x214)
> > > >>>> [<c06714e8>] (bus_add_driver) from [<c0673c1c>]
> > > >>>> (driver_register+0x78/0x110)
> > > >>>> [<c0673c1c>] (driver_register) from [<c0649ca8>]
> > > >>>> (exynos_drm_init+0xe4/0x118)
> > > >>>> [<c0649ca8>] (exynos_drm_init) from [<c0102484>]
> > > >>>> (do_one_initcall+0x8c/0x42c)
> > > >>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
> > > >>>> (kernel_init_freeable+0x190/0x1dc)
> > > >>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7880>]
> > > >>>> (kernel_init+0x8/0x118)
> > > >>>> [<c0af7880>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> > > >>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
> > > >>>> ...
> > > >>>> ---[ end trace ee27f313f9ed9da1 ]---
> > > >>>>
> > > >>>> # arm-linux-gnueabi-addr2line -e vmlinux c0628c08
> > > >>>> drivers/gpu/drm/drm_bridge.c:184 (discriminator 1)
> > > >>>>
> > > >>>> I will try to debug it a bit more today.
> > > >>> The above crash has been caused by lack of in_bridge initialization to
> > > >>> NULL in exynos_dsi_bind() in this patch. However, fixing it reveals
> > > >>> another issue:
> > > >>>
> > > >>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
> > > >>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> > > >>> OF: graph: no port node found in /soc/dsi@11c80000
> > > >>> 8<--- cut here ---
> > > >>> Unable to handle kernel NULL pointer dereference at virtual address
> > > >>> 00000280
> > > >>> pgd = (ptrval)
> > > >>> [00000280] *pgd=00000000
> > > >>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> > > >>> Modules linked in:
> > > >>> CPU: 0 PID: 1 Comm: swapper/0 Not tainted
> > > >>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec-dirty #1613
> > > >>> Hardware name: Samsung Exynos (Flattened Device Tree)
> > > >>> PC is at __mutex_lock+0x54/0xb18
> > > >>> LR is at lock_is_held_type+0x80/0x138
> > > >>> pc : [<c0afc920>]    lr : [<c0af63e8>]    psr: 60000013
> > > >>> sp : ef0dfd30  ip : 33937b74  fp : c13193c8
> > > >>> r10: c1208eec  r9 : 00000000  r8 : ee45f808
> > > >>> r7 : c19561a4  r6 : 00000000  r5 : 00000000  r4 : 0000024c
> > > >>> r3 : 00000000  r2 : 00204140  r1 : c124f13c  r0 : 00000000
> > > >>> Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> > > >>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> > > >>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> > > >>> Stack: (0xef0dfd30 to 0xef0e0000)
> > > >>> ...
> > > >>> [<c0afc920>] (__mutex_lock) from [<c0afd400>]
> > > >>> (mutex_lock_nested+0x1c/0x24)
> > > >>> [<c0afd400>] (mutex_lock_nested) from [<c064d4b8>]
> > > >>> (__exynos_dsi_host_attach+0x20/0x6c)
> > > >>> [<c064d4b8>] (__exynos_dsi_host_attach) from [<c064d914>]
> > > >>> (exynos_dsi_host_attach+0x70/0x194)
> > > >>> [<c064d914>] (exynos_dsi_host_attach) from [<c0656b64>]
> > > >>> (s6e8aa0_probe+0x1b0/0x218)
> > > >>> [<c0656b64>] (s6e8aa0_probe) from [<c0672530>]
> > > >>> (really_probe+0x200/0x4fc)
> > > >>> [<c0672530>] (really_probe) from [<c06729f4>]
> > > >>> (driver_probe_device+0x78/0x1fc)
> > > >>> [<c06729f4>] (driver_probe_device) from [<c0672ddc>]
> > > >>> (device_driver_attach+0x58/0x60)
> > > >>> [<c0672ddc>] (device_driver_attach) from [<c0672ec0>]
> > > >>> (__driver_attach+0xdc/0x174)
> > > >>> [<c0672ec0>] (__driver_attach) from [<c06702b8>]
> > > >>> (bus_for_each_dev+0x68/0xb4)
> > > >>> [<c06702b8>] (bus_for_each_dev) from [<c06715ec>]
> > > >>> (bus_add_driver+0x158/0x214)
> > > >>> [<c06715ec>] (bus_add_driver) from [<c0673d20>]
> > > >>> (driver_register+0x78/0x110)
> > > >>> [<c0673d20>] (driver_register) from [<c0102484>]
> > > >>> (do_one_initcall+0x8c/0x42c)
> > > >>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
> > > >>> (kernel_init_freeable+0x190/0x1dc)
> > > >>> [<c11011c0>] (kernel_init_freeable) from [<c0af7988>]
> > > >>> (kernel_init+0x8/0x118)
> > > >>> [<c0af7988>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> > > >>> Exception stack(0xef0dffb0 to 0xef0dfff8)
> > > >>> ...
> > > >>> ---[ end trace c06e996ec2e8234d ]---
> > > >>>
> > > >>> This means that dsi->encoder.dev is not initialized in
> > > >>> __exynos_dsi_host_attach().
> > > >>>
> > > >>> This happens, because drm_bridge_attach() in exynos_dsi_bind() returned
> > > >>> earlier -517 (deferred probe), what causes cleanup of encoder and
> > > >>> release of all drm resources.
> > > >>>
> > > >>> Then however, the panel tries to register itself and
> > > >>> exynos_dsi_host_attach() tries to access the released encoder (which is
> > > >>> zeroed in drm_encoder_release) and rest of resources, what causes
> > > >>> failure.
> > > >>>
> > > >>> It looks that something is missing. Maybe mipi host has to be
> > > >>> registered
> > > >>> later, when bridge is ready? I have no idea how it is handled before
> > > >>> this patch. Andrzej, could you comment it a bit?
> > > >> I intentionally changed the order, because if another bridge follows
> > > >> in the
> > > >> pipeline, the probe of the drm driver has to be deferred until some
> > > >> bridge
> > > >> provides a connector. The next bridge registers itself via the
> > > >> host_attach
> > > >> function and the deferral is ensured via the bind for the bind/unbind
> > > >> API or
> > > >> the bridge_attach function otherwise.
> > > >>
> > > >> On the other hand, the bridge does not have an encoder until the mipi
> > > >> device
> > > >> has been attached.
> > > >>
> > > >> As a solution, the exynos dsi driver must initialize the encoder in
> > > >> exynos_dsi_probe instead of in exynos_dsi_bind and access the encoder
> > > >> via
> > > >> exynos_dsi instead of the bridge.
> > > >>
> > > >> Can you try to move everything except samsung_dsim_bind from
> > > >> exynos_dsi_bind
> > > >> to exynos_dsi_probe (respectively for unbind) and report if it fixes the
> > > >> crash.
> > > >
> > > >
> > > > The original behaviour is that encoder (exynos_dsi) is registered
> > > > regardless of sink presence (initially panel, later also bridge) - it
> > > > avoids multiple issues with deferred probe, device driver bind/unbind
> > > > and module load/unload. Appearance or disappearance of sink is
> > > > reported to host nicely via DSI attach/detach callbacks - and it is
> > > > reflected in drm world as change state of the connector.
> > > >
> > > > Registering DSI host in bind and unregistering in unbind assures that
> > > > if mipi_dsi device is attached/detached the drm device is always
> > > > present - it makes device/driver binding race free and allows to avoid
> > > > additional locking.
> > > >
> > > > Moving DSI host registration to probe changes everything, for sure it
> > > > breaks the nice feature of DSI attach/detach callbacks and apparently
> > > > can cause different issues depending on device bind order.
> > > >
> > > > I will try to look at the patches tomorrow and maybe I can find more
> > > > constructive comments :)
> > >
> > >
> > > As I said yesterday, exynos_dsi driver uses dsi host attach/detach
> > > callbacks to control appearance/disappearance of downstream device. It
> > > allows to:
> > >
> > > 1. Safely bind/unbind different device drivers at any time and at any
> > > order, without killing exynos_drm and/or crashing system.
> > >
> > > 2. Avoid issues with late drm init - on some platforms exynos_drm device
> > > appeared too late, due to deferred probe, and resulted in black screen
> > > in userspace.
> > >
> > >
> > > Now if we want to convert exynos_dsi to drm_bridge I see following options:
> > >
> > > A. Forgot about callbacks and make the exynos_drm to defer probing until
> > > exynos_dsi bridge is available, probably it will cause later exynos_drm
> > > appearance, thus probably black screen on some targets. So for sure it
> > > will be suboptimal. Making it bridge unbind safe would be another
> > > problem, but most developers do not care about it so why should we? :)
> > >
> > > B. Try to mimic current behaviour - exynos_dsi register bridge ASAP,
> > > even if downstream devices are not yet attached, on attach/detach notify
> > > drm about it via connector status change, for this dsi_host registration
> > > should be performed from drm_bridge attach, I guess.
> > >
> > >
> > > Option A is more standard, but is unsafe and causes other issues.
> > >
> > > Option B keeps current behaviour.
> >
> > Maybe we can have both, but I am not sure, if I am missing something:
> >
> > I still prefer option A for the samsung-dsim driver, because it is more
> > standard, simpler and avoids issues with encoders, connectors or handling
> > hotplug.
> >
> > The idea is to use two bridges in the exynos-dsi driver: One bridge in the
> > samsung-dsim driver which implements option A and defers probing of the drm
> > driver until the next bridge is attached. And a second bridge in the
> > exynos_dsi that attaches to the first bridge (thus, allowing the exynos_drm
> > device to appear) and implements the hotplug handling for notifying drm via
> > connector status change.
> >
> > The driver for the i.MX8M would use the samsung-dsim bridge without an
> > additional bridge.
> >
> > This allows the samsung-dsim driver to expose the standard behavior while the
> > exynos_dsi may stick to the existing behavior for the exynos_drm driver.
> >
> > I hope this makes sense and does not sound too crazy. It might be difficult to
> > get the probing and mipi host/device registration correct, but I will try, if
> > this can work.
>
> Adding two bridges for being able to support hotplugging adds many special
> cases to the bridge driver and still requires more custom API to correctly add
> the second bridge. I don't think that this a viable path to go.

Just jumping in here: You cannot hotplug/hotremove anything from a
drm_device after drm_dev_register has been called, except
drm_connector. I didn't dig into details here so not sure whether you
want to late-bind your bridge after drm_dev_register is called or not,
so might just be fyi and not relevant to the discussion.
-Daniel

>
> This leaves us with:
>
> Option A) Standard drm_bridge behavior, which is currently implemented, but
> incompatible with the currently expected behavior of exynos_drm.
>
> Option B) Creating the drm device without all bridges being attached, which
> would work with the exynos_drm driver, but breaks for the standard drm_bridge
> behavior, especially, if the encoder/connector is created at the beginning of
> the pipeline and passed downwards when the bridges are attached.
>
> Option C) Extracting only low level register accesses into shared code, adding
> a custom interface and implementing the drm_bridge handling in the platform
> specific code.
>
> None of the options really convinces me.
>
> Michael
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel



-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 10/16] drm/exynos: implement a drm bridge
  2021-02-04 10:17                   ` Daniel Vetter
@ 2021-02-04 10:56                     ` Michael Tretter
  2021-02-04 16:05                       ` Daniel Vetter
  0 siblings, 1 reply; 61+ messages in thread
From: Michael Tretter @ 2021-02-04 10:56 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: Marek Vasut, aisheng.dong, linux-samsung-soc, ch, Neil Armstrong,
	Shawn Guo, dri-devel, Krzysztof Kozlowski, Seung-Woo Kim,
	dl-linux-imx, frieder.schrempf, abel.vesa, Andrzej Hajda,
	Bartlomiej Zolnierkiewicz, Laurent Pinchart, Sascha Hauer,
	Joonyoung Shim, sylvester.nawrocki, aford173, Marek Szyprowski

On Thu, 04 Feb 2021 11:17:49 +0100, Daniel Vetter wrote:
> On Wed, Feb 3, 2021 at 9:32 PM Michael Tretter <m.tretter@pengutronix.de> wrote:
> >
> > On Mon, 01 Feb 2021 17:33:14 +0100, Michael Tretter wrote:
> > > On Tue, 15 Sep 2020 21:40:40 +0200, Andrzej Hajda wrote:
> > > > W dniu 14.09.2020 o 23:19, Andrzej Hajda pisze:
> > > > > On 14.09.2020 22:01, Michael Tretter wrote:
> > > > >> On Mon, 14 Sep 2020 14:31:19 +0200, Marek Szyprowski wrote:
> > > > >>> On 14.09.2020 10:29, Marek Szyprowski wrote:
> > > > >>>> On 11.09.2020 15:54, Michael Tretter wrote:
> > > > >>>>> 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@pengutronix.de>
> > > > >>>> This one (and the whole series applied) still fails on Exynos boards:
> > > > >>>>
> > > > >>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping
> > > > >>>> operations
> > > > >>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> > > > >>>> OF: graph: no port node found in /soc/dsi@11c80000
> > > > >>>> 8<--- cut here ---
> > > > >>>> Unable to handle kernel NULL pointer dereference at virtual address
> > > > >>>> 00000084
> > > > >>>> pgd = (ptrval)
> > > > >>>> [00000084] *pgd=00000000
> > > > >>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> > > > >>>> Modules linked in:
> > > > >>>> CPU: 1 PID: 1 Comm: swapper/0 Not tainted
> > > > >>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec #1608
> > > > >>>> Hardware name: Samsung Exynos (Flattened Device Tree)
> > > > >>>> PC is at drm_bridge_attach+0x18/0x164
> > > > >>>> LR is at exynos_dsi_bind+0x88/0xa8
> > > > >>>> pc : [<c0628c08>]    lr : [<c064d560>]    psr: 20000013
> > > > >>>> sp : ef0dfca8  ip : 00000002  fp : c13190e0
> > > > >>>> r10: 00000000  r9 : ee46d580  r8 : c13190e0
> > > > >>>> r7 : ee438800  r6 : 00000018  r5 : ef253810  r4 : ef39e840
> > > > >>>> r3 : 00000000  r2 : 00000018  r1 : ef39e888  r0 : ef39e840
> > > > >>>> Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> > > > >>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> > > > >>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> > > > >>>> Stack: (0xef0dfca8 to 0xef0e0000)
> > > > >>>> ...
> > > > >>>> [<c0628c08>] (drm_bridge_attach) from [<c064d560>]
> > > > >>>> (exynos_dsi_bind+0x88/0xa8)
> > > > >>>> [<c064d560>] (exynos_dsi_bind) from [<c066a800>]
> > > > >>>> (component_bind_all+0xfc/0x290)
> > > > >>>> [<c066a800>] (component_bind_all) from [<c0649dc0>]
> > > > >>>> (exynos_drm_bind+0xe4/0x19c)
> > > > >>>> [<c0649dc0>] (exynos_drm_bind) from [<c066ad74>]
> > > > >>>> (try_to_bring_up_master+0x1e4/0x2c4)
> > > > >>>> [<c066ad74>] (try_to_bring_up_master) from [<c066b2b4>]
> > > > >>>> (component_master_add_with_match+0xd4/0x108)
> > > > >>>> [<c066b2b4>] (component_master_add_with_match) from [<c0649ae8>]
> > > > >>>> (exynos_drm_platform_probe+0xe4/0x110)
> > > > >>>> [<c0649ae8>] (exynos_drm_platform_probe) from [<c0674e6c>]
> > > > >>>> (platform_drv_probe+0x6c/0xa4)
> > > > >>>> [<c0674e6c>] (platform_drv_probe) from [<c067242c>]
> > > > >>>> (really_probe+0x200/0x4fc)
> > > > >>>> [<c067242c>] (really_probe) from [<c06728f0>]
> > > > >>>> (driver_probe_device+0x78/0x1fc)
> > > > >>>> [<c06728f0>] (driver_probe_device) from [<c0672cd8>]
> > > > >>>> (device_driver_attach+0x58/0x60)
> > > > >>>> [<c0672cd8>] (device_driver_attach) from [<c0672dbc>]
> > > > >>>> (__driver_attach+0xdc/0x174)
> > > > >>>> [<c0672dbc>] (__driver_attach) from [<c06701b4>]
> > > > >>>> (bus_for_each_dev+0x68/0xb4)
> > > > >>>> [<c06701b4>] (bus_for_each_dev) from [<c06714e8>]
> > > > >>>> (bus_add_driver+0x158/0x214)
> > > > >>>> [<c06714e8>] (bus_add_driver) from [<c0673c1c>]
> > > > >>>> (driver_register+0x78/0x110)
> > > > >>>> [<c0673c1c>] (driver_register) from [<c0649ca8>]
> > > > >>>> (exynos_drm_init+0xe4/0x118)
> > > > >>>> [<c0649ca8>] (exynos_drm_init) from [<c0102484>]
> > > > >>>> (do_one_initcall+0x8c/0x42c)
> > > > >>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
> > > > >>>> (kernel_init_freeable+0x190/0x1dc)
> > > > >>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7880>]
> > > > >>>> (kernel_init+0x8/0x118)
> > > > >>>> [<c0af7880>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> > > > >>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
> > > > >>>> ...
> > > > >>>> ---[ end trace ee27f313f9ed9da1 ]---
> > > > >>>>
> > > > >>>> # arm-linux-gnueabi-addr2line -e vmlinux c0628c08
> > > > >>>> drivers/gpu/drm/drm_bridge.c:184 (discriminator 1)
> > > > >>>>
> > > > >>>> I will try to debug it a bit more today.
> > > > >>> The above crash has been caused by lack of in_bridge initialization to
> > > > >>> NULL in exynos_dsi_bind() in this patch. However, fixing it reveals
> > > > >>> another issue:
> > > > >>>
> > > > >>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
> > > > >>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> > > > >>> OF: graph: no port node found in /soc/dsi@11c80000
> > > > >>> 8<--- cut here ---
> > > > >>> Unable to handle kernel NULL pointer dereference at virtual address
> > > > >>> 00000280
> > > > >>> pgd = (ptrval)
> > > > >>> [00000280] *pgd=00000000
> > > > >>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> > > > >>> Modules linked in:
> > > > >>> CPU: 0 PID: 1 Comm: swapper/0 Not tainted
> > > > >>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec-dirty #1613
> > > > >>> Hardware name: Samsung Exynos (Flattened Device Tree)
> > > > >>> PC is at __mutex_lock+0x54/0xb18
> > > > >>> LR is at lock_is_held_type+0x80/0x138
> > > > >>> pc : [<c0afc920>]    lr : [<c0af63e8>]    psr: 60000013
> > > > >>> sp : ef0dfd30  ip : 33937b74  fp : c13193c8
> > > > >>> r10: c1208eec  r9 : 00000000  r8 : ee45f808
> > > > >>> r7 : c19561a4  r6 : 00000000  r5 : 00000000  r4 : 0000024c
> > > > >>> r3 : 00000000  r2 : 00204140  r1 : c124f13c  r0 : 00000000
> > > > >>> Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> > > > >>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> > > > >>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> > > > >>> Stack: (0xef0dfd30 to 0xef0e0000)
> > > > >>> ...
> > > > >>> [<c0afc920>] (__mutex_lock) from [<c0afd400>]
> > > > >>> (mutex_lock_nested+0x1c/0x24)
> > > > >>> [<c0afd400>] (mutex_lock_nested) from [<c064d4b8>]
> > > > >>> (__exynos_dsi_host_attach+0x20/0x6c)
> > > > >>> [<c064d4b8>] (__exynos_dsi_host_attach) from [<c064d914>]
> > > > >>> (exynos_dsi_host_attach+0x70/0x194)
> > > > >>> [<c064d914>] (exynos_dsi_host_attach) from [<c0656b64>]
> > > > >>> (s6e8aa0_probe+0x1b0/0x218)
> > > > >>> [<c0656b64>] (s6e8aa0_probe) from [<c0672530>]
> > > > >>> (really_probe+0x200/0x4fc)
> > > > >>> [<c0672530>] (really_probe) from [<c06729f4>]
> > > > >>> (driver_probe_device+0x78/0x1fc)
> > > > >>> [<c06729f4>] (driver_probe_device) from [<c0672ddc>]
> > > > >>> (device_driver_attach+0x58/0x60)
> > > > >>> [<c0672ddc>] (device_driver_attach) from [<c0672ec0>]
> > > > >>> (__driver_attach+0xdc/0x174)
> > > > >>> [<c0672ec0>] (__driver_attach) from [<c06702b8>]
> > > > >>> (bus_for_each_dev+0x68/0xb4)
> > > > >>> [<c06702b8>] (bus_for_each_dev) from [<c06715ec>]
> > > > >>> (bus_add_driver+0x158/0x214)
> > > > >>> [<c06715ec>] (bus_add_driver) from [<c0673d20>]
> > > > >>> (driver_register+0x78/0x110)
> > > > >>> [<c0673d20>] (driver_register) from [<c0102484>]
> > > > >>> (do_one_initcall+0x8c/0x42c)
> > > > >>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
> > > > >>> (kernel_init_freeable+0x190/0x1dc)
> > > > >>> [<c11011c0>] (kernel_init_freeable) from [<c0af7988>]
> > > > >>> (kernel_init+0x8/0x118)
> > > > >>> [<c0af7988>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> > > > >>> Exception stack(0xef0dffb0 to 0xef0dfff8)
> > > > >>> ...
> > > > >>> ---[ end trace c06e996ec2e8234d ]---
> > > > >>>
> > > > >>> This means that dsi->encoder.dev is not initialized in
> > > > >>> __exynos_dsi_host_attach().
> > > > >>>
> > > > >>> This happens, because drm_bridge_attach() in exynos_dsi_bind() returned
> > > > >>> earlier -517 (deferred probe), what causes cleanup of encoder and
> > > > >>> release of all drm resources.
> > > > >>>
> > > > >>> Then however, the panel tries to register itself and
> > > > >>> exynos_dsi_host_attach() tries to access the released encoder (which is
> > > > >>> zeroed in drm_encoder_release) and rest of resources, what causes
> > > > >>> failure.
> > > > >>>
> > > > >>> It looks that something is missing. Maybe mipi host has to be
> > > > >>> registered
> > > > >>> later, when bridge is ready? I have no idea how it is handled before
> > > > >>> this patch. Andrzej, could you comment it a bit?
> > > > >> I intentionally changed the order, because if another bridge follows
> > > > >> in the
> > > > >> pipeline, the probe of the drm driver has to be deferred until some
> > > > >> bridge
> > > > >> provides a connector. The next bridge registers itself via the
> > > > >> host_attach
> > > > >> function and the deferral is ensured via the bind for the bind/unbind
> > > > >> API or
> > > > >> the bridge_attach function otherwise.
> > > > >>
> > > > >> On the other hand, the bridge does not have an encoder until the mipi
> > > > >> device
> > > > >> has been attached.
> > > > >>
> > > > >> As a solution, the exynos dsi driver must initialize the encoder in
> > > > >> exynos_dsi_probe instead of in exynos_dsi_bind and access the encoder
> > > > >> via
> > > > >> exynos_dsi instead of the bridge.
> > > > >>
> > > > >> Can you try to move everything except samsung_dsim_bind from
> > > > >> exynos_dsi_bind
> > > > >> to exynos_dsi_probe (respectively for unbind) and report if it fixes the
> > > > >> crash.
> > > > >
> > > > >
> > > > > The original behaviour is that encoder (exynos_dsi) is registered
> > > > > regardless of sink presence (initially panel, later also bridge) - it
> > > > > avoids multiple issues with deferred probe, device driver bind/unbind
> > > > > and module load/unload. Appearance or disappearance of sink is
> > > > > reported to host nicely via DSI attach/detach callbacks - and it is
> > > > > reflected in drm world as change state of the connector.
> > > > >
> > > > > Registering DSI host in bind and unregistering in unbind assures that
> > > > > if mipi_dsi device is attached/detached the drm device is always
> > > > > present - it makes device/driver binding race free and allows to avoid
> > > > > additional locking.
> > > > >
> > > > > Moving DSI host registration to probe changes everything, for sure it
> > > > > breaks the nice feature of DSI attach/detach callbacks and apparently
> > > > > can cause different issues depending on device bind order.
> > > > >
> > > > > I will try to look at the patches tomorrow and maybe I can find more
> > > > > constructive comments :)
> > > >
> > > >
> > > > As I said yesterday, exynos_dsi driver uses dsi host attach/detach
> > > > callbacks to control appearance/disappearance of downstream device. It
> > > > allows to:
> > > >
> > > > 1. Safely bind/unbind different device drivers at any time and at any
> > > > order, without killing exynos_drm and/or crashing system.
> > > >
> > > > 2. Avoid issues with late drm init - on some platforms exynos_drm device
> > > > appeared too late, due to deferred probe, and resulted in black screen
> > > > in userspace.
> > > >
> > > >
> > > > Now if we want to convert exynos_dsi to drm_bridge I see following options:
> > > >
> > > > A. Forgot about callbacks and make the exynos_drm to defer probing until
> > > > exynos_dsi bridge is available, probably it will cause later exynos_drm
> > > > appearance, thus probably black screen on some targets. So for sure it
> > > > will be suboptimal. Making it bridge unbind safe would be another
> > > > problem, but most developers do not care about it so why should we? :)
> > > >
> > > > B. Try to mimic current behaviour - exynos_dsi register bridge ASAP,
> > > > even if downstream devices are not yet attached, on attach/detach notify
> > > > drm about it via connector status change, for this dsi_host registration
> > > > should be performed from drm_bridge attach, I guess.
> > > >
> > > >
> > > > Option A is more standard, but is unsafe and causes other issues.
> > > >
> > > > Option B keeps current behaviour.
> > >
> > > Maybe we can have both, but I am not sure, if I am missing something:
> > >
> > > I still prefer option A for the samsung-dsim driver, because it is more
> > > standard, simpler and avoids issues with encoders, connectors or handling
> > > hotplug.
> > >
> > > The idea is to use two bridges in the exynos-dsi driver: One bridge in the
> > > samsung-dsim driver which implements option A and defers probing of the drm
> > > driver until the next bridge is attached. And a second bridge in the
> > > exynos_dsi that attaches to the first bridge (thus, allowing the exynos_drm
> > > device to appear) and implements the hotplug handling for notifying drm via
> > > connector status change.
> > >
> > > The driver for the i.MX8M would use the samsung-dsim bridge without an
> > > additional bridge.
> > >
> > > This allows the samsung-dsim driver to expose the standard behavior while the
> > > exynos_dsi may stick to the existing behavior for the exynos_drm driver.
> > >
> > > I hope this makes sense and does not sound too crazy. It might be difficult to
> > > get the probing and mipi host/device registration correct, but I will try, if
> > > this can work.
> >
> > Adding two bridges for being able to support hotplugging adds many special
> > cases to the bridge driver and still requires more custom API to correctly add
> > the second bridge. I don't think that this a viable path to go.
> 
> Just jumping in here: You cannot hotplug/hotremove anything from a
> drm_device after drm_dev_register has been called, except
> drm_connector. I didn't dig into details here so not sure whether you
> want to late-bind your bridge after drm_dev_register is called or not,
> so might just be fyi and not relevant to the discussion.

Thanks. AFAIC that is exactly what is currently implemented in the exynos_drm
driver (i.e. Option B)

exynos_dsi_bind configures the encoder and registers a DSI host. Afterwards,
exynos_drm_bind (as component_master_ops) calls drm_dev_register. Later, a DSI
device might attach to the DSI host and call exynos_dsi_host_attach. In
exynos_dsi_host_attach, the driver finds the drm_bridge for the DSI device and
attaches this bridge to the encoder _after_ drm_dev_register has been called.
This is invalid behavior, right?

Michael

> -Daniel
> 
> >
> > This leaves us with:
> >
> > Option A) Standard drm_bridge behavior, which is currently implemented, but
> > incompatible with the currently expected behavior of exynos_drm.
> >
> > Option B) Creating the drm device without all bridges being attached, which
> > would work with the exynos_drm driver, but breaks for the standard drm_bridge
> > behavior, especially, if the encoder/connector is created at the beginning of
> > the pipeline and passed downwards when the bridges are attached.
> >
> > Option C) Extracting only low level register accesses into shared code, adding
> > a custom interface and implementing the drm_bridge handling in the platform
> > specific code.
> >
> > None of the options really convinces me.
> >
> > Michael
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/dri-devel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 10/16] drm/exynos: implement a drm bridge
  2021-02-04 10:56                     ` Michael Tretter
@ 2021-02-04 16:05                       ` Daniel Vetter
  2021-02-04 16:28                         ` Andrzej Hajda
  0 siblings, 1 reply; 61+ messages in thread
From: Daniel Vetter @ 2021-02-04 16:05 UTC (permalink / raw)
  To: Michael Tretter
  Cc: Marek Vasut, aisheng.dong, linux-samsung-soc, ch, Neil Armstrong,
	Shawn Guo, Krzysztof Kozlowski, Seung-Woo Kim, dl-linux-imx,
	frieder.schrempf, abel.vesa, Andrzej Hajda,
	Bartlomiej Zolnierkiewicz, dri-devel, Joonyoung Shim,
	sylvester.nawrocki, Marek Szyprowski, aford173, Sascha Hauer,
	Laurent Pinchart

On Thu, Feb 04, 2021 at 11:56:32AM +0100, Michael Tretter wrote:
> On Thu, 04 Feb 2021 11:17:49 +0100, Daniel Vetter wrote:
> > On Wed, Feb 3, 2021 at 9:32 PM Michael Tretter <m.tretter@pengutronix.de> wrote:
> > >
> > > On Mon, 01 Feb 2021 17:33:14 +0100, Michael Tretter wrote:
> > > > On Tue, 15 Sep 2020 21:40:40 +0200, Andrzej Hajda wrote:
> > > > > W dniu 14.09.2020 o 23:19, Andrzej Hajda pisze:
> > > > > > On 14.09.2020 22:01, Michael Tretter wrote:
> > > > > >> On Mon, 14 Sep 2020 14:31:19 +0200, Marek Szyprowski wrote:
> > > > > >>> On 14.09.2020 10:29, Marek Szyprowski wrote:
> > > > > >>>> On 11.09.2020 15:54, Michael Tretter wrote:
> > > > > >>>>> 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@pengutronix.de>
> > > > > >>>> This one (and the whole series applied) still fails on Exynos boards:
> > > > > >>>>
> > > > > >>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping
> > > > > >>>> operations
> > > > > >>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> > > > > >>>> OF: graph: no port node found in /soc/dsi@11c80000
> > > > > >>>> 8<--- cut here ---
> > > > > >>>> Unable to handle kernel NULL pointer dereference at virtual address
> > > > > >>>> 00000084
> > > > > >>>> pgd = (ptrval)
> > > > > >>>> [00000084] *pgd=00000000
> > > > > >>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> > > > > >>>> Modules linked in:
> > > > > >>>> CPU: 1 PID: 1 Comm: swapper/0 Not tainted
> > > > > >>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec #1608
> > > > > >>>> Hardware name: Samsung Exynos (Flattened Device Tree)
> > > > > >>>> PC is at drm_bridge_attach+0x18/0x164
> > > > > >>>> LR is at exynos_dsi_bind+0x88/0xa8
> > > > > >>>> pc : [<c0628c08>]    lr : [<c064d560>]    psr: 20000013
> > > > > >>>> sp : ef0dfca8  ip : 00000002  fp : c13190e0
> > > > > >>>> r10: 00000000  r9 : ee46d580  r8 : c13190e0
> > > > > >>>> r7 : ee438800  r6 : 00000018  r5 : ef253810  r4 : ef39e840
> > > > > >>>> r3 : 00000000  r2 : 00000018  r1 : ef39e888  r0 : ef39e840
> > > > > >>>> Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> > > > > >>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> > > > > >>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> > > > > >>>> Stack: (0xef0dfca8 to 0xef0e0000)
> > > > > >>>> ...
> > > > > >>>> [<c0628c08>] (drm_bridge_attach) from [<c064d560>]
> > > > > >>>> (exynos_dsi_bind+0x88/0xa8)
> > > > > >>>> [<c064d560>] (exynos_dsi_bind) from [<c066a800>]
> > > > > >>>> (component_bind_all+0xfc/0x290)
> > > > > >>>> [<c066a800>] (component_bind_all) from [<c0649dc0>]
> > > > > >>>> (exynos_drm_bind+0xe4/0x19c)
> > > > > >>>> [<c0649dc0>] (exynos_drm_bind) from [<c066ad74>]
> > > > > >>>> (try_to_bring_up_master+0x1e4/0x2c4)
> > > > > >>>> [<c066ad74>] (try_to_bring_up_master) from [<c066b2b4>]
> > > > > >>>> (component_master_add_with_match+0xd4/0x108)
> > > > > >>>> [<c066b2b4>] (component_master_add_with_match) from [<c0649ae8>]
> > > > > >>>> (exynos_drm_platform_probe+0xe4/0x110)
> > > > > >>>> [<c0649ae8>] (exynos_drm_platform_probe) from [<c0674e6c>]
> > > > > >>>> (platform_drv_probe+0x6c/0xa4)
> > > > > >>>> [<c0674e6c>] (platform_drv_probe) from [<c067242c>]
> > > > > >>>> (really_probe+0x200/0x4fc)
> > > > > >>>> [<c067242c>] (really_probe) from [<c06728f0>]
> > > > > >>>> (driver_probe_device+0x78/0x1fc)
> > > > > >>>> [<c06728f0>] (driver_probe_device) from [<c0672cd8>]
> > > > > >>>> (device_driver_attach+0x58/0x60)
> > > > > >>>> [<c0672cd8>] (device_driver_attach) from [<c0672dbc>]
> > > > > >>>> (__driver_attach+0xdc/0x174)
> > > > > >>>> [<c0672dbc>] (__driver_attach) from [<c06701b4>]
> > > > > >>>> (bus_for_each_dev+0x68/0xb4)
> > > > > >>>> [<c06701b4>] (bus_for_each_dev) from [<c06714e8>]
> > > > > >>>> (bus_add_driver+0x158/0x214)
> > > > > >>>> [<c06714e8>] (bus_add_driver) from [<c0673c1c>]
> > > > > >>>> (driver_register+0x78/0x110)
> > > > > >>>> [<c0673c1c>] (driver_register) from [<c0649ca8>]
> > > > > >>>> (exynos_drm_init+0xe4/0x118)
> > > > > >>>> [<c0649ca8>] (exynos_drm_init) from [<c0102484>]
> > > > > >>>> (do_one_initcall+0x8c/0x42c)
> > > > > >>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
> > > > > >>>> (kernel_init_freeable+0x190/0x1dc)
> > > > > >>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7880>]
> > > > > >>>> (kernel_init+0x8/0x118)
> > > > > >>>> [<c0af7880>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> > > > > >>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
> > > > > >>>> ...
> > > > > >>>> ---[ end trace ee27f313f9ed9da1 ]---
> > > > > >>>>
> > > > > >>>> # arm-linux-gnueabi-addr2line -e vmlinux c0628c08
> > > > > >>>> drivers/gpu/drm/drm_bridge.c:184 (discriminator 1)
> > > > > >>>>
> > > > > >>>> I will try to debug it a bit more today.
> > > > > >>> The above crash has been caused by lack of in_bridge initialization to
> > > > > >>> NULL in exynos_dsi_bind() in this patch. However, fixing it reveals
> > > > > >>> another issue:
> > > > > >>>
> > > > > >>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
> > > > > >>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> > > > > >>> OF: graph: no port node found in /soc/dsi@11c80000
> > > > > >>> 8<--- cut here ---
> > > > > >>> Unable to handle kernel NULL pointer dereference at virtual address
> > > > > >>> 00000280
> > > > > >>> pgd = (ptrval)
> > > > > >>> [00000280] *pgd=00000000
> > > > > >>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> > > > > >>> Modules linked in:
> > > > > >>> CPU: 0 PID: 1 Comm: swapper/0 Not tainted
> > > > > >>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec-dirty #1613
> > > > > >>> Hardware name: Samsung Exynos (Flattened Device Tree)
> > > > > >>> PC is at __mutex_lock+0x54/0xb18
> > > > > >>> LR is at lock_is_held_type+0x80/0x138
> > > > > >>> pc : [<c0afc920>]    lr : [<c0af63e8>]    psr: 60000013
> > > > > >>> sp : ef0dfd30  ip : 33937b74  fp : c13193c8
> > > > > >>> r10: c1208eec  r9 : 00000000  r8 : ee45f808
> > > > > >>> r7 : c19561a4  r6 : 00000000  r5 : 00000000  r4 : 0000024c
> > > > > >>> r3 : 00000000  r2 : 00204140  r1 : c124f13c  r0 : 00000000
> > > > > >>> Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> > > > > >>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> > > > > >>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> > > > > >>> Stack: (0xef0dfd30 to 0xef0e0000)
> > > > > >>> ...
> > > > > >>> [<c0afc920>] (__mutex_lock) from [<c0afd400>]
> > > > > >>> (mutex_lock_nested+0x1c/0x24)
> > > > > >>> [<c0afd400>] (mutex_lock_nested) from [<c064d4b8>]
> > > > > >>> (__exynos_dsi_host_attach+0x20/0x6c)
> > > > > >>> [<c064d4b8>] (__exynos_dsi_host_attach) from [<c064d914>]
> > > > > >>> (exynos_dsi_host_attach+0x70/0x194)
> > > > > >>> [<c064d914>] (exynos_dsi_host_attach) from [<c0656b64>]
> > > > > >>> (s6e8aa0_probe+0x1b0/0x218)
> > > > > >>> [<c0656b64>] (s6e8aa0_probe) from [<c0672530>]
> > > > > >>> (really_probe+0x200/0x4fc)
> > > > > >>> [<c0672530>] (really_probe) from [<c06729f4>]
> > > > > >>> (driver_probe_device+0x78/0x1fc)
> > > > > >>> [<c06729f4>] (driver_probe_device) from [<c0672ddc>]
> > > > > >>> (device_driver_attach+0x58/0x60)
> > > > > >>> [<c0672ddc>] (device_driver_attach) from [<c0672ec0>]
> > > > > >>> (__driver_attach+0xdc/0x174)
> > > > > >>> [<c0672ec0>] (__driver_attach) from [<c06702b8>]
> > > > > >>> (bus_for_each_dev+0x68/0xb4)
> > > > > >>> [<c06702b8>] (bus_for_each_dev) from [<c06715ec>]
> > > > > >>> (bus_add_driver+0x158/0x214)
> > > > > >>> [<c06715ec>] (bus_add_driver) from [<c0673d20>]
> > > > > >>> (driver_register+0x78/0x110)
> > > > > >>> [<c0673d20>] (driver_register) from [<c0102484>]
> > > > > >>> (do_one_initcall+0x8c/0x42c)
> > > > > >>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
> > > > > >>> (kernel_init_freeable+0x190/0x1dc)
> > > > > >>> [<c11011c0>] (kernel_init_freeable) from [<c0af7988>]
> > > > > >>> (kernel_init+0x8/0x118)
> > > > > >>> [<c0af7988>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> > > > > >>> Exception stack(0xef0dffb0 to 0xef0dfff8)
> > > > > >>> ...
> > > > > >>> ---[ end trace c06e996ec2e8234d ]---
> > > > > >>>
> > > > > >>> This means that dsi->encoder.dev is not initialized in
> > > > > >>> __exynos_dsi_host_attach().
> > > > > >>>
> > > > > >>> This happens, because drm_bridge_attach() in exynos_dsi_bind() returned
> > > > > >>> earlier -517 (deferred probe), what causes cleanup of encoder and
> > > > > >>> release of all drm resources.
> > > > > >>>
> > > > > >>> Then however, the panel tries to register itself and
> > > > > >>> exynos_dsi_host_attach() tries to access the released encoder (which is
> > > > > >>> zeroed in drm_encoder_release) and rest of resources, what causes
> > > > > >>> failure.
> > > > > >>>
> > > > > >>> It looks that something is missing. Maybe mipi host has to be
> > > > > >>> registered
> > > > > >>> later, when bridge is ready? I have no idea how it is handled before
> > > > > >>> this patch. Andrzej, could you comment it a bit?
> > > > > >> I intentionally changed the order, because if another bridge follows
> > > > > >> in the
> > > > > >> pipeline, the probe of the drm driver has to be deferred until some
> > > > > >> bridge
> > > > > >> provides a connector. The next bridge registers itself via the
> > > > > >> host_attach
> > > > > >> function and the deferral is ensured via the bind for the bind/unbind
> > > > > >> API or
> > > > > >> the bridge_attach function otherwise.
> > > > > >>
> > > > > >> On the other hand, the bridge does not have an encoder until the mipi
> > > > > >> device
> > > > > >> has been attached.
> > > > > >>
> > > > > >> As a solution, the exynos dsi driver must initialize the encoder in
> > > > > >> exynos_dsi_probe instead of in exynos_dsi_bind and access the encoder
> > > > > >> via
> > > > > >> exynos_dsi instead of the bridge.
> > > > > >>
> > > > > >> Can you try to move everything except samsung_dsim_bind from
> > > > > >> exynos_dsi_bind
> > > > > >> to exynos_dsi_probe (respectively for unbind) and report if it fixes the
> > > > > >> crash.
> > > > > >
> > > > > >
> > > > > > The original behaviour is that encoder (exynos_dsi) is registered
> > > > > > regardless of sink presence (initially panel, later also bridge) - it
> > > > > > avoids multiple issues with deferred probe, device driver bind/unbind
> > > > > > and module load/unload. Appearance or disappearance of sink is
> > > > > > reported to host nicely via DSI attach/detach callbacks - and it is
> > > > > > reflected in drm world as change state of the connector.
> > > > > >
> > > > > > Registering DSI host in bind and unregistering in unbind assures that
> > > > > > if mipi_dsi device is attached/detached the drm device is always
> > > > > > present - it makes device/driver binding race free and allows to avoid
> > > > > > additional locking.
> > > > > >
> > > > > > Moving DSI host registration to probe changes everything, for sure it
> > > > > > breaks the nice feature of DSI attach/detach callbacks and apparently
> > > > > > can cause different issues depending on device bind order.
> > > > > >
> > > > > > I will try to look at the patches tomorrow and maybe I can find more
> > > > > > constructive comments :)
> > > > >
> > > > >
> > > > > As I said yesterday, exynos_dsi driver uses dsi host attach/detach
> > > > > callbacks to control appearance/disappearance of downstream device. It
> > > > > allows to:
> > > > >
> > > > > 1. Safely bind/unbind different device drivers at any time and at any
> > > > > order, without killing exynos_drm and/or crashing system.
> > > > >
> > > > > 2. Avoid issues with late drm init - on some platforms exynos_drm device
> > > > > appeared too late, due to deferred probe, and resulted in black screen
> > > > > in userspace.
> > > > >
> > > > >
> > > > > Now if we want to convert exynos_dsi to drm_bridge I see following options:
> > > > >
> > > > > A. Forgot about callbacks and make the exynos_drm to defer probing until
> > > > > exynos_dsi bridge is available, probably it will cause later exynos_drm
> > > > > appearance, thus probably black screen on some targets. So for sure it
> > > > > will be suboptimal. Making it bridge unbind safe would be another
> > > > > problem, but most developers do not care about it so why should we? :)
> > > > >
> > > > > B. Try to mimic current behaviour - exynos_dsi register bridge ASAP,
> > > > > even if downstream devices are not yet attached, on attach/detach notify
> > > > > drm about it via connector status change, for this dsi_host registration
> > > > > should be performed from drm_bridge attach, I guess.
> > > > >
> > > > >
> > > > > Option A is more standard, but is unsafe and causes other issues.
> > > > >
> > > > > Option B keeps current behaviour.
> > > >
> > > > Maybe we can have both, but I am not sure, if I am missing something:
> > > >
> > > > I still prefer option A for the samsung-dsim driver, because it is more
> > > > standard, simpler and avoids issues with encoders, connectors or handling
> > > > hotplug.
> > > >
> > > > The idea is to use two bridges in the exynos-dsi driver: One bridge in the
> > > > samsung-dsim driver which implements option A and defers probing of the drm
> > > > driver until the next bridge is attached. And a second bridge in the
> > > > exynos_dsi that attaches to the first bridge (thus, allowing the exynos_drm
> > > > device to appear) and implements the hotplug handling for notifying drm via
> > > > connector status change.
> > > >
> > > > The driver for the i.MX8M would use the samsung-dsim bridge without an
> > > > additional bridge.
> > > >
> > > > This allows the samsung-dsim driver to expose the standard behavior while the
> > > > exynos_dsi may stick to the existing behavior for the exynos_drm driver.
> > > >
> > > > I hope this makes sense and does not sound too crazy. It might be difficult to
> > > > get the probing and mipi host/device registration correct, but I will try, if
> > > > this can work.
> > >
> > > Adding two bridges for being able to support hotplugging adds many special
> > > cases to the bridge driver and still requires more custom API to correctly add
> > > the second bridge. I don't think that this a viable path to go.
> > 
> > Just jumping in here: You cannot hotplug/hotremove anything from a
> > drm_device after drm_dev_register has been called, except
> > drm_connector. I didn't dig into details here so not sure whether you
> > want to late-bind your bridge after drm_dev_register is called or not,
> > so might just be fyi and not relevant to the discussion.
> 
> Thanks. AFAIC that is exactly what is currently implemented in the exynos_drm
> driver (i.e. Option B)
> 
> exynos_dsi_bind configures the encoder and registers a DSI host. Afterwards,
> exynos_drm_bind (as component_master_ops) calls drm_dev_register. Later, a DSI
> device might attach to the DSI host and call exynos_dsi_host_attach. In
> exynos_dsi_host_attach, the driver finds the drm_bridge for the DSI device and
> attaches this bridge to the encoder _after_ drm_dev_register has been called.
> This is invalid behavior, right?

Definitely not supported, I don't think we have the right locks in place
to make sure this works.

Now if your _only_ adding a drm_bridge (and not an encoder or anything
like that), and you are adding the drm_connector correctly (like a
hotplugged DP MST sink), then that would at least work from a uapi pov.
Because drm_bridge isn't exposed as an uapi object.

But yeah, as-is, don't :-)

The solution here is a bunch of EPROBE_DEFER handling until all your
bridges are loaded, with or without the assistance of component.c
framework. Only then call drm_dev_register.
-Daniel

> 
> Michael
> 
> > -Daniel
> > 
> > >
> > > This leaves us with:
> > >
> > > Option A) Standard drm_bridge behavior, which is currently implemented, but
> > > incompatible with the currently expected behavior of exynos_drm.
> > >
> > > Option B) Creating the drm device without all bridges being attached, which
> > > would work with the exynos_drm driver, but breaks for the standard drm_bridge
> > > behavior, especially, if the encoder/connector is created at the beginning of
> > > the pipeline and passed downwards when the bridges are attached.
> > >
> > > Option C) Extracting only low level register accesses into shared code, adding
> > > a custom interface and implementing the drm_bridge handling in the platform
> > > specific code.
> > >
> > > None of the options really convinces me.
> > >
> > > Michael
> > > _______________________________________________
> > > dri-devel mailing list
> > > dri-devel@lists.freedesktop.org
> > > https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 10/16] drm/exynos: implement a drm bridge
  2021-02-04 16:05                       ` Daniel Vetter
@ 2021-02-04 16:28                         ` Andrzej Hajda
  2021-02-04 17:19                           ` Daniel Vetter
  0 siblings, 1 reply; 61+ messages in thread
From: Andrzej Hajda @ 2021-02-04 16:28 UTC (permalink / raw)
  To: Daniel Vetter, Michael Tretter
  Cc: Marek Vasut, aisheng.dong, linux-samsung-soc, Laurent Pinchart,
	ch, Neil Armstrong, aford173, dri-devel,
	Bartlomiej Zolnierkiewicz, Seung-Woo Kim, Krzysztof Kozlowski,
	frieder.schrempf, dl-linux-imx, Sascha Hauer, Joonyoung Shim,
	sylvester.nawrocki, Marek Szyprowski, Shawn Guo, abel.vesa


W dniu 04.02.2021 o 17:05, Daniel Vetter pisze:
> On Thu, Feb 04, 2021 at 11:56:32AM +0100, Michael Tretter wrote:
>> On Thu, 04 Feb 2021 11:17:49 +0100, Daniel Vetter wrote:
>>> On Wed, Feb 3, 2021 at 9:32 PM Michael Tretter <m.tretter@pengutronix.de> wrote:
>>>> On Mon, 01 Feb 2021 17:33:14 +0100, Michael Tretter wrote:
>>>>> On Tue, 15 Sep 2020 21:40:40 +0200, Andrzej Hajda wrote:
>>>>>> W dniu 14.09.2020 o 23:19, Andrzej Hajda pisze:
>>>>>>> On 14.09.2020 22:01, Michael Tretter wrote:
>>>>>>>> On Mon, 14 Sep 2020 14:31:19 +0200, Marek Szyprowski wrote:
>>>>>>>>> On 14.09.2020 10:29, Marek Szyprowski wrote:
>>>>>>>>>> On 11.09.2020 15:54, Michael Tretter wrote:
>>>>>>>>>>> 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@pengutronix.de>
>>>>>>>>>> This one (and the whole series applied) still fails on Exynos boards:
>>>>>>>>>>
>>>>>>>>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping
>>>>>>>>>> operations
>>>>>>>>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
>>>>>>>>>> OF: graph: no port node found in /soc/dsi@11c80000
>>>>>>>>>> 8<--- cut here ---
>>>>>>>>>> Unable to handle kernel NULL pointer dereference at virtual address
>>>>>>>>>> 00000084
>>>>>>>>>> pgd = (ptrval)
>>>>>>>>>> [00000084] *pgd=00000000
>>>>>>>>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
>>>>>>>>>> Modules linked in:
>>>>>>>>>> CPU: 1 PID: 1 Comm: swapper/0 Not tainted
>>>>>>>>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec #1608
>>>>>>>>>> Hardware name: Samsung Exynos (Flattened Device Tree)
>>>>>>>>>> PC is at drm_bridge_attach+0x18/0x164
>>>>>>>>>> LR is at exynos_dsi_bind+0x88/0xa8
>>>>>>>>>> pc : [<c0628c08>]    lr : [<c064d560>]    psr: 20000013
>>>>>>>>>> sp : ef0dfca8  ip : 00000002  fp : c13190e0
>>>>>>>>>> r10: 00000000  r9 : ee46d580  r8 : c13190e0
>>>>>>>>>> r7 : ee438800  r6 : 00000018  r5 : ef253810  r4 : ef39e840
>>>>>>>>>> r3 : 00000000  r2 : 00000018  r1 : ef39e888  r0 : ef39e840
>>>>>>>>>> Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
>>>>>>>>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
>>>>>>>>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
>>>>>>>>>> Stack: (0xef0dfca8 to 0xef0e0000)
>>>>>>>>>> ...
>>>>>>>>>> [<c0628c08>] (drm_bridge_attach) from [<c064d560>]
>>>>>>>>>> (exynos_dsi_bind+0x88/0xa8)
>>>>>>>>>> [<c064d560>] (exynos_dsi_bind) from [<c066a800>]
>>>>>>>>>> (component_bind_all+0xfc/0x290)
>>>>>>>>>> [<c066a800>] (component_bind_all) from [<c0649dc0>]
>>>>>>>>>> (exynos_drm_bind+0xe4/0x19c)
>>>>>>>>>> [<c0649dc0>] (exynos_drm_bind) from [<c066ad74>]
>>>>>>>>>> (try_to_bring_up_master+0x1e4/0x2c4)
>>>>>>>>>> [<c066ad74>] (try_to_bring_up_master) from [<c066b2b4>]
>>>>>>>>>> (component_master_add_with_match+0xd4/0x108)
>>>>>>>>>> [<c066b2b4>] (component_master_add_with_match) from [<c0649ae8>]
>>>>>>>>>> (exynos_drm_platform_probe+0xe4/0x110)
>>>>>>>>>> [<c0649ae8>] (exynos_drm_platform_probe) from [<c0674e6c>]
>>>>>>>>>> (platform_drv_probe+0x6c/0xa4)
>>>>>>>>>> [<c0674e6c>] (platform_drv_probe) from [<c067242c>]
>>>>>>>>>> (really_probe+0x200/0x4fc)
>>>>>>>>>> [<c067242c>] (really_probe) from [<c06728f0>]
>>>>>>>>>> (driver_probe_device+0x78/0x1fc)
>>>>>>>>>> [<c06728f0>] (driver_probe_device) from [<c0672cd8>]
>>>>>>>>>> (device_driver_attach+0x58/0x60)
>>>>>>>>>> [<c0672cd8>] (device_driver_attach) from [<c0672dbc>]
>>>>>>>>>> (__driver_attach+0xdc/0x174)
>>>>>>>>>> [<c0672dbc>] (__driver_attach) from [<c06701b4>]
>>>>>>>>>> (bus_for_each_dev+0x68/0xb4)
>>>>>>>>>> [<c06701b4>] (bus_for_each_dev) from [<c06714e8>]
>>>>>>>>>> (bus_add_driver+0x158/0x214)
>>>>>>>>>> [<c06714e8>] (bus_add_driver) from [<c0673c1c>]
>>>>>>>>>> (driver_register+0x78/0x110)
>>>>>>>>>> [<c0673c1c>] (driver_register) from [<c0649ca8>]
>>>>>>>>>> (exynos_drm_init+0xe4/0x118)
>>>>>>>>>> [<c0649ca8>] (exynos_drm_init) from [<c0102484>]
>>>>>>>>>> (do_one_initcall+0x8c/0x42c)
>>>>>>>>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
>>>>>>>>>> (kernel_init_freeable+0x190/0x1dc)
>>>>>>>>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7880>]
>>>>>>>>>> (kernel_init+0x8/0x118)
>>>>>>>>>> [<c0af7880>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
>>>>>>>>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
>>>>>>>>>> ...
>>>>>>>>>> ---[ end trace ee27f313f9ed9da1 ]---
>>>>>>>>>>
>>>>>>>>>> # arm-linux-gnueabi-addr2line -e vmlinux c0628c08
>>>>>>>>>> drivers/gpu/drm/drm_bridge.c:184 (discriminator 1)
>>>>>>>>>>
>>>>>>>>>> I will try to debug it a bit more today.
>>>>>>>>> The above crash has been caused by lack of in_bridge initialization to
>>>>>>>>> NULL in exynos_dsi_bind() in this patch. However, fixing it reveals
>>>>>>>>> another issue:
>>>>>>>>>
>>>>>>>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
>>>>>>>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
>>>>>>>>> OF: graph: no port node found in /soc/dsi@11c80000
>>>>>>>>> 8<--- cut here ---
>>>>>>>>> Unable to handle kernel NULL pointer dereference at virtual address
>>>>>>>>> 00000280
>>>>>>>>> pgd = (ptrval)
>>>>>>>>> [00000280] *pgd=00000000
>>>>>>>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
>>>>>>>>> Modules linked in:
>>>>>>>>> CPU: 0 PID: 1 Comm: swapper/0 Not tainted
>>>>>>>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec-dirty #1613
>>>>>>>>> Hardware name: Samsung Exynos (Flattened Device Tree)
>>>>>>>>> PC is at __mutex_lock+0x54/0xb18
>>>>>>>>> LR is at lock_is_held_type+0x80/0x138
>>>>>>>>> pc : [<c0afc920>]    lr : [<c0af63e8>]    psr: 60000013
>>>>>>>>> sp : ef0dfd30  ip : 33937b74  fp : c13193c8
>>>>>>>>> r10: c1208eec  r9 : 00000000  r8 : ee45f808
>>>>>>>>> r7 : c19561a4  r6 : 00000000  r5 : 00000000  r4 : 0000024c
>>>>>>>>> r3 : 00000000  r2 : 00204140  r1 : c124f13c  r0 : 00000000
>>>>>>>>> Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
>>>>>>>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
>>>>>>>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
>>>>>>>>> Stack: (0xef0dfd30 to 0xef0e0000)
>>>>>>>>> ...
>>>>>>>>> [<c0afc920>] (__mutex_lock) from [<c0afd400>]
>>>>>>>>> (mutex_lock_nested+0x1c/0x24)
>>>>>>>>> [<c0afd400>] (mutex_lock_nested) from [<c064d4b8>]
>>>>>>>>> (__exynos_dsi_host_attach+0x20/0x6c)
>>>>>>>>> [<c064d4b8>] (__exynos_dsi_host_attach) from [<c064d914>]
>>>>>>>>> (exynos_dsi_host_attach+0x70/0x194)
>>>>>>>>> [<c064d914>] (exynos_dsi_host_attach) from [<c0656b64>]
>>>>>>>>> (s6e8aa0_probe+0x1b0/0x218)
>>>>>>>>> [<c0656b64>] (s6e8aa0_probe) from [<c0672530>]
>>>>>>>>> (really_probe+0x200/0x4fc)
>>>>>>>>> [<c0672530>] (really_probe) from [<c06729f4>]
>>>>>>>>> (driver_probe_device+0x78/0x1fc)
>>>>>>>>> [<c06729f4>] (driver_probe_device) from [<c0672ddc>]
>>>>>>>>> (device_driver_attach+0x58/0x60)
>>>>>>>>> [<c0672ddc>] (device_driver_attach) from [<c0672ec0>]
>>>>>>>>> (__driver_attach+0xdc/0x174)
>>>>>>>>> [<c0672ec0>] (__driver_attach) from [<c06702b8>]
>>>>>>>>> (bus_for_each_dev+0x68/0xb4)
>>>>>>>>> [<c06702b8>] (bus_for_each_dev) from [<c06715ec>]
>>>>>>>>> (bus_add_driver+0x158/0x214)
>>>>>>>>> [<c06715ec>] (bus_add_driver) from [<c0673d20>]
>>>>>>>>> (driver_register+0x78/0x110)
>>>>>>>>> [<c0673d20>] (driver_register) from [<c0102484>]
>>>>>>>>> (do_one_initcall+0x8c/0x42c)
>>>>>>>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
>>>>>>>>> (kernel_init_freeable+0x190/0x1dc)
>>>>>>>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7988>]
>>>>>>>>> (kernel_init+0x8/0x118)
>>>>>>>>> [<c0af7988>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
>>>>>>>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
>>>>>>>>> ...
>>>>>>>>> ---[ end trace c06e996ec2e8234d ]---
>>>>>>>>>
>>>>>>>>> This means that dsi->encoder.dev is not initialized in
>>>>>>>>> __exynos_dsi_host_attach().
>>>>>>>>>
>>>>>>>>> This happens, because drm_bridge_attach() in exynos_dsi_bind() returned
>>>>>>>>> earlier -517 (deferred probe), what causes cleanup of encoder and
>>>>>>>>> release of all drm resources.
>>>>>>>>>
>>>>>>>>> Then however, the panel tries to register itself and
>>>>>>>>> exynos_dsi_host_attach() tries to access the released encoder (which is
>>>>>>>>> zeroed in drm_encoder_release) and rest of resources, what causes
>>>>>>>>> failure.
>>>>>>>>>
>>>>>>>>> It looks that something is missing. Maybe mipi host has to be
>>>>>>>>> registered
>>>>>>>>> later, when bridge is ready? I have no idea how it is handled before
>>>>>>>>> this patch. Andrzej, could you comment it a bit?
>>>>>>>> I intentionally changed the order, because if another bridge follows
>>>>>>>> in the
>>>>>>>> pipeline, the probe of the drm driver has to be deferred until some
>>>>>>>> bridge
>>>>>>>> provides a connector. The next bridge registers itself via the
>>>>>>>> host_attach
>>>>>>>> function and the deferral is ensured via the bind for the bind/unbind
>>>>>>>> API or
>>>>>>>> the bridge_attach function otherwise.
>>>>>>>>
>>>>>>>> On the other hand, the bridge does not have an encoder until the mipi
>>>>>>>> device
>>>>>>>> has been attached.
>>>>>>>>
>>>>>>>> As a solution, the exynos dsi driver must initialize the encoder in
>>>>>>>> exynos_dsi_probe instead of in exynos_dsi_bind and access the encoder
>>>>>>>> via
>>>>>>>> exynos_dsi instead of the bridge.
>>>>>>>>
>>>>>>>> Can you try to move everything except samsung_dsim_bind from
>>>>>>>> exynos_dsi_bind
>>>>>>>> to exynos_dsi_probe (respectively for unbind) and report if it fixes the
>>>>>>>> crash.
>>>>>>>
>>>>>>> The original behaviour is that encoder (exynos_dsi) is registered
>>>>>>> regardless of sink presence (initially panel, later also bridge) - it
>>>>>>> avoids multiple issues with deferred probe, device driver bind/unbind
>>>>>>> and module load/unload. Appearance or disappearance of sink is
>>>>>>> reported to host nicely via DSI attach/detach callbacks - and it is
>>>>>>> reflected in drm world as change state of the connector.
>>>>>>>
>>>>>>> Registering DSI host in bind and unregistering in unbind assures that
>>>>>>> if mipi_dsi device is attached/detached the drm device is always
>>>>>>> present - it makes device/driver binding race free and allows to avoid
>>>>>>> additional locking.
>>>>>>>
>>>>>>> Moving DSI host registration to probe changes everything, for sure it
>>>>>>> breaks the nice feature of DSI attach/detach callbacks and apparently
>>>>>>> can cause different issues depending on device bind order.
>>>>>>>
>>>>>>> I will try to look at the patches tomorrow and maybe I can find more
>>>>>>> constructive comments :)
>>>>>>
>>>>>> As I said yesterday, exynos_dsi driver uses dsi host attach/detach
>>>>>> callbacks to control appearance/disappearance of downstream device. It
>>>>>> allows to:
>>>>>>
>>>>>> 1. Safely bind/unbind different device drivers at any time and at any
>>>>>> order, without killing exynos_drm and/or crashing system.
>>>>>>
>>>>>> 2. Avoid issues with late drm init - on some platforms exynos_drm device
>>>>>> appeared too late, due to deferred probe, and resulted in black screen
>>>>>> in userspace.
>>>>>>
>>>>>>
>>>>>> Now if we want to convert exynos_dsi to drm_bridge I see following options:
>>>>>>
>>>>>> A. Forgot about callbacks and make the exynos_drm to defer probing until
>>>>>> exynos_dsi bridge is available, probably it will cause later exynos_drm
>>>>>> appearance, thus probably black screen on some targets. So for sure it
>>>>>> will be suboptimal. Making it bridge unbind safe would be another
>>>>>> problem, but most developers do not care about it so why should we? :)
>>>>>>
>>>>>> B. Try to mimic current behaviour - exynos_dsi register bridge ASAP,
>>>>>> even if downstream devices are not yet attached, on attach/detach notify
>>>>>> drm about it via connector status change, for this dsi_host registration
>>>>>> should be performed from drm_bridge attach, I guess.
>>>>>>
>>>>>>
>>>>>> Option A is more standard, but is unsafe and causes other issues.
>>>>>>
>>>>>> Option B keeps current behaviour.
>>>>> Maybe we can have both, but I am not sure, if I am missing something:
>>>>>
>>>>> I still prefer option A for the samsung-dsim driver, because it is more
>>>>> standard, simpler and avoids issues with encoders, connectors or handling
>>>>> hotplug.
>>>>>
>>>>> The idea is to use two bridges in the exynos-dsi driver: One bridge in the
>>>>> samsung-dsim driver which implements option A and defers probing of the drm
>>>>> driver until the next bridge is attached. And a second bridge in the
>>>>> exynos_dsi that attaches to the first bridge (thus, allowing the exynos_drm
>>>>> device to appear) and implements the hotplug handling for notifying drm via
>>>>> connector status change.
>>>>>
>>>>> The driver for the i.MX8M would use the samsung-dsim bridge without an
>>>>> additional bridge.
>>>>>
>>>>> This allows the samsung-dsim driver to expose the standard behavior while the
>>>>> exynos_dsi may stick to the existing behavior for the exynos_drm driver.
>>>>>
>>>>> I hope this makes sense and does not sound too crazy. It might be difficult to
>>>>> get the probing and mipi host/device registration correct, but I will try, if
>>>>> this can work.
>>>> Adding two bridges for being able to support hotplugging adds many special
>>>> cases to the bridge driver and still requires more custom API to correctly add
>>>> the second bridge. I don't think that this a viable path to go.
>>> Just jumping in here: You cannot hotplug/hotremove anything from a
>>> drm_device after drm_dev_register has been called, except
>>> drm_connector. I didn't dig into details here so not sure whether you
>>> want to late-bind your bridge after drm_dev_register is called or not,
>>> so might just be fyi and not relevant to the discussion.
>> Thanks. AFAIC that is exactly what is currently implemented in the exynos_drm
>> driver (i.e. Option B)
>>
>> exynos_dsi_bind configures the encoder and registers a DSI host. Afterwards,
>> exynos_drm_bind (as component_master_ops) calls drm_dev_register. Later, a DSI
>> device might attach to the DSI host and call exynos_dsi_host_attach. In
>> exynos_dsi_host_attach, the driver finds the drm_bridge for the DSI device and
>> attaches this bridge to the encoder _after_ drm_dev_register has been called.
>> This is invalid behavior, right?
> Definitely not supported, I don't think we have the right locks in place
> to make sure this works.
>
> Now if your _only_ adding a drm_bridge (and not an encoder or anything
> like that), and you are adding the drm_connector correctly (like a
> hotplugged DP MST sink), then that would at least work from a uapi pov.
> Because drm_bridge isn't exposed as an uapi object.
>
> But yeah, as-is, don't :-)
>
> The solution here is a bunch of EPROBE_DEFER handling until all your
> bridges are loaded, with or without the assistance of component.c
> framework. Only then call drm_dev_register.
> -Daniel


I have impression we have similar conversation already.

As you stated drm_bridge and drm_panel are not exposed to userspace so 
there shouldn't be problem with them from uapi PoV.

On the other side drm_panel or drm_bridge are not used until pipeline 
enters connected state (at least they were not some time ago :) ). The 
issue is that bridge exposes drm_connector, but as you stated (again :) 
) connectors can be hotplugged, so in theory it should work. Practical 
tests shows that it also works, but bugs can be still there.

Bunch of EPROBE_DEFER was very slow (as a result userspace timeouted and 
decided there is no display), and does not handle unbinding/re-binding 
drivers.


Regards

Andrzej


>
>> Michael
>>
>>> -Daniel
>>>
>>>> This leaves us with:
>>>>
>>>> Option A) Standard drm_bridge behavior, which is currently implemented, but
>>>> incompatible with the currently expected behavior of exynos_drm.
>>>>
>>>> Option B) Creating the drm device without all bridges being attached, which
>>>> would work with the exynos_drm driver, but breaks for the standard drm_bridge
>>>> behavior, especially, if the encoder/connector is created at the beginning of
>>>> the pipeline and passed downwards when the bridges are attached.
>>>>
>>>> Option C) Extracting only low level register accesses into shared code, adding
>>>> a custom interface and implementing the drm_bridge handling in the platform
>>>> specific code.
>>>>
>>>> None of the options really convinces me.
>>>>
>>>> Michael
>>>> _______________________________________________
>>>> dri-devel mailing list
>>>> dri-devel@lists.freedesktop.org
>>>> https://protect2.fireeye.com/v1/url?k=9b91fda0-c40ac4d5-9b9076ef-0cc47a31ce4e-a7f88871f6fd42d4&q=1&e=f2cfade9-8ecf-4697-88b3-69d93ed38361&u=https%3A%2F%2Flists.freedesktop.org%2Fmailman%2Flistinfo%2Fdri-devel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 10/16] drm/exynos: implement a drm bridge
  2021-02-04 16:28                         ` Andrzej Hajda
@ 2021-02-04 17:19                           ` Daniel Vetter
  2021-02-04 17:26                             ` Laurent Pinchart
  0 siblings, 1 reply; 61+ messages in thread
From: Daniel Vetter @ 2021-02-04 17:19 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: Marek Vasut, aisheng.dong, linux-samsung-soc, Laurent Pinchart,
	ch, Neil Armstrong, aford173, dri-devel,
	Bartlomiej Zolnierkiewicz, Seung-Woo Kim, Michael Tretter,
	Krzysztof Kozlowski, frieder.schrempf, dl-linux-imx,
	Sascha Hauer, Joonyoung Shim, sylvester.nawrocki,
	Marek Szyprowski, Shawn Guo, abel.vesa

On Thu, Feb 4, 2021 at 5:28 PM Andrzej Hajda <a.hajda@samsung.com> wrote:
>
>
> W dniu 04.02.2021 o 17:05, Daniel Vetter pisze:
> > On Thu, Feb 04, 2021 at 11:56:32AM +0100, Michael Tretter wrote:
> >> On Thu, 04 Feb 2021 11:17:49 +0100, Daniel Vetter wrote:
> >>> On Wed, Feb 3, 2021 at 9:32 PM Michael Tretter <m.tretter@pengutronix.de> wrote:
> >>>> On Mon, 01 Feb 2021 17:33:14 +0100, Michael Tretter wrote:
> >>>>> On Tue, 15 Sep 2020 21:40:40 +0200, Andrzej Hajda wrote:
> >>>>>> W dniu 14.09.2020 o 23:19, Andrzej Hajda pisze:
> >>>>>>> On 14.09.2020 22:01, Michael Tretter wrote:
> >>>>>>>> On Mon, 14 Sep 2020 14:31:19 +0200, Marek Szyprowski wrote:
> >>>>>>>>> On 14.09.2020 10:29, Marek Szyprowski wrote:
> >>>>>>>>>> On 11.09.2020 15:54, Michael Tretter wrote:
> >>>>>>>>>>> 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@pengutronix.de>
> >>>>>>>>>> This one (and the whole series applied) still fails on Exynos boards:
> >>>>>>>>>>
> >>>>>>>>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping
> >>>>>>>>>> operations
> >>>>>>>>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> >>>>>>>>>> OF: graph: no port node found in /soc/dsi@11c80000
> >>>>>>>>>> 8<--- cut here ---
> >>>>>>>>>> Unable to handle kernel NULL pointer dereference at virtual address
> >>>>>>>>>> 00000084
> >>>>>>>>>> pgd = (ptrval)
> >>>>>>>>>> [00000084] *pgd=00000000
> >>>>>>>>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> >>>>>>>>>> Modules linked in:
> >>>>>>>>>> CPU: 1 PID: 1 Comm: swapper/0 Not tainted
> >>>>>>>>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec #1608
> >>>>>>>>>> Hardware name: Samsung Exynos (Flattened Device Tree)
> >>>>>>>>>> PC is at drm_bridge_attach+0x18/0x164
> >>>>>>>>>> LR is at exynos_dsi_bind+0x88/0xa8
> >>>>>>>>>> pc : [<c0628c08>]    lr : [<c064d560>]    psr: 20000013
> >>>>>>>>>> sp : ef0dfca8  ip : 00000002  fp : c13190e0
> >>>>>>>>>> r10: 00000000  r9 : ee46d580  r8 : c13190e0
> >>>>>>>>>> r7 : ee438800  r6 : 00000018  r5 : ef253810  r4 : ef39e840
> >>>>>>>>>> r3 : 00000000  r2 : 00000018  r1 : ef39e888  r0 : ef39e840
> >>>>>>>>>> Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> >>>>>>>>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> >>>>>>>>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> >>>>>>>>>> Stack: (0xef0dfca8 to 0xef0e0000)
> >>>>>>>>>> ...
> >>>>>>>>>> [<c0628c08>] (drm_bridge_attach) from [<c064d560>]
> >>>>>>>>>> (exynos_dsi_bind+0x88/0xa8)
> >>>>>>>>>> [<c064d560>] (exynos_dsi_bind) from [<c066a800>]
> >>>>>>>>>> (component_bind_all+0xfc/0x290)
> >>>>>>>>>> [<c066a800>] (component_bind_all) from [<c0649dc0>]
> >>>>>>>>>> (exynos_drm_bind+0xe4/0x19c)
> >>>>>>>>>> [<c0649dc0>] (exynos_drm_bind) from [<c066ad74>]
> >>>>>>>>>> (try_to_bring_up_master+0x1e4/0x2c4)
> >>>>>>>>>> [<c066ad74>] (try_to_bring_up_master) from [<c066b2b4>]
> >>>>>>>>>> (component_master_add_with_match+0xd4/0x108)
> >>>>>>>>>> [<c066b2b4>] (component_master_add_with_match) from [<c0649ae8>]
> >>>>>>>>>> (exynos_drm_platform_probe+0xe4/0x110)
> >>>>>>>>>> [<c0649ae8>] (exynos_drm_platform_probe) from [<c0674e6c>]
> >>>>>>>>>> (platform_drv_probe+0x6c/0xa4)
> >>>>>>>>>> [<c0674e6c>] (platform_drv_probe) from [<c067242c>]
> >>>>>>>>>> (really_probe+0x200/0x4fc)
> >>>>>>>>>> [<c067242c>] (really_probe) from [<c06728f0>]
> >>>>>>>>>> (driver_probe_device+0x78/0x1fc)
> >>>>>>>>>> [<c06728f0>] (driver_probe_device) from [<c0672cd8>]
> >>>>>>>>>> (device_driver_attach+0x58/0x60)
> >>>>>>>>>> [<c0672cd8>] (device_driver_attach) from [<c0672dbc>]
> >>>>>>>>>> (__driver_attach+0xdc/0x174)
> >>>>>>>>>> [<c0672dbc>] (__driver_attach) from [<c06701b4>]
> >>>>>>>>>> (bus_for_each_dev+0x68/0xb4)
> >>>>>>>>>> [<c06701b4>] (bus_for_each_dev) from [<c06714e8>]
> >>>>>>>>>> (bus_add_driver+0x158/0x214)
> >>>>>>>>>> [<c06714e8>] (bus_add_driver) from [<c0673c1c>]
> >>>>>>>>>> (driver_register+0x78/0x110)
> >>>>>>>>>> [<c0673c1c>] (driver_register) from [<c0649ca8>]
> >>>>>>>>>> (exynos_drm_init+0xe4/0x118)
> >>>>>>>>>> [<c0649ca8>] (exynos_drm_init) from [<c0102484>]
> >>>>>>>>>> (do_one_initcall+0x8c/0x42c)
> >>>>>>>>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
> >>>>>>>>>> (kernel_init_freeable+0x190/0x1dc)
> >>>>>>>>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7880>]
> >>>>>>>>>> (kernel_init+0x8/0x118)
> >>>>>>>>>> [<c0af7880>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> >>>>>>>>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
> >>>>>>>>>> ...
> >>>>>>>>>> ---[ end trace ee27f313f9ed9da1 ]---
> >>>>>>>>>>
> >>>>>>>>>> # arm-linux-gnueabi-addr2line -e vmlinux c0628c08
> >>>>>>>>>> drivers/gpu/drm/drm_bridge.c:184 (discriminator 1)
> >>>>>>>>>>
> >>>>>>>>>> I will try to debug it a bit more today.
> >>>>>>>>> The above crash has been caused by lack of in_bridge initialization to
> >>>>>>>>> NULL in exynos_dsi_bind() in this patch. However, fixing it reveals
> >>>>>>>>> another issue:
> >>>>>>>>>
> >>>>>>>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
> >>>>>>>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> >>>>>>>>> OF: graph: no port node found in /soc/dsi@11c80000
> >>>>>>>>> 8<--- cut here ---
> >>>>>>>>> Unable to handle kernel NULL pointer dereference at virtual address
> >>>>>>>>> 00000280
> >>>>>>>>> pgd = (ptrval)
> >>>>>>>>> [00000280] *pgd=00000000
> >>>>>>>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> >>>>>>>>> Modules linked in:
> >>>>>>>>> CPU: 0 PID: 1 Comm: swapper/0 Not tainted
> >>>>>>>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec-dirty #1613
> >>>>>>>>> Hardware name: Samsung Exynos (Flattened Device Tree)
> >>>>>>>>> PC is at __mutex_lock+0x54/0xb18
> >>>>>>>>> LR is at lock_is_held_type+0x80/0x138
> >>>>>>>>> pc : [<c0afc920>]    lr : [<c0af63e8>]    psr: 60000013
> >>>>>>>>> sp : ef0dfd30  ip : 33937b74  fp : c13193c8
> >>>>>>>>> r10: c1208eec  r9 : 00000000  r8 : ee45f808
> >>>>>>>>> r7 : c19561a4  r6 : 00000000  r5 : 00000000  r4 : 0000024c
> >>>>>>>>> r3 : 00000000  r2 : 00204140  r1 : c124f13c  r0 : 00000000
> >>>>>>>>> Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> >>>>>>>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> >>>>>>>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> >>>>>>>>> Stack: (0xef0dfd30 to 0xef0e0000)
> >>>>>>>>> ...
> >>>>>>>>> [<c0afc920>] (__mutex_lock) from [<c0afd400>]
> >>>>>>>>> (mutex_lock_nested+0x1c/0x24)
> >>>>>>>>> [<c0afd400>] (mutex_lock_nested) from [<c064d4b8>]
> >>>>>>>>> (__exynos_dsi_host_attach+0x20/0x6c)
> >>>>>>>>> [<c064d4b8>] (__exynos_dsi_host_attach) from [<c064d914>]
> >>>>>>>>> (exynos_dsi_host_attach+0x70/0x194)
> >>>>>>>>> [<c064d914>] (exynos_dsi_host_attach) from [<c0656b64>]
> >>>>>>>>> (s6e8aa0_probe+0x1b0/0x218)
> >>>>>>>>> [<c0656b64>] (s6e8aa0_probe) from [<c0672530>]
> >>>>>>>>> (really_probe+0x200/0x4fc)
> >>>>>>>>> [<c0672530>] (really_probe) from [<c06729f4>]
> >>>>>>>>> (driver_probe_device+0x78/0x1fc)
> >>>>>>>>> [<c06729f4>] (driver_probe_device) from [<c0672ddc>]
> >>>>>>>>> (device_driver_attach+0x58/0x60)
> >>>>>>>>> [<c0672ddc>] (device_driver_attach) from [<c0672ec0>]
> >>>>>>>>> (__driver_attach+0xdc/0x174)
> >>>>>>>>> [<c0672ec0>] (__driver_attach) from [<c06702b8>]
> >>>>>>>>> (bus_for_each_dev+0x68/0xb4)
> >>>>>>>>> [<c06702b8>] (bus_for_each_dev) from [<c06715ec>]
> >>>>>>>>> (bus_add_driver+0x158/0x214)
> >>>>>>>>> [<c06715ec>] (bus_add_driver) from [<c0673d20>]
> >>>>>>>>> (driver_register+0x78/0x110)
> >>>>>>>>> [<c0673d20>] (driver_register) from [<c0102484>]
> >>>>>>>>> (do_one_initcall+0x8c/0x42c)
> >>>>>>>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
> >>>>>>>>> (kernel_init_freeable+0x190/0x1dc)
> >>>>>>>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7988>]
> >>>>>>>>> (kernel_init+0x8/0x118)
> >>>>>>>>> [<c0af7988>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> >>>>>>>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
> >>>>>>>>> ...
> >>>>>>>>> ---[ end trace c06e996ec2e8234d ]---
> >>>>>>>>>
> >>>>>>>>> This means that dsi->encoder.dev is not initialized in
> >>>>>>>>> __exynos_dsi_host_attach().
> >>>>>>>>>
> >>>>>>>>> This happens, because drm_bridge_attach() in exynos_dsi_bind() returned
> >>>>>>>>> earlier -517 (deferred probe), what causes cleanup of encoder and
> >>>>>>>>> release of all drm resources.
> >>>>>>>>>
> >>>>>>>>> Then however, the panel tries to register itself and
> >>>>>>>>> exynos_dsi_host_attach() tries to access the released encoder (which is
> >>>>>>>>> zeroed in drm_encoder_release) and rest of resources, what causes
> >>>>>>>>> failure.
> >>>>>>>>>
> >>>>>>>>> It looks that something is missing. Maybe mipi host has to be
> >>>>>>>>> registered
> >>>>>>>>> later, when bridge is ready? I have no idea how it is handled before
> >>>>>>>>> this patch. Andrzej, could you comment it a bit?
> >>>>>>>> I intentionally changed the order, because if another bridge follows
> >>>>>>>> in the
> >>>>>>>> pipeline, the probe of the drm driver has to be deferred until some
> >>>>>>>> bridge
> >>>>>>>> provides a connector. The next bridge registers itself via the
> >>>>>>>> host_attach
> >>>>>>>> function and the deferral is ensured via the bind for the bind/unbind
> >>>>>>>> API or
> >>>>>>>> the bridge_attach function otherwise.
> >>>>>>>>
> >>>>>>>> On the other hand, the bridge does not have an encoder until the mipi
> >>>>>>>> device
> >>>>>>>> has been attached.
> >>>>>>>>
> >>>>>>>> As a solution, the exynos dsi driver must initialize the encoder in
> >>>>>>>> exynos_dsi_probe instead of in exynos_dsi_bind and access the encoder
> >>>>>>>> via
> >>>>>>>> exynos_dsi instead of the bridge.
> >>>>>>>>
> >>>>>>>> Can you try to move everything except samsung_dsim_bind from
> >>>>>>>> exynos_dsi_bind
> >>>>>>>> to exynos_dsi_probe (respectively for unbind) and report if it fixes the
> >>>>>>>> crash.
> >>>>>>>
> >>>>>>> The original behaviour is that encoder (exynos_dsi) is registered
> >>>>>>> regardless of sink presence (initially panel, later also bridge) - it
> >>>>>>> avoids multiple issues with deferred probe, device driver bind/unbind
> >>>>>>> and module load/unload. Appearance or disappearance of sink is
> >>>>>>> reported to host nicely via DSI attach/detach callbacks - and it is
> >>>>>>> reflected in drm world as change state of the connector.
> >>>>>>>
> >>>>>>> Registering DSI host in bind and unregistering in unbind assures that
> >>>>>>> if mipi_dsi device is attached/detached the drm device is always
> >>>>>>> present - it makes device/driver binding race free and allows to avoid
> >>>>>>> additional locking.
> >>>>>>>
> >>>>>>> Moving DSI host registration to probe changes everything, for sure it
> >>>>>>> breaks the nice feature of DSI attach/detach callbacks and apparently
> >>>>>>> can cause different issues depending on device bind order.
> >>>>>>>
> >>>>>>> I will try to look at the patches tomorrow and maybe I can find more
> >>>>>>> constructive comments :)
> >>>>>>
> >>>>>> As I said yesterday, exynos_dsi driver uses dsi host attach/detach
> >>>>>> callbacks to control appearance/disappearance of downstream device. It
> >>>>>> allows to:
> >>>>>>
> >>>>>> 1. Safely bind/unbind different device drivers at any time and at any
> >>>>>> order, without killing exynos_drm and/or crashing system.
> >>>>>>
> >>>>>> 2. Avoid issues with late drm init - on some platforms exynos_drm device
> >>>>>> appeared too late, due to deferred probe, and resulted in black screen
> >>>>>> in userspace.
> >>>>>>
> >>>>>>
> >>>>>> Now if we want to convert exynos_dsi to drm_bridge I see following options:
> >>>>>>
> >>>>>> A. Forgot about callbacks and make the exynos_drm to defer probing until
> >>>>>> exynos_dsi bridge is available, probably it will cause later exynos_drm
> >>>>>> appearance, thus probably black screen on some targets. So for sure it
> >>>>>> will be suboptimal. Making it bridge unbind safe would be another
> >>>>>> problem, but most developers do not care about it so why should we? :)
> >>>>>>
> >>>>>> B. Try to mimic current behaviour - exynos_dsi register bridge ASAP,
> >>>>>> even if downstream devices are not yet attached, on attach/detach notify
> >>>>>> drm about it via connector status change, for this dsi_host registration
> >>>>>> should be performed from drm_bridge attach, I guess.
> >>>>>>
> >>>>>>
> >>>>>> Option A is more standard, but is unsafe and causes other issues.
> >>>>>>
> >>>>>> Option B keeps current behaviour.
> >>>>> Maybe we can have both, but I am not sure, if I am missing something:
> >>>>>
> >>>>> I still prefer option A for the samsung-dsim driver, because it is more
> >>>>> standard, simpler and avoids issues with encoders, connectors or handling
> >>>>> hotplug.
> >>>>>
> >>>>> The idea is to use two bridges in the exynos-dsi driver: One bridge in the
> >>>>> samsung-dsim driver which implements option A and defers probing of the drm
> >>>>> driver until the next bridge is attached. And a second bridge in the
> >>>>> exynos_dsi that attaches to the first bridge (thus, allowing the exynos_drm
> >>>>> device to appear) and implements the hotplug handling for notifying drm via
> >>>>> connector status change.
> >>>>>
> >>>>> The driver for the i.MX8M would use the samsung-dsim bridge without an
> >>>>> additional bridge.
> >>>>>
> >>>>> This allows the samsung-dsim driver to expose the standard behavior while the
> >>>>> exynos_dsi may stick to the existing behavior for the exynos_drm driver.
> >>>>>
> >>>>> I hope this makes sense and does not sound too crazy. It might be difficult to
> >>>>> get the probing and mipi host/device registration correct, but I will try, if
> >>>>> this can work.
> >>>> Adding two bridges for being able to support hotplugging adds many special
> >>>> cases to the bridge driver and still requires more custom API to correctly add
> >>>> the second bridge. I don't think that this a viable path to go.
> >>> Just jumping in here: You cannot hotplug/hotremove anything from a
> >>> drm_device after drm_dev_register has been called, except
> >>> drm_connector. I didn't dig into details here so not sure whether you
> >>> want to late-bind your bridge after drm_dev_register is called or not,
> >>> so might just be fyi and not relevant to the discussion.
> >> Thanks. AFAIC that is exactly what is currently implemented in the exynos_drm
> >> driver (i.e. Option B)
> >>
> >> exynos_dsi_bind configures the encoder and registers a DSI host. Afterwards,
> >> exynos_drm_bind (as component_master_ops) calls drm_dev_register. Later, a DSI
> >> device might attach to the DSI host and call exynos_dsi_host_attach. In
> >> exynos_dsi_host_attach, the driver finds the drm_bridge for the DSI device and
> >> attaches this bridge to the encoder _after_ drm_dev_register has been called.
> >> This is invalid behavior, right?
> > Definitely not supported, I don't think we have the right locks in place
> > to make sure this works.
> >
> > Now if your _only_ adding a drm_bridge (and not an encoder or anything
> > like that), and you are adding the drm_connector correctly (like a
> > hotplugged DP MST sink), then that would at least work from a uapi pov.
> > Because drm_bridge isn't exposed as an uapi object.
> >
> > But yeah, as-is, don't :-)
> >
> > The solution here is a bunch of EPROBE_DEFER handling until all your
> > bridges are loaded, with or without the assistance of component.c
> > framework. Only then call drm_dev_register.
> > -Daniel
>
>
> I have impression we have similar conversation already.
>
> As you stated drm_bridge and drm_panel are not exposed to userspace so
> there shouldn't be problem with them from uapi PoV.
>
> On the other side drm_panel or drm_bridge are not used until pipeline
> enters connected state (at least they were not some time ago :) ). The
> issue is that bridge exposes drm_connector, but as you stated (again :)
> ) connectors can be hotplugged, so in theory it should work. Practical
> tests shows that it also works, but bugs can be still there.
>
> Bunch of EPROBE_DEFER was very slow (as a result userspace timeouted and
> decided there is no display), and does not handle unbinding/re-binding
> drivers.

Rebinding drivers should be fixed now, with a bunch of fixes in driver
core. If not, we need to fix this more.

Also, EPROBE_DEFER is how this is supposed to work. If it's too slow,
we need to fix EPROBE_DEFER (there's ideas for pre-sorting that never
seem to go anywhere), not paper over it with bad architecture in
drivers.

There's also a bit the issue that most userspace handles panels in a
special way, and if they don't find the panel at first, that doesn't
work. Stuff like "which is the main screen" on laptops.

So yeah please fix this properly.
-Daniel

>
>
> Regards
>
> Andrzej
>
>
> >
> >> Michael
> >>
> >>> -Daniel
> >>>
> >>>> This leaves us with:
> >>>>
> >>>> Option A) Standard drm_bridge behavior, which is currently implemented, but
> >>>> incompatible with the currently expected behavior of exynos_drm.
> >>>>
> >>>> Option B) Creating the drm device without all bridges being attached, which
> >>>> would work with the exynos_drm driver, but breaks for the standard drm_bridge
> >>>> behavior, especially, if the encoder/connector is created at the beginning of
> >>>> the pipeline and passed downwards when the bridges are attached.
> >>>>
> >>>> Option C) Extracting only low level register accesses into shared code, adding
> >>>> a custom interface and implementing the drm_bridge handling in the platform
> >>>> specific code.
> >>>>
> >>>> None of the options really convinces me.
> >>>>
> >>>> Michael
> >>>> _______________________________________________
> >>>> dri-devel mailing list
> >>>> dri-devel@lists.freedesktop.org
> >>>> https://protect2.fireeye.com/v1/url?k=9b91fda0-c40ac4d5-9b9076ef-0cc47a31ce4e-a7f88871f6fd42d4&q=1&e=f2cfade9-8ecf-4697-88b3-69d93ed38361&u=https%3A%2F%2Flists.freedesktop.org%2Fmailman%2Flistinfo%2Fdri-devel



-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 10/16] drm/exynos: implement a drm bridge
  2021-02-04 17:19                           ` Daniel Vetter
@ 2021-02-04 17:26                             ` Laurent Pinchart
  2021-02-04 17:46                               ` Daniel Vetter
  0 siblings, 1 reply; 61+ messages in thread
From: Laurent Pinchart @ 2021-02-04 17:26 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: Marek Vasut, aisheng.dong, linux-samsung-soc, ch, Neil Armstrong,
	aford173, dri-devel, Bartlomiej Zolnierkiewicz, Seung-Woo Kim,
	Michael Tretter, Krzysztof Kozlowski, frieder.schrempf,
	Andrzej Hajda, dl-linux-imx, Sascha Hauer, Joonyoung Shim,
	sylvester.nawrocki, Marek Szyprowski, Shawn Guo, abel.vesa

Hi Daniel,

On Thu, Feb 04, 2021 at 06:19:22PM +0100, Daniel Vetter wrote:
> On Thu, Feb 4, 2021 at 5:28 PM Andrzej Hajda wrote:
> > W dniu 04.02.2021 o 17:05, Daniel Vetter pisze:
> > > On Thu, Feb 04, 2021 at 11:56:32AM +0100, Michael Tretter wrote:
> > >> On Thu, 04 Feb 2021 11:17:49 +0100, Daniel Vetter wrote:
> > >>> On Wed, Feb 3, 2021 at 9:32 PM Michael Tretter wrote:
> > >>>> On Mon, 01 Feb 2021 17:33:14 +0100, Michael Tretter wrote:
> > >>>>> On Tue, 15 Sep 2020 21:40:40 +0200, Andrzej Hajda wrote:
> > >>>>>> W dniu 14.09.2020 o 23:19, Andrzej Hajda pisze:
> > >>>>>>> On 14.09.2020 22:01, Michael Tretter wrote:
> > >>>>>>>> On Mon, 14 Sep 2020 14:31:19 +0200, Marek Szyprowski wrote:
> > >>>>>>>>> On 14.09.2020 10:29, Marek Szyprowski wrote:
> > >>>>>>>>>> On 11.09.2020 15:54, Michael Tretter wrote:
> > >>>>>>>>>>> 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@pengutronix.de>
> > >>>>>>>>>> This one (and the whole series applied) still fails on Exynos boards:
> > >>>>>>>>>>
> > >>>>>>>>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping
> > >>>>>>>>>> operations
> > >>>>>>>>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> > >>>>>>>>>> OF: graph: no port node found in /soc/dsi@11c80000
> > >>>>>>>>>> 8<--- cut here ---
> > >>>>>>>>>> Unable to handle kernel NULL pointer dereference at virtual address
> > >>>>>>>>>> 00000084
> > >>>>>>>>>> pgd = (ptrval)
> > >>>>>>>>>> [00000084] *pgd=00000000
> > >>>>>>>>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> > >>>>>>>>>> Modules linked in:
> > >>>>>>>>>> CPU: 1 PID: 1 Comm: swapper/0 Not tainted
> > >>>>>>>>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec #1608
> > >>>>>>>>>> Hardware name: Samsung Exynos (Flattened Device Tree)
> > >>>>>>>>>> PC is at drm_bridge_attach+0x18/0x164
> > >>>>>>>>>> LR is at exynos_dsi_bind+0x88/0xa8
> > >>>>>>>>>> pc : [<c0628c08>]    lr : [<c064d560>]    psr: 20000013
> > >>>>>>>>>> sp : ef0dfca8  ip : 00000002  fp : c13190e0
> > >>>>>>>>>> r10: 00000000  r9 : ee46d580  r8 : c13190e0
> > >>>>>>>>>> r7 : ee438800  r6 : 00000018  r5 : ef253810  r4 : ef39e840
> > >>>>>>>>>> r3 : 00000000  r2 : 00000018  r1 : ef39e888  r0 : ef39e840
> > >>>>>>>>>> Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> > >>>>>>>>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> > >>>>>>>>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> > >>>>>>>>>> Stack: (0xef0dfca8 to 0xef0e0000)
> > >>>>>>>>>> ...
> > >>>>>>>>>> [<c0628c08>] (drm_bridge_attach) from [<c064d560>]
> > >>>>>>>>>> (exynos_dsi_bind+0x88/0xa8)
> > >>>>>>>>>> [<c064d560>] (exynos_dsi_bind) from [<c066a800>]
> > >>>>>>>>>> (component_bind_all+0xfc/0x290)
> > >>>>>>>>>> [<c066a800>] (component_bind_all) from [<c0649dc0>]
> > >>>>>>>>>> (exynos_drm_bind+0xe4/0x19c)
> > >>>>>>>>>> [<c0649dc0>] (exynos_drm_bind) from [<c066ad74>]
> > >>>>>>>>>> (try_to_bring_up_master+0x1e4/0x2c4)
> > >>>>>>>>>> [<c066ad74>] (try_to_bring_up_master) from [<c066b2b4>]
> > >>>>>>>>>> (component_master_add_with_match+0xd4/0x108)
> > >>>>>>>>>> [<c066b2b4>] (component_master_add_with_match) from [<c0649ae8>]
> > >>>>>>>>>> (exynos_drm_platform_probe+0xe4/0x110)
> > >>>>>>>>>> [<c0649ae8>] (exynos_drm_platform_probe) from [<c0674e6c>]
> > >>>>>>>>>> (platform_drv_probe+0x6c/0xa4)
> > >>>>>>>>>> [<c0674e6c>] (platform_drv_probe) from [<c067242c>]
> > >>>>>>>>>> (really_probe+0x200/0x4fc)
> > >>>>>>>>>> [<c067242c>] (really_probe) from [<c06728f0>]
> > >>>>>>>>>> (driver_probe_device+0x78/0x1fc)
> > >>>>>>>>>> [<c06728f0>] (driver_probe_device) from [<c0672cd8>]
> > >>>>>>>>>> (device_driver_attach+0x58/0x60)
> > >>>>>>>>>> [<c0672cd8>] (device_driver_attach) from [<c0672dbc>]
> > >>>>>>>>>> (__driver_attach+0xdc/0x174)
> > >>>>>>>>>> [<c0672dbc>] (__driver_attach) from [<c06701b4>]
> > >>>>>>>>>> (bus_for_each_dev+0x68/0xb4)
> > >>>>>>>>>> [<c06701b4>] (bus_for_each_dev) from [<c06714e8>]
> > >>>>>>>>>> (bus_add_driver+0x158/0x214)
> > >>>>>>>>>> [<c06714e8>] (bus_add_driver) from [<c0673c1c>]
> > >>>>>>>>>> (driver_register+0x78/0x110)
> > >>>>>>>>>> [<c0673c1c>] (driver_register) from [<c0649ca8>]
> > >>>>>>>>>> (exynos_drm_init+0xe4/0x118)
> > >>>>>>>>>> [<c0649ca8>] (exynos_drm_init) from [<c0102484>]
> > >>>>>>>>>> (do_one_initcall+0x8c/0x42c)
> > >>>>>>>>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
> > >>>>>>>>>> (kernel_init_freeable+0x190/0x1dc)
> > >>>>>>>>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7880>]
> > >>>>>>>>>> (kernel_init+0x8/0x118)
> > >>>>>>>>>> [<c0af7880>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> > >>>>>>>>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
> > >>>>>>>>>> ...
> > >>>>>>>>>> ---[ end trace ee27f313f9ed9da1 ]---
> > >>>>>>>>>>
> > >>>>>>>>>> # arm-linux-gnueabi-addr2line -e vmlinux c0628c08
> > >>>>>>>>>> drivers/gpu/drm/drm_bridge.c:184 (discriminator 1)
> > >>>>>>>>>>
> > >>>>>>>>>> I will try to debug it a bit more today.
> > >>>>>>>>> The above crash has been caused by lack of in_bridge initialization to
> > >>>>>>>>> NULL in exynos_dsi_bind() in this patch. However, fixing it reveals
> > >>>>>>>>> another issue:
> > >>>>>>>>>
> > >>>>>>>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
> > >>>>>>>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> > >>>>>>>>> OF: graph: no port node found in /soc/dsi@11c80000
> > >>>>>>>>> 8<--- cut here ---
> > >>>>>>>>> Unable to handle kernel NULL pointer dereference at virtual address
> > >>>>>>>>> 00000280
> > >>>>>>>>> pgd = (ptrval)
> > >>>>>>>>> [00000280] *pgd=00000000
> > >>>>>>>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> > >>>>>>>>> Modules linked in:
> > >>>>>>>>> CPU: 0 PID: 1 Comm: swapper/0 Not tainted
> > >>>>>>>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec-dirty #1613
> > >>>>>>>>> Hardware name: Samsung Exynos (Flattened Device Tree)
> > >>>>>>>>> PC is at __mutex_lock+0x54/0xb18
> > >>>>>>>>> LR is at lock_is_held_type+0x80/0x138
> > >>>>>>>>> pc : [<c0afc920>]    lr : [<c0af63e8>]    psr: 60000013
> > >>>>>>>>> sp : ef0dfd30  ip : 33937b74  fp : c13193c8
> > >>>>>>>>> r10: c1208eec  r9 : 00000000  r8 : ee45f808
> > >>>>>>>>> r7 : c19561a4  r6 : 00000000  r5 : 00000000  r4 : 0000024c
> > >>>>>>>>> r3 : 00000000  r2 : 00204140  r1 : c124f13c  r0 : 00000000
> > >>>>>>>>> Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> > >>>>>>>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> > >>>>>>>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> > >>>>>>>>> Stack: (0xef0dfd30 to 0xef0e0000)
> > >>>>>>>>> ...
> > >>>>>>>>> [<c0afc920>] (__mutex_lock) from [<c0afd400>]
> > >>>>>>>>> (mutex_lock_nested+0x1c/0x24)
> > >>>>>>>>> [<c0afd400>] (mutex_lock_nested) from [<c064d4b8>]
> > >>>>>>>>> (__exynos_dsi_host_attach+0x20/0x6c)
> > >>>>>>>>> [<c064d4b8>] (__exynos_dsi_host_attach) from [<c064d914>]
> > >>>>>>>>> (exynos_dsi_host_attach+0x70/0x194)
> > >>>>>>>>> [<c064d914>] (exynos_dsi_host_attach) from [<c0656b64>]
> > >>>>>>>>> (s6e8aa0_probe+0x1b0/0x218)
> > >>>>>>>>> [<c0656b64>] (s6e8aa0_probe) from [<c0672530>]
> > >>>>>>>>> (really_probe+0x200/0x4fc)
> > >>>>>>>>> [<c0672530>] (really_probe) from [<c06729f4>]
> > >>>>>>>>> (driver_probe_device+0x78/0x1fc)
> > >>>>>>>>> [<c06729f4>] (driver_probe_device) from [<c0672ddc>]
> > >>>>>>>>> (device_driver_attach+0x58/0x60)
> > >>>>>>>>> [<c0672ddc>] (device_driver_attach) from [<c0672ec0>]
> > >>>>>>>>> (__driver_attach+0xdc/0x174)
> > >>>>>>>>> [<c0672ec0>] (__driver_attach) from [<c06702b8>]
> > >>>>>>>>> (bus_for_each_dev+0x68/0xb4)
> > >>>>>>>>> [<c06702b8>] (bus_for_each_dev) from [<c06715ec>]
> > >>>>>>>>> (bus_add_driver+0x158/0x214)
> > >>>>>>>>> [<c06715ec>] (bus_add_driver) from [<c0673d20>]
> > >>>>>>>>> (driver_register+0x78/0x110)
> > >>>>>>>>> [<c0673d20>] (driver_register) from [<c0102484>]
> > >>>>>>>>> (do_one_initcall+0x8c/0x42c)
> > >>>>>>>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
> > >>>>>>>>> (kernel_init_freeable+0x190/0x1dc)
> > >>>>>>>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7988>]
> > >>>>>>>>> (kernel_init+0x8/0x118)
> > >>>>>>>>> [<c0af7988>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> > >>>>>>>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
> > >>>>>>>>> ...
> > >>>>>>>>> ---[ end trace c06e996ec2e8234d ]---
> > >>>>>>>>>
> > >>>>>>>>> This means that dsi->encoder.dev is not initialized in
> > >>>>>>>>> __exynos_dsi_host_attach().
> > >>>>>>>>>
> > >>>>>>>>> This happens, because drm_bridge_attach() in exynos_dsi_bind() returned
> > >>>>>>>>> earlier -517 (deferred probe), what causes cleanup of encoder and
> > >>>>>>>>> release of all drm resources.
> > >>>>>>>>>
> > >>>>>>>>> Then however, the panel tries to register itself and
> > >>>>>>>>> exynos_dsi_host_attach() tries to access the released encoder (which is
> > >>>>>>>>> zeroed in drm_encoder_release) and rest of resources, what causes
> > >>>>>>>>> failure.
> > >>>>>>>>>
> > >>>>>>>>> It looks that something is missing. Maybe mipi host has to be
> > >>>>>>>>> registered
> > >>>>>>>>> later, when bridge is ready? I have no idea how it is handled before
> > >>>>>>>>> this patch. Andrzej, could you comment it a bit?
> > >>>>>>>> I intentionally changed the order, because if another bridge follows
> > >>>>>>>> in the
> > >>>>>>>> pipeline, the probe of the drm driver has to be deferred until some
> > >>>>>>>> bridge
> > >>>>>>>> provides a connector. The next bridge registers itself via the
> > >>>>>>>> host_attach
> > >>>>>>>> function and the deferral is ensured via the bind for the bind/unbind
> > >>>>>>>> API or
> > >>>>>>>> the bridge_attach function otherwise.
> > >>>>>>>>
> > >>>>>>>> On the other hand, the bridge does not have an encoder until the mipi
> > >>>>>>>> device
> > >>>>>>>> has been attached.
> > >>>>>>>>
> > >>>>>>>> As a solution, the exynos dsi driver must initialize the encoder in
> > >>>>>>>> exynos_dsi_probe instead of in exynos_dsi_bind and access the encoder
> > >>>>>>>> via
> > >>>>>>>> exynos_dsi instead of the bridge.
> > >>>>>>>>
> > >>>>>>>> Can you try to move everything except samsung_dsim_bind from
> > >>>>>>>> exynos_dsi_bind
> > >>>>>>>> to exynos_dsi_probe (respectively for unbind) and report if it fixes the
> > >>>>>>>> crash.
> > >>>>>>>
> > >>>>>>> The original behaviour is that encoder (exynos_dsi) is registered
> > >>>>>>> regardless of sink presence (initially panel, later also bridge) - it
> > >>>>>>> avoids multiple issues with deferred probe, device driver bind/unbind
> > >>>>>>> and module load/unload. Appearance or disappearance of sink is
> > >>>>>>> reported to host nicely via DSI attach/detach callbacks - and it is
> > >>>>>>> reflected in drm world as change state of the connector.
> > >>>>>>>
> > >>>>>>> Registering DSI host in bind and unregistering in unbind assures that
> > >>>>>>> if mipi_dsi device is attached/detached the drm device is always
> > >>>>>>> present - it makes device/driver binding race free and allows to avoid
> > >>>>>>> additional locking.
> > >>>>>>>
> > >>>>>>> Moving DSI host registration to probe changes everything, for sure it
> > >>>>>>> breaks the nice feature of DSI attach/detach callbacks and apparently
> > >>>>>>> can cause different issues depending on device bind order.
> > >>>>>>>
> > >>>>>>> I will try to look at the patches tomorrow and maybe I can find more
> > >>>>>>> constructive comments :)
> > >>>>>>
> > >>>>>> As I said yesterday, exynos_dsi driver uses dsi host attach/detach
> > >>>>>> callbacks to control appearance/disappearance of downstream device. It
> > >>>>>> allows to:
> > >>>>>>
> > >>>>>> 1. Safely bind/unbind different device drivers at any time and at any
> > >>>>>> order, without killing exynos_drm and/or crashing system.
> > >>>>>>
> > >>>>>> 2. Avoid issues with late drm init - on some platforms exynos_drm device
> > >>>>>> appeared too late, due to deferred probe, and resulted in black screen
> > >>>>>> in userspace.
> > >>>>>>
> > >>>>>>
> > >>>>>> Now if we want to convert exynos_dsi to drm_bridge I see following options:
> > >>>>>>
> > >>>>>> A. Forgot about callbacks and make the exynos_drm to defer probing until
> > >>>>>> exynos_dsi bridge is available, probably it will cause later exynos_drm
> > >>>>>> appearance, thus probably black screen on some targets. So for sure it
> > >>>>>> will be suboptimal. Making it bridge unbind safe would be another
> > >>>>>> problem, but most developers do not care about it so why should we? :)
> > >>>>>>
> > >>>>>> B. Try to mimic current behaviour - exynos_dsi register bridge ASAP,
> > >>>>>> even if downstream devices are not yet attached, on attach/detach notify
> > >>>>>> drm about it via connector status change, for this dsi_host registration
> > >>>>>> should be performed from drm_bridge attach, I guess.
> > >>>>>>
> > >>>>>>
> > >>>>>> Option A is more standard, but is unsafe and causes other issues.
> > >>>>>>
> > >>>>>> Option B keeps current behaviour.
> > >>>>> Maybe we can have both, but I am not sure, if I am missing something:
> > >>>>>
> > >>>>> I still prefer option A for the samsung-dsim driver, because it is more
> > >>>>> standard, simpler and avoids issues with encoders, connectors or handling
> > >>>>> hotplug.
> > >>>>>
> > >>>>> The idea is to use two bridges in the exynos-dsi driver: One bridge in the
> > >>>>> samsung-dsim driver which implements option A and defers probing of the drm
> > >>>>> driver until the next bridge is attached. And a second bridge in the
> > >>>>> exynos_dsi that attaches to the first bridge (thus, allowing the exynos_drm
> > >>>>> device to appear) and implements the hotplug handling for notifying drm via
> > >>>>> connector status change.
> > >>>>>
> > >>>>> The driver for the i.MX8M would use the samsung-dsim bridge without an
> > >>>>> additional bridge.
> > >>>>>
> > >>>>> This allows the samsung-dsim driver to expose the standard behavior while the
> > >>>>> exynos_dsi may stick to the existing behavior for the exynos_drm driver.
> > >>>>>
> > >>>>> I hope this makes sense and does not sound too crazy. It might be difficult to
> > >>>>> get the probing and mipi host/device registration correct, but I will try, if
> > >>>>> this can work.
> > >>>> Adding two bridges for being able to support hotplugging adds many special
> > >>>> cases to the bridge driver and still requires more custom API to correctly add
> > >>>> the second bridge. I don't think that this a viable path to go.
> > >>> Just jumping in here: You cannot hotplug/hotremove anything from a
> > >>> drm_device after drm_dev_register has been called, except
> > >>> drm_connector. I didn't dig into details here so not sure whether you
> > >>> want to late-bind your bridge after drm_dev_register is called or not,
> > >>> so might just be fyi and not relevant to the discussion.
> > >> Thanks. AFAIC that is exactly what is currently implemented in the exynos_drm
> > >> driver (i.e. Option B)
> > >>
> > >> exynos_dsi_bind configures the encoder and registers a DSI host. Afterwards,
> > >> exynos_drm_bind (as component_master_ops) calls drm_dev_register. Later, a DSI
> > >> device might attach to the DSI host and call exynos_dsi_host_attach. In
> > >> exynos_dsi_host_attach, the driver finds the drm_bridge for the DSI device and
> > >> attaches this bridge to the encoder _after_ drm_dev_register has been called.
> > >> This is invalid behavior, right?
> > > Definitely not supported, I don't think we have the right locks in place
> > > to make sure this works.
> > >
> > > Now if your _only_ adding a drm_bridge (and not an encoder or anything
> > > like that), and you are adding the drm_connector correctly (like a
> > > hotplugged DP MST sink), then that would at least work from a uapi pov.
> > > Because drm_bridge isn't exposed as an uapi object.
> > >
> > > But yeah, as-is, don't :-)
> > >
> > > The solution here is a bunch of EPROBE_DEFER handling until all your
> > > bridges are loaded, with or without the assistance of component.c
> > > framework. Only then call drm_dev_register.
> >
> > I have impression we have similar conversation already.
> >
> > As you stated drm_bridge and drm_panel are not exposed to userspace so
> > there shouldn't be problem with them from uapi PoV.
> >
> > On the other side drm_panel or drm_bridge are not used until pipeline
> > enters connected state (at least they were not some time ago :) ). The
> > issue is that bridge exposes drm_connector, but as you stated (again :)
> > ) connectors can be hotplugged, so in theory it should work. Practical
> > tests shows that it also works, but bugs can be still there.
> >
> > Bunch of EPROBE_DEFER was very slow (as a result userspace timeouted and
> > decided there is no display), and does not handle unbinding/re-binding
> > drivers.
> 
> Rebinding drivers should be fixed now, with a bunch of fixes in driver
> core. If not, we need to fix this more.
> 
> Also, EPROBE_DEFER is how this is supposed to work. If it's too slow,
> we need to fix EPROBE_DEFER (there's ideas for pre-sorting that never
> seem to go anywhere), not paper over it with bad architecture in
> drivers.

I've heard this argument multiple times, but it sounds more like an
attempt to ignore the problem and hope it will fall on someone else's
plate :-) Improvement in the probe deferral mechanism are certainly an
option to explore, but as far as I can tell nobody has proven that this
mechanism is or will be able to solve all problems related to probe
ordering dependencies. I wouldn't rule out the need for different
solutions for some of the issues.

> There's also a bit the issue that most userspace handles panels in a
> special way, and if they don't find the panel at first, that doesn't
> work. Stuff like "which is the main screen" on laptops.
> 
> So yeah please fix this properly.
>
> > >>>> This leaves us with:
> > >>>>
> > >>>> Option A) Standard drm_bridge behavior, which is currently implemented, but
> > >>>> incompatible with the currently expected behavior of exynos_drm.
> > >>>>
> > >>>> Option B) Creating the drm device without all bridges being attached, which
> > >>>> would work with the exynos_drm driver, but breaks for the standard drm_bridge
> > >>>> behavior, especially, if the encoder/connector is created at the beginning of
> > >>>> the pipeline and passed downwards when the bridges are attached.
> > >>>>
> > >>>> Option C) Extracting only low level register accesses into shared code, adding
> > >>>> a custom interface and implementing the drm_bridge handling in the platform
> > >>>> specific code.
> > >>>>
> > >>>> None of the options really convinces me.

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 10/16] drm/exynos: implement a drm bridge
  2021-02-04 17:26                             ` Laurent Pinchart
@ 2021-02-04 17:46                               ` Daniel Vetter
  2021-02-10  9:10                                 ` Frieder Schrempf
  0 siblings, 1 reply; 61+ messages in thread
From: Daniel Vetter @ 2021-02-04 17:46 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Marek Vasut, aisheng.dong, linux-samsung-soc, ch, Neil Armstrong,
	aford173, dri-devel, Bartlomiej Zolnierkiewicz, Seung-Woo Kim,
	Michael Tretter, Krzysztof Kozlowski, frieder.schrempf,
	Andrzej Hajda, dl-linux-imx, Sascha Hauer, Joonyoung Shim,
	sylvester.nawrocki, Marek Szyprowski, Shawn Guo, abel.vesa

On Thu, Feb 4, 2021 at 6:26 PM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Daniel,
>
> On Thu, Feb 04, 2021 at 06:19:22PM +0100, Daniel Vetter wrote:
> > On Thu, Feb 4, 2021 at 5:28 PM Andrzej Hajda wrote:
> > > W dniu 04.02.2021 o 17:05, Daniel Vetter pisze:
> > > > On Thu, Feb 04, 2021 at 11:56:32AM +0100, Michael Tretter wrote:
> > > >> On Thu, 04 Feb 2021 11:17:49 +0100, Daniel Vetter wrote:
> > > >>> On Wed, Feb 3, 2021 at 9:32 PM Michael Tretter wrote:
> > > >>>> On Mon, 01 Feb 2021 17:33:14 +0100, Michael Tretter wrote:
> > > >>>>> On Tue, 15 Sep 2020 21:40:40 +0200, Andrzej Hajda wrote:
> > > >>>>>> W dniu 14.09.2020 o 23:19, Andrzej Hajda pisze:
> > > >>>>>>> On 14.09.2020 22:01, Michael Tretter wrote:
> > > >>>>>>>> On Mon, 14 Sep 2020 14:31:19 +0200, Marek Szyprowski wrote:
> > > >>>>>>>>> On 14.09.2020 10:29, Marek Szyprowski wrote:
> > > >>>>>>>>>> On 11.09.2020 15:54, Michael Tretter wrote:
> > > >>>>>>>>>>> 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@pengutronix.de>
> > > >>>>>>>>>> This one (and the whole series applied) still fails on Exynos boards:
> > > >>>>>>>>>>
> > > >>>>>>>>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping
> > > >>>>>>>>>> operations
> > > >>>>>>>>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> > > >>>>>>>>>> OF: graph: no port node found in /soc/dsi@11c80000
> > > >>>>>>>>>> 8<--- cut here ---
> > > >>>>>>>>>> Unable to handle kernel NULL pointer dereference at virtual address
> > > >>>>>>>>>> 00000084
> > > >>>>>>>>>> pgd = (ptrval)
> > > >>>>>>>>>> [00000084] *pgd=00000000
> > > >>>>>>>>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> > > >>>>>>>>>> Modules linked in:
> > > >>>>>>>>>> CPU: 1 PID: 1 Comm: swapper/0 Not tainted
> > > >>>>>>>>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec #1608
> > > >>>>>>>>>> Hardware name: Samsung Exynos (Flattened Device Tree)
> > > >>>>>>>>>> PC is at drm_bridge_attach+0x18/0x164
> > > >>>>>>>>>> LR is at exynos_dsi_bind+0x88/0xa8
> > > >>>>>>>>>> pc : [<c0628c08>]    lr : [<c064d560>]    psr: 20000013
> > > >>>>>>>>>> sp : ef0dfca8  ip : 00000002  fp : c13190e0
> > > >>>>>>>>>> r10: 00000000  r9 : ee46d580  r8 : c13190e0
> > > >>>>>>>>>> r7 : ee438800  r6 : 00000018  r5 : ef253810  r4 : ef39e840
> > > >>>>>>>>>> r3 : 00000000  r2 : 00000018  r1 : ef39e888  r0 : ef39e840
> > > >>>>>>>>>> Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> > > >>>>>>>>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> > > >>>>>>>>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> > > >>>>>>>>>> Stack: (0xef0dfca8 to 0xef0e0000)
> > > >>>>>>>>>> ...
> > > >>>>>>>>>> [<c0628c08>] (drm_bridge_attach) from [<c064d560>]
> > > >>>>>>>>>> (exynos_dsi_bind+0x88/0xa8)
> > > >>>>>>>>>> [<c064d560>] (exynos_dsi_bind) from [<c066a800>]
> > > >>>>>>>>>> (component_bind_all+0xfc/0x290)
> > > >>>>>>>>>> [<c066a800>] (component_bind_all) from [<c0649dc0>]
> > > >>>>>>>>>> (exynos_drm_bind+0xe4/0x19c)
> > > >>>>>>>>>> [<c0649dc0>] (exynos_drm_bind) from [<c066ad74>]
> > > >>>>>>>>>> (try_to_bring_up_master+0x1e4/0x2c4)
> > > >>>>>>>>>> [<c066ad74>] (try_to_bring_up_master) from [<c066b2b4>]
> > > >>>>>>>>>> (component_master_add_with_match+0xd4/0x108)
> > > >>>>>>>>>> [<c066b2b4>] (component_master_add_with_match) from [<c0649ae8>]
> > > >>>>>>>>>> (exynos_drm_platform_probe+0xe4/0x110)
> > > >>>>>>>>>> [<c0649ae8>] (exynos_drm_platform_probe) from [<c0674e6c>]
> > > >>>>>>>>>> (platform_drv_probe+0x6c/0xa4)
> > > >>>>>>>>>> [<c0674e6c>] (platform_drv_probe) from [<c067242c>]
> > > >>>>>>>>>> (really_probe+0x200/0x4fc)
> > > >>>>>>>>>> [<c067242c>] (really_probe) from [<c06728f0>]
> > > >>>>>>>>>> (driver_probe_device+0x78/0x1fc)
> > > >>>>>>>>>> [<c06728f0>] (driver_probe_device) from [<c0672cd8>]
> > > >>>>>>>>>> (device_driver_attach+0x58/0x60)
> > > >>>>>>>>>> [<c0672cd8>] (device_driver_attach) from [<c0672dbc>]
> > > >>>>>>>>>> (__driver_attach+0xdc/0x174)
> > > >>>>>>>>>> [<c0672dbc>] (__driver_attach) from [<c06701b4>]
> > > >>>>>>>>>> (bus_for_each_dev+0x68/0xb4)
> > > >>>>>>>>>> [<c06701b4>] (bus_for_each_dev) from [<c06714e8>]
> > > >>>>>>>>>> (bus_add_driver+0x158/0x214)
> > > >>>>>>>>>> [<c06714e8>] (bus_add_driver) from [<c0673c1c>]
> > > >>>>>>>>>> (driver_register+0x78/0x110)
> > > >>>>>>>>>> [<c0673c1c>] (driver_register) from [<c0649ca8>]
> > > >>>>>>>>>> (exynos_drm_init+0xe4/0x118)
> > > >>>>>>>>>> [<c0649ca8>] (exynos_drm_init) from [<c0102484>]
> > > >>>>>>>>>> (do_one_initcall+0x8c/0x42c)
> > > >>>>>>>>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
> > > >>>>>>>>>> (kernel_init_freeable+0x190/0x1dc)
> > > >>>>>>>>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7880>]
> > > >>>>>>>>>> (kernel_init+0x8/0x118)
> > > >>>>>>>>>> [<c0af7880>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> > > >>>>>>>>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
> > > >>>>>>>>>> ...
> > > >>>>>>>>>> ---[ end trace ee27f313f9ed9da1 ]---
> > > >>>>>>>>>>
> > > >>>>>>>>>> # arm-linux-gnueabi-addr2line -e vmlinux c0628c08
> > > >>>>>>>>>> drivers/gpu/drm/drm_bridge.c:184 (discriminator 1)
> > > >>>>>>>>>>
> > > >>>>>>>>>> I will try to debug it a bit more today.
> > > >>>>>>>>> The above crash has been caused by lack of in_bridge initialization to
> > > >>>>>>>>> NULL in exynos_dsi_bind() in this patch. However, fixing it reveals
> > > >>>>>>>>> another issue:
> > > >>>>>>>>>
> > > >>>>>>>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
> > > >>>>>>>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> > > >>>>>>>>> OF: graph: no port node found in /soc/dsi@11c80000
> > > >>>>>>>>> 8<--- cut here ---
> > > >>>>>>>>> Unable to handle kernel NULL pointer dereference at virtual address
> > > >>>>>>>>> 00000280
> > > >>>>>>>>> pgd = (ptrval)
> > > >>>>>>>>> [00000280] *pgd=00000000
> > > >>>>>>>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> > > >>>>>>>>> Modules linked in:
> > > >>>>>>>>> CPU: 0 PID: 1 Comm: swapper/0 Not tainted
> > > >>>>>>>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec-dirty #1613
> > > >>>>>>>>> Hardware name: Samsung Exynos (Flattened Device Tree)
> > > >>>>>>>>> PC is at __mutex_lock+0x54/0xb18
> > > >>>>>>>>> LR is at lock_is_held_type+0x80/0x138
> > > >>>>>>>>> pc : [<c0afc920>]    lr : [<c0af63e8>]    psr: 60000013
> > > >>>>>>>>> sp : ef0dfd30  ip : 33937b74  fp : c13193c8
> > > >>>>>>>>> r10: c1208eec  r9 : 00000000  r8 : ee45f808
> > > >>>>>>>>> r7 : c19561a4  r6 : 00000000  r5 : 00000000  r4 : 0000024c
> > > >>>>>>>>> r3 : 00000000  r2 : 00204140  r1 : c124f13c  r0 : 00000000
> > > >>>>>>>>> Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> > > >>>>>>>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> > > >>>>>>>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> > > >>>>>>>>> Stack: (0xef0dfd30 to 0xef0e0000)
> > > >>>>>>>>> ...
> > > >>>>>>>>> [<c0afc920>] (__mutex_lock) from [<c0afd400>]
> > > >>>>>>>>> (mutex_lock_nested+0x1c/0x24)
> > > >>>>>>>>> [<c0afd400>] (mutex_lock_nested) from [<c064d4b8>]
> > > >>>>>>>>> (__exynos_dsi_host_attach+0x20/0x6c)
> > > >>>>>>>>> [<c064d4b8>] (__exynos_dsi_host_attach) from [<c064d914>]
> > > >>>>>>>>> (exynos_dsi_host_attach+0x70/0x194)
> > > >>>>>>>>> [<c064d914>] (exynos_dsi_host_attach) from [<c0656b64>]
> > > >>>>>>>>> (s6e8aa0_probe+0x1b0/0x218)
> > > >>>>>>>>> [<c0656b64>] (s6e8aa0_probe) from [<c0672530>]
> > > >>>>>>>>> (really_probe+0x200/0x4fc)
> > > >>>>>>>>> [<c0672530>] (really_probe) from [<c06729f4>]
> > > >>>>>>>>> (driver_probe_device+0x78/0x1fc)
> > > >>>>>>>>> [<c06729f4>] (driver_probe_device) from [<c0672ddc>]
> > > >>>>>>>>> (device_driver_attach+0x58/0x60)
> > > >>>>>>>>> [<c0672ddc>] (device_driver_attach) from [<c0672ec0>]
> > > >>>>>>>>> (__driver_attach+0xdc/0x174)
> > > >>>>>>>>> [<c0672ec0>] (__driver_attach) from [<c06702b8>]
> > > >>>>>>>>> (bus_for_each_dev+0x68/0xb4)
> > > >>>>>>>>> [<c06702b8>] (bus_for_each_dev) from [<c06715ec>]
> > > >>>>>>>>> (bus_add_driver+0x158/0x214)
> > > >>>>>>>>> [<c06715ec>] (bus_add_driver) from [<c0673d20>]
> > > >>>>>>>>> (driver_register+0x78/0x110)
> > > >>>>>>>>> [<c0673d20>] (driver_register) from [<c0102484>]
> > > >>>>>>>>> (do_one_initcall+0x8c/0x42c)
> > > >>>>>>>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
> > > >>>>>>>>> (kernel_init_freeable+0x190/0x1dc)
> > > >>>>>>>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7988>]
> > > >>>>>>>>> (kernel_init+0x8/0x118)
> > > >>>>>>>>> [<c0af7988>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> > > >>>>>>>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
> > > >>>>>>>>> ...
> > > >>>>>>>>> ---[ end trace c06e996ec2e8234d ]---
> > > >>>>>>>>>
> > > >>>>>>>>> This means that dsi->encoder.dev is not initialized in
> > > >>>>>>>>> __exynos_dsi_host_attach().
> > > >>>>>>>>>
> > > >>>>>>>>> This happens, because drm_bridge_attach() in exynos_dsi_bind() returned
> > > >>>>>>>>> earlier -517 (deferred probe), what causes cleanup of encoder and
> > > >>>>>>>>> release of all drm resources.
> > > >>>>>>>>>
> > > >>>>>>>>> Then however, the panel tries to register itself and
> > > >>>>>>>>> exynos_dsi_host_attach() tries to access the released encoder (which is
> > > >>>>>>>>> zeroed in drm_encoder_release) and rest of resources, what causes
> > > >>>>>>>>> failure.
> > > >>>>>>>>>
> > > >>>>>>>>> It looks that something is missing. Maybe mipi host has to be
> > > >>>>>>>>> registered
> > > >>>>>>>>> later, when bridge is ready? I have no idea how it is handled before
> > > >>>>>>>>> this patch. Andrzej, could you comment it a bit?
> > > >>>>>>>> I intentionally changed the order, because if another bridge follows
> > > >>>>>>>> in the
> > > >>>>>>>> pipeline, the probe of the drm driver has to be deferred until some
> > > >>>>>>>> bridge
> > > >>>>>>>> provides a connector. The next bridge registers itself via the
> > > >>>>>>>> host_attach
> > > >>>>>>>> function and the deferral is ensured via the bind for the bind/unbind
> > > >>>>>>>> API or
> > > >>>>>>>> the bridge_attach function otherwise.
> > > >>>>>>>>
> > > >>>>>>>> On the other hand, the bridge does not have an encoder until the mipi
> > > >>>>>>>> device
> > > >>>>>>>> has been attached.
> > > >>>>>>>>
> > > >>>>>>>> As a solution, the exynos dsi driver must initialize the encoder in
> > > >>>>>>>> exynos_dsi_probe instead of in exynos_dsi_bind and access the encoder
> > > >>>>>>>> via
> > > >>>>>>>> exynos_dsi instead of the bridge.
> > > >>>>>>>>
> > > >>>>>>>> Can you try to move everything except samsung_dsim_bind from
> > > >>>>>>>> exynos_dsi_bind
> > > >>>>>>>> to exynos_dsi_probe (respectively for unbind) and report if it fixes the
> > > >>>>>>>> crash.
> > > >>>>>>>
> > > >>>>>>> The original behaviour is that encoder (exynos_dsi) is registered
> > > >>>>>>> regardless of sink presence (initially panel, later also bridge) - it
> > > >>>>>>> avoids multiple issues with deferred probe, device driver bind/unbind
> > > >>>>>>> and module load/unload. Appearance or disappearance of sink is
> > > >>>>>>> reported to host nicely via DSI attach/detach callbacks - and it is
> > > >>>>>>> reflected in drm world as change state of the connector.
> > > >>>>>>>
> > > >>>>>>> Registering DSI host in bind and unregistering in unbind assures that
> > > >>>>>>> if mipi_dsi device is attached/detached the drm device is always
> > > >>>>>>> present - it makes device/driver binding race free and allows to avoid
> > > >>>>>>> additional locking.
> > > >>>>>>>
> > > >>>>>>> Moving DSI host registration to probe changes everything, for sure it
> > > >>>>>>> breaks the nice feature of DSI attach/detach callbacks and apparently
> > > >>>>>>> can cause different issues depending on device bind order.
> > > >>>>>>>
> > > >>>>>>> I will try to look at the patches tomorrow and maybe I can find more
> > > >>>>>>> constructive comments :)
> > > >>>>>>
> > > >>>>>> As I said yesterday, exynos_dsi driver uses dsi host attach/detach
> > > >>>>>> callbacks to control appearance/disappearance of downstream device. It
> > > >>>>>> allows to:
> > > >>>>>>
> > > >>>>>> 1. Safely bind/unbind different device drivers at any time and at any
> > > >>>>>> order, without killing exynos_drm and/or crashing system.
> > > >>>>>>
> > > >>>>>> 2. Avoid issues with late drm init - on some platforms exynos_drm device
> > > >>>>>> appeared too late, due to deferred probe, and resulted in black screen
> > > >>>>>> in userspace.
> > > >>>>>>
> > > >>>>>>
> > > >>>>>> Now if we want to convert exynos_dsi to drm_bridge I see following options:
> > > >>>>>>
> > > >>>>>> A. Forgot about callbacks and make the exynos_drm to defer probing until
> > > >>>>>> exynos_dsi bridge is available, probably it will cause later exynos_drm
> > > >>>>>> appearance, thus probably black screen on some targets. So for sure it
> > > >>>>>> will be suboptimal. Making it bridge unbind safe would be another
> > > >>>>>> problem, but most developers do not care about it so why should we? :)
> > > >>>>>>
> > > >>>>>> B. Try to mimic current behaviour - exynos_dsi register bridge ASAP,
> > > >>>>>> even if downstream devices are not yet attached, on attach/detach notify
> > > >>>>>> drm about it via connector status change, for this dsi_host registration
> > > >>>>>> should be performed from drm_bridge attach, I guess.
> > > >>>>>>
> > > >>>>>>
> > > >>>>>> Option A is more standard, but is unsafe and causes other issues.
> > > >>>>>>
> > > >>>>>> Option B keeps current behaviour.
> > > >>>>> Maybe we can have both, but I am not sure, if I am missing something:
> > > >>>>>
> > > >>>>> I still prefer option A for the samsung-dsim driver, because it is more
> > > >>>>> standard, simpler and avoids issues with encoders, connectors or handling
> > > >>>>> hotplug.
> > > >>>>>
> > > >>>>> The idea is to use two bridges in the exynos-dsi driver: One bridge in the
> > > >>>>> samsung-dsim driver which implements option A and defers probing of the drm
> > > >>>>> driver until the next bridge is attached. And a second bridge in the
> > > >>>>> exynos_dsi that attaches to the first bridge (thus, allowing the exynos_drm
> > > >>>>> device to appear) and implements the hotplug handling for notifying drm via
> > > >>>>> connector status change.
> > > >>>>>
> > > >>>>> The driver for the i.MX8M would use the samsung-dsim bridge without an
> > > >>>>> additional bridge.
> > > >>>>>
> > > >>>>> This allows the samsung-dsim driver to expose the standard behavior while the
> > > >>>>> exynos_dsi may stick to the existing behavior for the exynos_drm driver.
> > > >>>>>
> > > >>>>> I hope this makes sense and does not sound too crazy. It might be difficult to
> > > >>>>> get the probing and mipi host/device registration correct, but I will try, if
> > > >>>>> this can work.
> > > >>>> Adding two bridges for being able to support hotplugging adds many special
> > > >>>> cases to the bridge driver and still requires more custom API to correctly add
> > > >>>> the second bridge. I don't think that this a viable path to go.
> > > >>> Just jumping in here: You cannot hotplug/hotremove anything from a
> > > >>> drm_device after drm_dev_register has been called, except
> > > >>> drm_connector. I didn't dig into details here so not sure whether you
> > > >>> want to late-bind your bridge after drm_dev_register is called or not,
> > > >>> so might just be fyi and not relevant to the discussion.
> > > >> Thanks. AFAIC that is exactly what is currently implemented in the exynos_drm
> > > >> driver (i.e. Option B)
> > > >>
> > > >> exynos_dsi_bind configures the encoder and registers a DSI host. Afterwards,
> > > >> exynos_drm_bind (as component_master_ops) calls drm_dev_register. Later, a DSI
> > > >> device might attach to the DSI host and call exynos_dsi_host_attach. In
> > > >> exynos_dsi_host_attach, the driver finds the drm_bridge for the DSI device and
> > > >> attaches this bridge to the encoder _after_ drm_dev_register has been called.
> > > >> This is invalid behavior, right?
> > > > Definitely not supported, I don't think we have the right locks in place
> > > > to make sure this works.
> > > >
> > > > Now if your _only_ adding a drm_bridge (and not an encoder or anything
> > > > like that), and you are adding the drm_connector correctly (like a
> > > > hotplugged DP MST sink), then that would at least work from a uapi pov.
> > > > Because drm_bridge isn't exposed as an uapi object.
> > > >
> > > > But yeah, as-is, don't :-)
> > > >
> > > > The solution here is a bunch of EPROBE_DEFER handling until all your
> > > > bridges are loaded, with or without the assistance of component.c
> > > > framework. Only then call drm_dev_register.
> > >
> > > I have impression we have similar conversation already.
> > >
> > > As you stated drm_bridge and drm_panel are not exposed to userspace so
> > > there shouldn't be problem with them from uapi PoV.
> > >
> > > On the other side drm_panel or drm_bridge are not used until pipeline
> > > enters connected state (at least they were not some time ago :) ). The
> > > issue is that bridge exposes drm_connector, but as you stated (again :)
> > > ) connectors can be hotplugged, so in theory it should work. Practical
> > > tests shows that it also works, but bugs can be still there.
> > >
> > > Bunch of EPROBE_DEFER was very slow (as a result userspace timeouted and
> > > decided there is no display), and does not handle unbinding/re-binding
> > > drivers.
> >
> > Rebinding drivers should be fixed now, with a bunch of fixes in driver
> > core. If not, we need to fix this more.
> >
> > Also, EPROBE_DEFER is how this is supposed to work. If it's too slow,
> > we need to fix EPROBE_DEFER (there's ideas for pre-sorting that never
> > seem to go anywhere), not paper over it with bad architecture in
> > drivers.
>
> I've heard this argument multiple times, but it sounds more like an
> attempt to ignore the problem and hope it will fall on someone else's
> plate :-) Improvement in the probe deferral mechanism are certainly an
> option to explore, but as far as I can tell nobody has proven that this
> mechanism is or will be able to solve all problems related to probe
> ordering dependencies. I wouldn't rule out the need for different
> solutions for some of the issues.

Then build another one. But adding hotplug for stuff that is there,
and shouldn't be hotplugged, just because it's easier on driver
writers and harder on userspace isn't really a good approach.
-Daniel

> > There's also a bit the issue that most userspace handles panels in a
> > special way, and if they don't find the panel at first, that doesn't
> > work. Stuff like "which is the main screen" on laptops.
> >
> > So yeah please fix this properly.
> >
> > > >>>> This leaves us with:
> > > >>>>
> > > >>>> Option A) Standard drm_bridge behavior, which is currently implemented, but
> > > >>>> incompatible with the currently expected behavior of exynos_drm.
> > > >>>>
> > > >>>> Option B) Creating the drm device without all bridges being attached, which
> > > >>>> would work with the exynos_drm driver, but breaks for the standard drm_bridge
> > > >>>> behavior, especially, if the encoder/connector is created at the beginning of
> > > >>>> the pipeline and passed downwards when the bridges are attached.
> > > >>>>
> > > >>>> Option C) Extracting only low level register accesses into shared code, adding
> > > >>>> a custom interface and implementing the drm_bridge handling in the platform
> > > >>>> specific code.
> > > >>>>
> > > >>>> None of the options really convinces me.
>
> --
> Regards,
>
> Laurent Pinchart



-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 10/16] drm/exynos: implement a drm bridge
  2021-02-04 17:46                               ` Daniel Vetter
@ 2021-02-10  9:10                                 ` Frieder Schrempf
  2021-02-18  8:04                                   ` Michael Tretter
  0 siblings, 1 reply; 61+ messages in thread
From: Frieder Schrempf @ 2021-02-10  9:10 UTC (permalink / raw)
  To: Daniel Vetter, Laurent Pinchart
  Cc: Marek Vasut, aisheng.dong, linux-samsung-soc, ch, Neil Armstrong,
	aford173, dri-devel, Bartlomiej Zolnierkiewicz, Seung-Woo Kim,
	Michael Tretter, Krzysztof Kozlowski, Andrzej Hajda,
	dl-linux-imx, Sascha Hauer, Joonyoung Shim, sylvester.nawrocki,
	Marek Szyprowski, Shawn Guo, abel.vesa

On 04.02.21 18:46, Daniel Vetter wrote:
> On Thu, Feb 4, 2021 at 6:26 PM Laurent Pinchart
> <laurent.pinchart@ideasonboard.com> wrote:
>>
>> Hi Daniel,
>>
>> On Thu, Feb 04, 2021 at 06:19:22PM +0100, Daniel Vetter wrote:
>>> On Thu, Feb 4, 2021 at 5:28 PM Andrzej Hajda wrote:
>>>> W dniu 04.02.2021 o 17:05, Daniel Vetter pisze:
>>>>> On Thu, Feb 04, 2021 at 11:56:32AM +0100, Michael Tretter wrote:
>>>>>> On Thu, 04 Feb 2021 11:17:49 +0100, Daniel Vetter wrote:
>>>>>>> On Wed, Feb 3, 2021 at 9:32 PM Michael Tretter wrote:
>>>>>>>> On Mon, 01 Feb 2021 17:33:14 +0100, Michael Tretter wrote:
>>>>>>>>> On Tue, 15 Sep 2020 21:40:40 +0200, Andrzej Hajda wrote:
>>>>>>>>>> W dniu 14.09.2020 o 23:19, Andrzej Hajda pisze:
>>>>>>>>>>> On 14.09.2020 22:01, Michael Tretter wrote:
>>>>>>>>>>>> On Mon, 14 Sep 2020 14:31:19 +0200, Marek Szyprowski wrote:
>>>>>>>>>>>>> On 14.09.2020 10:29, Marek Szyprowski wrote:
>>>>>>>>>>>>>> On 11.09.2020 15:54, Michael Tretter wrote:
>>>>>>>>>>>>>>> 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@pengutronix.de>
>>>>>>>>>>>>>> This one (and the whole series applied) still fails on Exynos boards:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping
>>>>>>>>>>>>>> operations
>>>>>>>>>>>>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
>>>>>>>>>>>>>> OF: graph: no port node found in /soc/dsi@11c80000
>>>>>>>>>>>>>> 8<--- cut here ---
>>>>>>>>>>>>>> Unable to handle kernel NULL pointer dereference at virtual address
>>>>>>>>>>>>>> 00000084
>>>>>>>>>>>>>> pgd = (ptrval)
>>>>>>>>>>>>>> [00000084] *pgd=00000000
>>>>>>>>>>>>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
>>>>>>>>>>>>>> Modules linked in:
>>>>>>>>>>>>>> CPU: 1 PID: 1 Comm: swapper/0 Not tainted
>>>>>>>>>>>>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec #1608
>>>>>>>>>>>>>> Hardware name: Samsung Exynos (Flattened Device Tree)
>>>>>>>>>>>>>> PC is at drm_bridge_attach+0x18/0x164
>>>>>>>>>>>>>> LR is at exynos_dsi_bind+0x88/0xa8
>>>>>>>>>>>>>> pc : [<c0628c08>]    lr : [<c064d560>]    psr: 20000013
>>>>>>>>>>>>>> sp : ef0dfca8  ip : 00000002  fp : c13190e0
>>>>>>>>>>>>>> r10: 00000000  r9 : ee46d580  r8 : c13190e0
>>>>>>>>>>>>>> r7 : ee438800  r6 : 00000018  r5 : ef253810  r4 : ef39e840
>>>>>>>>>>>>>> r3 : 00000000  r2 : 00000018  r1 : ef39e888  r0 : ef39e840
>>>>>>>>>>>>>> Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
>>>>>>>>>>>>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
>>>>>>>>>>>>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
>>>>>>>>>>>>>> Stack: (0xef0dfca8 to 0xef0e0000)
>>>>>>>>>>>>>> ...
>>>>>>>>>>>>>> [<c0628c08>] (drm_bridge_attach) from [<c064d560>]
>>>>>>>>>>>>>> (exynos_dsi_bind+0x88/0xa8)
>>>>>>>>>>>>>> [<c064d560>] (exynos_dsi_bind) from [<c066a800>]
>>>>>>>>>>>>>> (component_bind_all+0xfc/0x290)
>>>>>>>>>>>>>> [<c066a800>] (component_bind_all) from [<c0649dc0>]
>>>>>>>>>>>>>> (exynos_drm_bind+0xe4/0x19c)
>>>>>>>>>>>>>> [<c0649dc0>] (exynos_drm_bind) from [<c066ad74>]
>>>>>>>>>>>>>> (try_to_bring_up_master+0x1e4/0x2c4)
>>>>>>>>>>>>>> [<c066ad74>] (try_to_bring_up_master) from [<c066b2b4>]
>>>>>>>>>>>>>> (component_master_add_with_match+0xd4/0x108)
>>>>>>>>>>>>>> [<c066b2b4>] (component_master_add_with_match) from [<c0649ae8>]
>>>>>>>>>>>>>> (exynos_drm_platform_probe+0xe4/0x110)
>>>>>>>>>>>>>> [<c0649ae8>] (exynos_drm_platform_probe) from [<c0674e6c>]
>>>>>>>>>>>>>> (platform_drv_probe+0x6c/0xa4)
>>>>>>>>>>>>>> [<c0674e6c>] (platform_drv_probe) from [<c067242c>]
>>>>>>>>>>>>>> (really_probe+0x200/0x4fc)
>>>>>>>>>>>>>> [<c067242c>] (really_probe) from [<c06728f0>]
>>>>>>>>>>>>>> (driver_probe_device+0x78/0x1fc)
>>>>>>>>>>>>>> [<c06728f0>] (driver_probe_device) from [<c0672cd8>]
>>>>>>>>>>>>>> (device_driver_attach+0x58/0x60)
>>>>>>>>>>>>>> [<c0672cd8>] (device_driver_attach) from [<c0672dbc>]
>>>>>>>>>>>>>> (__driver_attach+0xdc/0x174)
>>>>>>>>>>>>>> [<c0672dbc>] (__driver_attach) from [<c06701b4>]
>>>>>>>>>>>>>> (bus_for_each_dev+0x68/0xb4)
>>>>>>>>>>>>>> [<c06701b4>] (bus_for_each_dev) from [<c06714e8>]
>>>>>>>>>>>>>> (bus_add_driver+0x158/0x214)
>>>>>>>>>>>>>> [<c06714e8>] (bus_add_driver) from [<c0673c1c>]
>>>>>>>>>>>>>> (driver_register+0x78/0x110)
>>>>>>>>>>>>>> [<c0673c1c>] (driver_register) from [<c0649ca8>]
>>>>>>>>>>>>>> (exynos_drm_init+0xe4/0x118)
>>>>>>>>>>>>>> [<c0649ca8>] (exynos_drm_init) from [<c0102484>]
>>>>>>>>>>>>>> (do_one_initcall+0x8c/0x42c)
>>>>>>>>>>>>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
>>>>>>>>>>>>>> (kernel_init_freeable+0x190/0x1dc)
>>>>>>>>>>>>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7880>]
>>>>>>>>>>>>>> (kernel_init+0x8/0x118)
>>>>>>>>>>>>>> [<c0af7880>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
>>>>>>>>>>>>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
>>>>>>>>>>>>>> ...
>>>>>>>>>>>>>> ---[ end trace ee27f313f9ed9da1 ]---
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> # arm-linux-gnueabi-addr2line -e vmlinux c0628c08
>>>>>>>>>>>>>> drivers/gpu/drm/drm_bridge.c:184 (discriminator 1)
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> I will try to debug it a bit more today.
>>>>>>>>>>>>> The above crash has been caused by lack of in_bridge initialization to
>>>>>>>>>>>>> NULL in exynos_dsi_bind() in this patch. However, fixing it reveals
>>>>>>>>>>>>> another issue:
>>>>>>>>>>>>>
>>>>>>>>>>>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
>>>>>>>>>>>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
>>>>>>>>>>>>> OF: graph: no port node found in /soc/dsi@11c80000
>>>>>>>>>>>>> 8<--- cut here ---
>>>>>>>>>>>>> Unable to handle kernel NULL pointer dereference at virtual address
>>>>>>>>>>>>> 00000280
>>>>>>>>>>>>> pgd = (ptrval)
>>>>>>>>>>>>> [00000280] *pgd=00000000
>>>>>>>>>>>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
>>>>>>>>>>>>> Modules linked in:
>>>>>>>>>>>>> CPU: 0 PID: 1 Comm: swapper/0 Not tainted
>>>>>>>>>>>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec-dirty #1613
>>>>>>>>>>>>> Hardware name: Samsung Exynos (Flattened Device Tree)
>>>>>>>>>>>>> PC is at __mutex_lock+0x54/0xb18
>>>>>>>>>>>>> LR is at lock_is_held_type+0x80/0x138
>>>>>>>>>>>>> pc : [<c0afc920>]    lr : [<c0af63e8>]    psr: 60000013
>>>>>>>>>>>>> sp : ef0dfd30  ip : 33937b74  fp : c13193c8
>>>>>>>>>>>>> r10: c1208eec  r9 : 00000000  r8 : ee45f808
>>>>>>>>>>>>> r7 : c19561a4  r6 : 00000000  r5 : 00000000  r4 : 0000024c
>>>>>>>>>>>>> r3 : 00000000  r2 : 00204140  r1 : c124f13c  r0 : 00000000
>>>>>>>>>>>>> Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
>>>>>>>>>>>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
>>>>>>>>>>>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
>>>>>>>>>>>>> Stack: (0xef0dfd30 to 0xef0e0000)
>>>>>>>>>>>>> ...
>>>>>>>>>>>>> [<c0afc920>] (__mutex_lock) from [<c0afd400>]
>>>>>>>>>>>>> (mutex_lock_nested+0x1c/0x24)
>>>>>>>>>>>>> [<c0afd400>] (mutex_lock_nested) from [<c064d4b8>]
>>>>>>>>>>>>> (__exynos_dsi_host_attach+0x20/0x6c)
>>>>>>>>>>>>> [<c064d4b8>] (__exynos_dsi_host_attach) from [<c064d914>]
>>>>>>>>>>>>> (exynos_dsi_host_attach+0x70/0x194)
>>>>>>>>>>>>> [<c064d914>] (exynos_dsi_host_attach) from [<c0656b64>]
>>>>>>>>>>>>> (s6e8aa0_probe+0x1b0/0x218)
>>>>>>>>>>>>> [<c0656b64>] (s6e8aa0_probe) from [<c0672530>]
>>>>>>>>>>>>> (really_probe+0x200/0x4fc)
>>>>>>>>>>>>> [<c0672530>] (really_probe) from [<c06729f4>]
>>>>>>>>>>>>> (driver_probe_device+0x78/0x1fc)
>>>>>>>>>>>>> [<c06729f4>] (driver_probe_device) from [<c0672ddc>]
>>>>>>>>>>>>> (device_driver_attach+0x58/0x60)
>>>>>>>>>>>>> [<c0672ddc>] (device_driver_attach) from [<c0672ec0>]
>>>>>>>>>>>>> (__driver_attach+0xdc/0x174)
>>>>>>>>>>>>> [<c0672ec0>] (__driver_attach) from [<c06702b8>]
>>>>>>>>>>>>> (bus_for_each_dev+0x68/0xb4)
>>>>>>>>>>>>> [<c06702b8>] (bus_for_each_dev) from [<c06715ec>]
>>>>>>>>>>>>> (bus_add_driver+0x158/0x214)
>>>>>>>>>>>>> [<c06715ec>] (bus_add_driver) from [<c0673d20>]
>>>>>>>>>>>>> (driver_register+0x78/0x110)
>>>>>>>>>>>>> [<c0673d20>] (driver_register) from [<c0102484>]
>>>>>>>>>>>>> (do_one_initcall+0x8c/0x42c)
>>>>>>>>>>>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
>>>>>>>>>>>>> (kernel_init_freeable+0x190/0x1dc)
>>>>>>>>>>>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7988>]
>>>>>>>>>>>>> (kernel_init+0x8/0x118)
>>>>>>>>>>>>> [<c0af7988>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
>>>>>>>>>>>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
>>>>>>>>>>>>> ...
>>>>>>>>>>>>> ---[ end trace c06e996ec2e8234d ]---
>>>>>>>>>>>>>
>>>>>>>>>>>>> This means that dsi->encoder.dev is not initialized in
>>>>>>>>>>>>> __exynos_dsi_host_attach().
>>>>>>>>>>>>>
>>>>>>>>>>>>> This happens, because drm_bridge_attach() in exynos_dsi_bind() returned
>>>>>>>>>>>>> earlier -517 (deferred probe), what causes cleanup of encoder and
>>>>>>>>>>>>> release of all drm resources.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Then however, the panel tries to register itself and
>>>>>>>>>>>>> exynos_dsi_host_attach() tries to access the released encoder (which is
>>>>>>>>>>>>> zeroed in drm_encoder_release) and rest of resources, what causes
>>>>>>>>>>>>> failure.
>>>>>>>>>>>>>
>>>>>>>>>>>>> It looks that something is missing. Maybe mipi host has to be
>>>>>>>>>>>>> registered
>>>>>>>>>>>>> later, when bridge is ready? I have no idea how it is handled before
>>>>>>>>>>>>> this patch. Andrzej, could you comment it a bit?
>>>>>>>>>>>> I intentionally changed the order, because if another bridge follows
>>>>>>>>>>>> in the
>>>>>>>>>>>> pipeline, the probe of the drm driver has to be deferred until some
>>>>>>>>>>>> bridge
>>>>>>>>>>>> provides a connector. The next bridge registers itself via the
>>>>>>>>>>>> host_attach
>>>>>>>>>>>> function and the deferral is ensured via the bind for the bind/unbind
>>>>>>>>>>>> API or
>>>>>>>>>>>> the bridge_attach function otherwise.
>>>>>>>>>>>>
>>>>>>>>>>>> On the other hand, the bridge does not have an encoder until the mipi
>>>>>>>>>>>> device
>>>>>>>>>>>> has been attached.
>>>>>>>>>>>>
>>>>>>>>>>>> As a solution, the exynos dsi driver must initialize the encoder in
>>>>>>>>>>>> exynos_dsi_probe instead of in exynos_dsi_bind and access the encoder
>>>>>>>>>>>> via
>>>>>>>>>>>> exynos_dsi instead of the bridge.
>>>>>>>>>>>>
>>>>>>>>>>>> Can you try to move everything except samsung_dsim_bind from
>>>>>>>>>>>> exynos_dsi_bind
>>>>>>>>>>>> to exynos_dsi_probe (respectively for unbind) and report if it fixes the
>>>>>>>>>>>> crash.
>>>>>>>>>>>
>>>>>>>>>>> The original behaviour is that encoder (exynos_dsi) is registered
>>>>>>>>>>> regardless of sink presence (initially panel, later also bridge) - it
>>>>>>>>>>> avoids multiple issues with deferred probe, device driver bind/unbind
>>>>>>>>>>> and module load/unload. Appearance or disappearance of sink is
>>>>>>>>>>> reported to host nicely via DSI attach/detach callbacks - and it is
>>>>>>>>>>> reflected in drm world as change state of the connector.
>>>>>>>>>>>
>>>>>>>>>>> Registering DSI host in bind and unregistering in unbind assures that
>>>>>>>>>>> if mipi_dsi device is attached/detached the drm device is always
>>>>>>>>>>> present - it makes device/driver binding race free and allows to avoid
>>>>>>>>>>> additional locking.
>>>>>>>>>>>
>>>>>>>>>>> Moving DSI host registration to probe changes everything, for sure it
>>>>>>>>>>> breaks the nice feature of DSI attach/detach callbacks and apparently
>>>>>>>>>>> can cause different issues depending on device bind order.
>>>>>>>>>>>
>>>>>>>>>>> I will try to look at the patches tomorrow and maybe I can find more
>>>>>>>>>>> constructive comments :)
>>>>>>>>>>
>>>>>>>>>> As I said yesterday, exynos_dsi driver uses dsi host attach/detach
>>>>>>>>>> callbacks to control appearance/disappearance of downstream device. It
>>>>>>>>>> allows to:
>>>>>>>>>>
>>>>>>>>>> 1. Safely bind/unbind different device drivers at any time and at any
>>>>>>>>>> order, without killing exynos_drm and/or crashing system.
>>>>>>>>>>
>>>>>>>>>> 2. Avoid issues with late drm init - on some platforms exynos_drm device
>>>>>>>>>> appeared too late, due to deferred probe, and resulted in black screen
>>>>>>>>>> in userspace.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Now if we want to convert exynos_dsi to drm_bridge I see following options:
>>>>>>>>>>
>>>>>>>>>> A. Forgot about callbacks and make the exynos_drm to defer probing until
>>>>>>>>>> exynos_dsi bridge is available, probably it will cause later exynos_drm
>>>>>>>>>> appearance, thus probably black screen on some targets. So for sure it
>>>>>>>>>> will be suboptimal. Making it bridge unbind safe would be another
>>>>>>>>>> problem, but most developers do not care about it so why should we? :)
>>>>>>>>>>
>>>>>>>>>> B. Try to mimic current behaviour - exynos_dsi register bridge ASAP,
>>>>>>>>>> even if downstream devices are not yet attached, on attach/detach notify
>>>>>>>>>> drm about it via connector status change, for this dsi_host registration
>>>>>>>>>> should be performed from drm_bridge attach, I guess.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Option A is more standard, but is unsafe and causes other issues.
>>>>>>>>>>
>>>>>>>>>> Option B keeps current behaviour.
>>>>>>>>> Maybe we can have both, but I am not sure, if I am missing something:
>>>>>>>>>
>>>>>>>>> I still prefer option A for the samsung-dsim driver, because it is more
>>>>>>>>> standard, simpler and avoids issues with encoders, connectors or handling
>>>>>>>>> hotplug.
>>>>>>>>>
>>>>>>>>> The idea is to use two bridges in the exynos-dsi driver: One bridge in the
>>>>>>>>> samsung-dsim driver which implements option A and defers probing of the drm
>>>>>>>>> driver until the next bridge is attached. And a second bridge in the
>>>>>>>>> exynos_dsi that attaches to the first bridge (thus, allowing the exynos_drm
>>>>>>>>> device to appear) and implements the hotplug handling for notifying drm via
>>>>>>>>> connector status change.
>>>>>>>>>
>>>>>>>>> The driver for the i.MX8M would use the samsung-dsim bridge without an
>>>>>>>>> additional bridge.
>>>>>>>>>
>>>>>>>>> This allows the samsung-dsim driver to expose the standard behavior while the
>>>>>>>>> exynos_dsi may stick to the existing behavior for the exynos_drm driver.
>>>>>>>>>
>>>>>>>>> I hope this makes sense and does not sound too crazy. It might be difficult to
>>>>>>>>> get the probing and mipi host/device registration correct, but I will try, if
>>>>>>>>> this can work.
>>>>>>>> Adding two bridges for being able to support hotplugging adds many special
>>>>>>>> cases to the bridge driver and still requires more custom API to correctly add
>>>>>>>> the second bridge. I don't think that this a viable path to go.
>>>>>>> Just jumping in here: You cannot hotplug/hotremove anything from a
>>>>>>> drm_device after drm_dev_register has been called, except
>>>>>>> drm_connector. I didn't dig into details here so not sure whether you
>>>>>>> want to late-bind your bridge after drm_dev_register is called or not,
>>>>>>> so might just be fyi and not relevant to the discussion.
>>>>>> Thanks. AFAIC that is exactly what is currently implemented in the exynos_drm
>>>>>> driver (i.e. Option B)
>>>>>>
>>>>>> exynos_dsi_bind configures the encoder and registers a DSI host. Afterwards,
>>>>>> exynos_drm_bind (as component_master_ops) calls drm_dev_register. Later, a DSI
>>>>>> device might attach to the DSI host and call exynos_dsi_host_attach. In
>>>>>> exynos_dsi_host_attach, the driver finds the drm_bridge for the DSI device and
>>>>>> attaches this bridge to the encoder _after_ drm_dev_register has been called.
>>>>>> This is invalid behavior, right?
>>>>> Definitely not supported, I don't think we have the right locks in place
>>>>> to make sure this works.
>>>>>
>>>>> Now if your _only_ adding a drm_bridge (and not an encoder or anything
>>>>> like that), and you are adding the drm_connector correctly (like a
>>>>> hotplugged DP MST sink), then that would at least work from a uapi pov.
>>>>> Because drm_bridge isn't exposed as an uapi object.
>>>>>
>>>>> But yeah, as-is, don't :-)
>>>>>
>>>>> The solution here is a bunch of EPROBE_DEFER handling until all your
>>>>> bridges are loaded, with or without the assistance of component.c
>>>>> framework. Only then call drm_dev_register.
>>>>
>>>> I have impression we have similar conversation already.
>>>>
>>>> As you stated drm_bridge and drm_panel are not exposed to userspace so
>>>> there shouldn't be problem with them from uapi PoV.
>>>>
>>>> On the other side drm_panel or drm_bridge are not used until pipeline
>>>> enters connected state (at least they were not some time ago :) ). The
>>>> issue is that bridge exposes drm_connector, but as you stated (again :)
>>>> ) connectors can be hotplugged, so in theory it should work. Practical
>>>> tests shows that it also works, but bugs can be still there.
>>>>
>>>> Bunch of EPROBE_DEFER was very slow (as a result userspace timeouted and
>>>> decided there is no display), and does not handle unbinding/re-binding
>>>> drivers.
>>>
>>> Rebinding drivers should be fixed now, with a bunch of fixes in driver
>>> core. If not, we need to fix this more.
>>>
>>> Also, EPROBE_DEFER is how this is supposed to work. If it's too slow,
>>> we need to fix EPROBE_DEFER (there's ideas for pre-sorting that never
>>> seem to go anywhere), not paper over it with bad architecture in
>>> drivers.
>>
>> I've heard this argument multiple times, but it sounds more like an
>> attempt to ignore the problem and hope it will fall on someone else's
>> plate :-) Improvement in the probe deferral mechanism are certainly an
>> option to explore, but as far as I can tell nobody has proven that this
>> mechanism is or will be able to solve all problems related to probe
>> ordering dependencies. I wouldn't rule out the need for different
>> solutions for some of the issues.
> 
> Then build another one. But adding hotplug for stuff that is there,
> and shouldn't be hotplugged, just because it's easier on driver
> writers and harder on userspace isn't really a good approach.
> -Daniel

I think it is quite clear that replacing or reworking the deferral 
mechanism is out of scope for this discussion, which is why I would like 
to come back to the original issue and sum this up as far as I 
understand it (which is not really far when it comes to the details):

We have the existing exynos driver that avoids the standard deferral 
mechanism in favor of something that works but Daniel describes as 
"definitely not supported".

We have a proposal from Michael for converting the driver to the 
standard drm_bridge behavior and more work from Michael and Marek based 
on this to implement the platform specific parts for i.MX8MM.

 From the i.MX8MM POV this approach already received some testing and 
looks good as far as I can judge. Upstreaming this solution is blocked 
because of objections from the Samsung maintainers.

Sorry if I'm being blunt or naive, but where to go from here?
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 10/16] drm/exynos: implement a drm bridge
  2021-02-10  9:10                                 ` Frieder Schrempf
@ 2021-02-18  8:04                                   ` Michael Tretter
  2021-02-18 16:02                                     ` Andrzej Hajda
  0 siblings, 1 reply; 61+ messages in thread
From: Michael Tretter @ 2021-02-18  8:04 UTC (permalink / raw)
  To: Frieder Schrempf
  Cc: ch, Neil Armstrong, dri-devel, Andrzej Hajda, Laurent Pinchart,
	Marek Szyprowski, Marek Vasut, linux-samsung-soc, abel.vesa,
	Krzysztof Kozlowski, dl-linux-imx, Sascha Hauer,
	Bartlomiej Zolnierkiewicz, sylvester.nawrocki, aford173,
	Joonyoung Shim, aisheng.dong, Seung-Woo Kim, kyungmin.park,
	Shawn Guo

On Wed, 10 Feb 2021 10:10:37 +0100, Frieder Schrempf wrote:
> On 04.02.21 18:46, Daniel Vetter wrote:
> > On Thu, Feb 4, 2021 at 6:26 PM Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
> > > On Thu, Feb 04, 2021 at 06:19:22PM +0100, Daniel Vetter wrote:
> > > > On Thu, Feb 4, 2021 at 5:28 PM Andrzej Hajda wrote:
> > > > > W dniu 04.02.2021 o 17:05, Daniel Vetter pisze:
> > > > > > On Thu, Feb 04, 2021 at 11:56:32AM +0100, Michael Tretter wrote:
> > > > > > > On Thu, 04 Feb 2021 11:17:49 +0100, Daniel Vetter wrote:
> > > > > > > > On Wed, Feb 3, 2021 at 9:32 PM Michael Tretter wrote:
> > > > > > > > > On Mon, 01 Feb 2021 17:33:14 +0100, Michael Tretter wrote:
> > > > > > > > > > On Tue, 15 Sep 2020 21:40:40 +0200, Andrzej Hajda wrote:
> > > > > > > > > > > W dniu 14.09.2020 o 23:19, Andrzej Hajda pisze:
> > > > > > > > > > > > On 14.09.2020 22:01, Michael Tretter wrote:
> > > > > > > > > > > > > On Mon, 14 Sep 2020 14:31:19 +0200, Marek Szyprowski wrote:
> > > > > > > > > > > > > > On 14.09.2020 10:29, Marek Szyprowski wrote:
> > > > > > > > > > > > > > > On 11.09.2020 15:54, Michael Tretter wrote:
> > > > > > > > > > > > > > > > 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@pengutronix.de>
> > > > > > > > > > > > > > > This one (and the whole series applied) still fails on Exynos boards:
> > > > > > > > > > > > > > > 
> > > > > > > > > > > > > > > [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping
> > > > > > > > > > > > > > > operations
> > > > > > > > > > > > > > > exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> > > > > > > > > > > > > > > OF: graph: no port node found in /soc/dsi@11c80000
> > > > > > > > > > > > > > > 8<--- cut here ---
> > > > > > > > > > > > > > > Unable to handle kernel NULL pointer dereference at virtual address
> > > > > > > > > > > > > > > 00000084
> > > > > > > > > > > > > > > pgd = (ptrval)
> > > > > > > > > > > > > > > [00000084] *pgd=00000000
> > > > > > > > > > > > > > > Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> > > > > > > > > > > > > > > Modules linked in:
> > > > > > > > > > > > > > > CPU: 1 PID: 1 Comm: swapper/0 Not tainted
> > > > > > > > > > > > > > > 5.9.0-rc4-next-20200911-00010-g417dc70d70ec #1608
> > > > > > > > > > > > > > > Hardware name: Samsung Exynos (Flattened Device Tree)
> > > > > > > > > > > > > > > PC is at drm_bridge_attach+0x18/0x164
> > > > > > > > > > > > > > > LR is at exynos_dsi_bind+0x88/0xa8
> > > > > > > > > > > > > > > pc : [<c0628c08>]    lr : [<c064d560>]    psr: 20000013
> > > > > > > > > > > > > > > sp : ef0dfca8  ip : 00000002  fp : c13190e0
> > > > > > > > > > > > > > > r10: 00000000  r9 : ee46d580  r8 : c13190e0
> > > > > > > > > > > > > > > r7 : ee438800  r6 : 00000018  r5 : ef253810  r4 : ef39e840
> > > > > > > > > > > > > > > r3 : 00000000  r2 : 00000018  r1 : ef39e888  r0 : ef39e840
> > > > > > > > > > > > > > > Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> > > > > > > > > > > > > > > Control: 10c5387d  Table: 4000404a  DAC: 00000051
> > > > > > > > > > > > > > > Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> > > > > > > > > > > > > > > Stack: (0xef0dfca8 to 0xef0e0000)
> > > > > > > > > > > > > > > ...
> > > > > > > > > > > > > > > [<c0628c08>] (drm_bridge_attach) from [<c064d560>]
> > > > > > > > > > > > > > > (exynos_dsi_bind+0x88/0xa8)
> > > > > > > > > > > > > > > [<c064d560>] (exynos_dsi_bind) from [<c066a800>]
> > > > > > > > > > > > > > > (component_bind_all+0xfc/0x290)
> > > > > > > > > > > > > > > [<c066a800>] (component_bind_all) from [<c0649dc0>]
> > > > > > > > > > > > > > > (exynos_drm_bind+0xe4/0x19c)
> > > > > > > > > > > > > > > [<c0649dc0>] (exynos_drm_bind) from [<c066ad74>]
> > > > > > > > > > > > > > > (try_to_bring_up_master+0x1e4/0x2c4)
> > > > > > > > > > > > > > > [<c066ad74>] (try_to_bring_up_master) from [<c066b2b4>]
> > > > > > > > > > > > > > > (component_master_add_with_match+0xd4/0x108)
> > > > > > > > > > > > > > > [<c066b2b4>] (component_master_add_with_match) from [<c0649ae8>]
> > > > > > > > > > > > > > > (exynos_drm_platform_probe+0xe4/0x110)
> > > > > > > > > > > > > > > [<c0649ae8>] (exynos_drm_platform_probe) from [<c0674e6c>]
> > > > > > > > > > > > > > > (platform_drv_probe+0x6c/0xa4)
> > > > > > > > > > > > > > > [<c0674e6c>] (platform_drv_probe) from [<c067242c>]
> > > > > > > > > > > > > > > (really_probe+0x200/0x4fc)
> > > > > > > > > > > > > > > [<c067242c>] (really_probe) from [<c06728f0>]
> > > > > > > > > > > > > > > (driver_probe_device+0x78/0x1fc)
> > > > > > > > > > > > > > > [<c06728f0>] (driver_probe_device) from [<c0672cd8>]
> > > > > > > > > > > > > > > (device_driver_attach+0x58/0x60)
> > > > > > > > > > > > > > > [<c0672cd8>] (device_driver_attach) from [<c0672dbc>]
> > > > > > > > > > > > > > > (__driver_attach+0xdc/0x174)
> > > > > > > > > > > > > > > [<c0672dbc>] (__driver_attach) from [<c06701b4>]
> > > > > > > > > > > > > > > (bus_for_each_dev+0x68/0xb4)
> > > > > > > > > > > > > > > [<c06701b4>] (bus_for_each_dev) from [<c06714e8>]
> > > > > > > > > > > > > > > (bus_add_driver+0x158/0x214)
> > > > > > > > > > > > > > > [<c06714e8>] (bus_add_driver) from [<c0673c1c>]
> > > > > > > > > > > > > > > (driver_register+0x78/0x110)
> > > > > > > > > > > > > > > [<c0673c1c>] (driver_register) from [<c0649ca8>]
> > > > > > > > > > > > > > > (exynos_drm_init+0xe4/0x118)
> > > > > > > > > > > > > > > [<c0649ca8>] (exynos_drm_init) from [<c0102484>]
> > > > > > > > > > > > > > > (do_one_initcall+0x8c/0x42c)
> > > > > > > > > > > > > > > [<c0102484>] (do_one_initcall) from [<c11011c0>]
> > > > > > > > > > > > > > > (kernel_init_freeable+0x190/0x1dc)
> > > > > > > > > > > > > > > [<c11011c0>] (kernel_init_freeable) from [<c0af7880>]
> > > > > > > > > > > > > > > (kernel_init+0x8/0x118)
> > > > > > > > > > > > > > > [<c0af7880>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> > > > > > > > > > > > > > > Exception stack(0xef0dffb0 to 0xef0dfff8)
> > > > > > > > > > > > > > > ...
> > > > > > > > > > > > > > > ---[ end trace ee27f313f9ed9da1 ]---
> > > > > > > > > > > > > > > 
> > > > > > > > > > > > > > > # arm-linux-gnueabi-addr2line -e vmlinux c0628c08
> > > > > > > > > > > > > > > drivers/gpu/drm/drm_bridge.c:184 (discriminator 1)
> > > > > > > > > > > > > > > 
> > > > > > > > > > > > > > > I will try to debug it a bit more today.
> > > > > > > > > > > > > > The above crash has been caused by lack of in_bridge initialization to
> > > > > > > > > > > > > > NULL in exynos_dsi_bind() in this patch. However, fixing it reveals
> > > > > > > > > > > > > > another issue:
> > > > > > > > > > > > > > 
> > > > > > > > > > > > > > [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
> > > > > > > > > > > > > > exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> > > > > > > > > > > > > > OF: graph: no port node found in /soc/dsi@11c80000
> > > > > > > > > > > > > > 8<--- cut here ---
> > > > > > > > > > > > > > Unable to handle kernel NULL pointer dereference at virtual address
> > > > > > > > > > > > > > 00000280
> > > > > > > > > > > > > > pgd = (ptrval)
> > > > > > > > > > > > > > [00000280] *pgd=00000000
> > > > > > > > > > > > > > Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> > > > > > > > > > > > > > Modules linked in:
> > > > > > > > > > > > > > CPU: 0 PID: 1 Comm: swapper/0 Not tainted
> > > > > > > > > > > > > > 5.9.0-rc4-next-20200911-00010-g417dc70d70ec-dirty #1613
> > > > > > > > > > > > > > Hardware name: Samsung Exynos (Flattened Device Tree)
> > > > > > > > > > > > > > PC is at __mutex_lock+0x54/0xb18
> > > > > > > > > > > > > > LR is at lock_is_held_type+0x80/0x138
> > > > > > > > > > > > > > pc : [<c0afc920>]    lr : [<c0af63e8>]    psr: 60000013
> > > > > > > > > > > > > > sp : ef0dfd30  ip : 33937b74  fp : c13193c8
> > > > > > > > > > > > > > r10: c1208eec  r9 : 00000000  r8 : ee45f808
> > > > > > > > > > > > > > r7 : c19561a4  r6 : 00000000  r5 : 00000000  r4 : 0000024c
> > > > > > > > > > > > > > r3 : 00000000  r2 : 00204140  r1 : c124f13c  r0 : 00000000
> > > > > > > > > > > > > > Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> > > > > > > > > > > > > > Control: 10c5387d  Table: 4000404a  DAC: 00000051
> > > > > > > > > > > > > > Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> > > > > > > > > > > > > > Stack: (0xef0dfd30 to 0xef0e0000)
> > > > > > > > > > > > > > ...
> > > > > > > > > > > > > > [<c0afc920>] (__mutex_lock) from [<c0afd400>]
> > > > > > > > > > > > > > (mutex_lock_nested+0x1c/0x24)
> > > > > > > > > > > > > > [<c0afd400>] (mutex_lock_nested) from [<c064d4b8>]
> > > > > > > > > > > > > > (__exynos_dsi_host_attach+0x20/0x6c)
> > > > > > > > > > > > > > [<c064d4b8>] (__exynos_dsi_host_attach) from [<c064d914>]
> > > > > > > > > > > > > > (exynos_dsi_host_attach+0x70/0x194)
> > > > > > > > > > > > > > [<c064d914>] (exynos_dsi_host_attach) from [<c0656b64>]
> > > > > > > > > > > > > > (s6e8aa0_probe+0x1b0/0x218)
> > > > > > > > > > > > > > [<c0656b64>] (s6e8aa0_probe) from [<c0672530>]
> > > > > > > > > > > > > > (really_probe+0x200/0x4fc)
> > > > > > > > > > > > > > [<c0672530>] (really_probe) from [<c06729f4>]
> > > > > > > > > > > > > > (driver_probe_device+0x78/0x1fc)
> > > > > > > > > > > > > > [<c06729f4>] (driver_probe_device) from [<c0672ddc>]
> > > > > > > > > > > > > > (device_driver_attach+0x58/0x60)
> > > > > > > > > > > > > > [<c0672ddc>] (device_driver_attach) from [<c0672ec0>]
> > > > > > > > > > > > > > (__driver_attach+0xdc/0x174)
> > > > > > > > > > > > > > [<c0672ec0>] (__driver_attach) from [<c06702b8>]
> > > > > > > > > > > > > > (bus_for_each_dev+0x68/0xb4)
> > > > > > > > > > > > > > [<c06702b8>] (bus_for_each_dev) from [<c06715ec>]
> > > > > > > > > > > > > > (bus_add_driver+0x158/0x214)
> > > > > > > > > > > > > > [<c06715ec>] (bus_add_driver) from [<c0673d20>]
> > > > > > > > > > > > > > (driver_register+0x78/0x110)
> > > > > > > > > > > > > > [<c0673d20>] (driver_register) from [<c0102484>]
> > > > > > > > > > > > > > (do_one_initcall+0x8c/0x42c)
> > > > > > > > > > > > > > [<c0102484>] (do_one_initcall) from [<c11011c0>]
> > > > > > > > > > > > > > (kernel_init_freeable+0x190/0x1dc)
> > > > > > > > > > > > > > [<c11011c0>] (kernel_init_freeable) from [<c0af7988>]
> > > > > > > > > > > > > > (kernel_init+0x8/0x118)
> > > > > > > > > > > > > > [<c0af7988>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> > > > > > > > > > > > > > Exception stack(0xef0dffb0 to 0xef0dfff8)
> > > > > > > > > > > > > > ...
> > > > > > > > > > > > > > ---[ end trace c06e996ec2e8234d ]---
> > > > > > > > > > > > > > 
> > > > > > > > > > > > > > This means that dsi->encoder.dev is not initialized in
> > > > > > > > > > > > > > __exynos_dsi_host_attach().
> > > > > > > > > > > > > > 
> > > > > > > > > > > > > > This happens, because drm_bridge_attach() in exynos_dsi_bind() returned
> > > > > > > > > > > > > > earlier -517 (deferred probe), what causes cleanup of encoder and
> > > > > > > > > > > > > > release of all drm resources.
> > > > > > > > > > > > > > 
> > > > > > > > > > > > > > Then however, the panel tries to register itself and
> > > > > > > > > > > > > > exynos_dsi_host_attach() tries to access the released encoder (which is
> > > > > > > > > > > > > > zeroed in drm_encoder_release) and rest of resources, what causes
> > > > > > > > > > > > > > failure.
> > > > > > > > > > > > > > 
> > > > > > > > > > > > > > It looks that something is missing. Maybe mipi host has to be
> > > > > > > > > > > > > > registered
> > > > > > > > > > > > > > later, when bridge is ready? I have no idea how it is handled before
> > > > > > > > > > > > > > this patch. Andrzej, could you comment it a bit?
> > > > > > > > > > > > > I intentionally changed the order, because if another bridge follows
> > > > > > > > > > > > > in the
> > > > > > > > > > > > > pipeline, the probe of the drm driver has to be deferred until some
> > > > > > > > > > > > > bridge
> > > > > > > > > > > > > provides a connector. The next bridge registers itself via the
> > > > > > > > > > > > > host_attach
> > > > > > > > > > > > > function and the deferral is ensured via the bind for the bind/unbind
> > > > > > > > > > > > > API or
> > > > > > > > > > > > > the bridge_attach function otherwise.
> > > > > > > > > > > > > 
> > > > > > > > > > > > > On the other hand, the bridge does not have an encoder until the mipi
> > > > > > > > > > > > > device
> > > > > > > > > > > > > has been attached.
> > > > > > > > > > > > > 
> > > > > > > > > > > > > As a solution, the exynos dsi driver must initialize the encoder in
> > > > > > > > > > > > > exynos_dsi_probe instead of in exynos_dsi_bind and access the encoder
> > > > > > > > > > > > > via
> > > > > > > > > > > > > exynos_dsi instead of the bridge.
> > > > > > > > > > > > > 
> > > > > > > > > > > > > Can you try to move everything except samsung_dsim_bind from
> > > > > > > > > > > > > exynos_dsi_bind
> > > > > > > > > > > > > to exynos_dsi_probe (respectively for unbind) and report if it fixes the
> > > > > > > > > > > > > crash.
> > > > > > > > > > > > 
> > > > > > > > > > > > The original behaviour is that encoder (exynos_dsi) is registered
> > > > > > > > > > > > regardless of sink presence (initially panel, later also bridge) - it
> > > > > > > > > > > > avoids multiple issues with deferred probe, device driver bind/unbind
> > > > > > > > > > > > and module load/unload. Appearance or disappearance of sink is
> > > > > > > > > > > > reported to host nicely via DSI attach/detach callbacks - and it is
> > > > > > > > > > > > reflected in drm world as change state of the connector.
> > > > > > > > > > > > 
> > > > > > > > > > > > Registering DSI host in bind and unregistering in unbind assures that
> > > > > > > > > > > > if mipi_dsi device is attached/detached the drm device is always
> > > > > > > > > > > > present - it makes device/driver binding race free and allows to avoid
> > > > > > > > > > > > additional locking.
> > > > > > > > > > > > 
> > > > > > > > > > > > Moving DSI host registration to probe changes everything, for sure it
> > > > > > > > > > > > breaks the nice feature of DSI attach/detach callbacks and apparently
> > > > > > > > > > > > can cause different issues depending on device bind order.
> > > > > > > > > > > > 
> > > > > > > > > > > > I will try to look at the patches tomorrow and maybe I can find more
> > > > > > > > > > > > constructive comments :)
> > > > > > > > > > > 
> > > > > > > > > > > As I said yesterday, exynos_dsi driver uses dsi host attach/detach
> > > > > > > > > > > callbacks to control appearance/disappearance of downstream device. It
> > > > > > > > > > > allows to:
> > > > > > > > > > > 
> > > > > > > > > > > 1. Safely bind/unbind different device drivers at any time and at any
> > > > > > > > > > > order, without killing exynos_drm and/or crashing system.
> > > > > > > > > > > 
> > > > > > > > > > > 2. Avoid issues with late drm init - on some platforms exynos_drm device
> > > > > > > > > > > appeared too late, due to deferred probe, and resulted in black screen
> > > > > > > > > > > in userspace.
> > > > > > > > > > > 
> > > > > > > > > > > 
> > > > > > > > > > > Now if we want to convert exynos_dsi to drm_bridge I see following options:
> > > > > > > > > > > 
> > > > > > > > > > > A. Forgot about callbacks and make the exynos_drm to defer probing until
> > > > > > > > > > > exynos_dsi bridge is available, probably it will cause later exynos_drm
> > > > > > > > > > > appearance, thus probably black screen on some targets. So for sure it
> > > > > > > > > > > will be suboptimal. Making it bridge unbind safe would be another
> > > > > > > > > > > problem, but most developers do not care about it so why should we? :)
> > > > > > > > > > > 
> > > > > > > > > > > B. Try to mimic current behaviour - exynos_dsi register bridge ASAP,
> > > > > > > > > > > even if downstream devices are not yet attached, on attach/detach notify
> > > > > > > > > > > drm about it via connector status change, for this dsi_host registration
> > > > > > > > > > > should be performed from drm_bridge attach, I guess.
> > > > > > > > > > > 
> > > > > > > > > > > 
> > > > > > > > > > > Option A is more standard, but is unsafe and causes other issues.
> > > > > > > > > > > 
> > > > > > > > > > > Option B keeps current behaviour.
> > > > > > > > > > Maybe we can have both, but I am not sure, if I am missing something:
> > > > > > > > > > 
> > > > > > > > > > I still prefer option A for the samsung-dsim driver, because it is more
> > > > > > > > > > standard, simpler and avoids issues with encoders, connectors or handling
> > > > > > > > > > hotplug.
> > > > > > > > > > 
> > > > > > > > > > The idea is to use two bridges in the exynos-dsi driver: One bridge in the
> > > > > > > > > > samsung-dsim driver which implements option A and defers probing of the drm
> > > > > > > > > > driver until the next bridge is attached. And a second bridge in the
> > > > > > > > > > exynos_dsi that attaches to the first bridge (thus, allowing the exynos_drm
> > > > > > > > > > device to appear) and implements the hotplug handling for notifying drm via
> > > > > > > > > > connector status change.
> > > > > > > > > > 
> > > > > > > > > > The driver for the i.MX8M would use the samsung-dsim bridge without an
> > > > > > > > > > additional bridge.
> > > > > > > > > > 
> > > > > > > > > > This allows the samsung-dsim driver to expose the standard behavior while the
> > > > > > > > > > exynos_dsi may stick to the existing behavior for the exynos_drm driver.
> > > > > > > > > > 
> > > > > > > > > > I hope this makes sense and does not sound too crazy. It might be difficult to
> > > > > > > > > > get the probing and mipi host/device registration correct, but I will try, if
> > > > > > > > > > this can work.
> > > > > > > > > Adding two bridges for being able to support hotplugging adds many special
> > > > > > > > > cases to the bridge driver and still requires more custom API to correctly add
> > > > > > > > > the second bridge. I don't think that this a viable path to go.
> > > > > > > > Just jumping in here: You cannot hotplug/hotremove anything from a
> > > > > > > > drm_device after drm_dev_register has been called, except
> > > > > > > > drm_connector. I didn't dig into details here so not sure whether you
> > > > > > > > want to late-bind your bridge after drm_dev_register is called or not,
> > > > > > > > so might just be fyi and not relevant to the discussion.
> > > > > > > Thanks. AFAIC that is exactly what is currently implemented in the exynos_drm
> > > > > > > driver (i.e. Option B)
> > > > > > > 
> > > > > > > exynos_dsi_bind configures the encoder and registers a DSI host. Afterwards,
> > > > > > > exynos_drm_bind (as component_master_ops) calls drm_dev_register. Later, a DSI
> > > > > > > device might attach to the DSI host and call exynos_dsi_host_attach. In
> > > > > > > exynos_dsi_host_attach, the driver finds the drm_bridge for the DSI device and
> > > > > > > attaches this bridge to the encoder _after_ drm_dev_register has been called.
> > > > > > > This is invalid behavior, right?
> > > > > > Definitely not supported, I don't think we have the right locks in place
> > > > > > to make sure this works.
> > > > > > 
> > > > > > Now if your _only_ adding a drm_bridge (and not an encoder or anything
> > > > > > like that), and you are adding the drm_connector correctly (like a
> > > > > > hotplugged DP MST sink), then that would at least work from a uapi pov.
> > > > > > Because drm_bridge isn't exposed as an uapi object.
> > > > > > 
> > > > > > But yeah, as-is, don't :-)
> > > > > > 
> > > > > > The solution here is a bunch of EPROBE_DEFER handling until all your
> > > > > > bridges are loaded, with or without the assistance of component.c
> > > > > > framework. Only then call drm_dev_register.
> > > > > 
> > > > > I have impression we have similar conversation already.
> > > > > 
> > > > > As you stated drm_bridge and drm_panel are not exposed to userspace so
> > > > > there shouldn't be problem with them from uapi PoV.
> > > > > 
> > > > > On the other side drm_panel or drm_bridge are not used until pipeline
> > > > > enters connected state (at least they were not some time ago :) ). The
> > > > > issue is that bridge exposes drm_connector, but as you stated (again :)
> > > > > ) connectors can be hotplugged, so in theory it should work. Practical
> > > > > tests shows that it also works, but bugs can be still there.
> > > > > 
> > > > > Bunch of EPROBE_DEFER was very slow (as a result userspace timeouted and
> > > > > decided there is no display), and does not handle unbinding/re-binding
> > > > > drivers.
> > > > 
> > > > Rebinding drivers should be fixed now, with a bunch of fixes in driver
> > > > core. If not, we need to fix this more.
> > > > 
> > > > Also, EPROBE_DEFER is how this is supposed to work. If it's too slow,
> > > > we need to fix EPROBE_DEFER (there's ideas for pre-sorting that never
> > > > seem to go anywhere), not paper over it with bad architecture in
> > > > drivers.
> > > 
> > > I've heard this argument multiple times, but it sounds more like an
> > > attempt to ignore the problem and hope it will fall on someone else's
> > > plate :-) Improvement in the probe deferral mechanism are certainly an
> > > option to explore, but as far as I can tell nobody has proven that this
> > > mechanism is or will be able to solve all problems related to probe
> > > ordering dependencies. I wouldn't rule out the need for different
> > > solutions for some of the issues.
> > 
> > Then build another one. But adding hotplug for stuff that is there,
> > and shouldn't be hotplugged, just because it's easier on driver
> > writers and harder on userspace isn't really a good approach.
> > -Daniel
> 
> I think it is quite clear that replacing or reworking the deferral mechanism
> is out of scope for this discussion, which is why I would like to come back
> to the original issue and sum this up as far as I understand it (which is
> not really far when it comes to the details):
> 
> We have the existing exynos driver that avoids the standard deferral
> mechanism in favor of something that works but Daniel describes as
> "definitely not supported".
> 
> We have a proposal from Michael for converting the driver to the standard
> drm_bridge behavior and more work from Michael and Marek based on this to
> implement the platform specific parts for i.MX8MM.
> 
> From the i.MX8MM POV this approach already received some testing and looks
> good as far as I can judge. Upstreaming this solution is blocked because of
> objections from the Samsung maintainers.
> 
> Sorry if I'm being blunt or naive, but where to go from here?
> 

Maybe some more information by the Samsung maintainers would help:

If I understand correctly, the main reason for the non-standard behavior is a
userspace application that runs into a timeout if the drm-device does not
appear in time. Correct? Is there something we can do about that?

The other reason is the convenience of binding and unbinding a bridge driver,
while the drm device is kept available. Correct? Is this used in development,
testing, or production?

Is there anything else that prevents the exynos drm from switching to the
standard behavior?

Would a exynos drm specific wrapper, which uses a standard bridge driver but
exposes the non-standard behavior, be acceptable? (Unfortunately, my first try
on something like that felt really awkward and didn't really work.)

Michael
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 10/16] drm/exynos: implement a drm bridge
  2021-02-18  8:04                                   ` Michael Tretter
@ 2021-02-18 16:02                                     ` Andrzej Hajda
  2021-02-23 12:07                                       ` Daniel Vetter
  0 siblings, 1 reply; 61+ messages in thread
From: Andrzej Hajda @ 2021-02-18 16:02 UTC (permalink / raw)
  To: Michael Tretter, Frieder Schrempf
  Cc: Marek Vasut, aisheng.dong, linux-samsung-soc, ch, Neil Armstrong,
	Shawn Guo, Bartlomiej Zolnierkiewicz, Seung-Woo Kim,
	dl-linux-imx, dri-devel, abel.vesa, kyungmin.park,
	Laurent Pinchart, Sascha Hauer, Krzysztof Kozlowski,
	sylvester.nawrocki, Joonyoung Shim, aford173, Marek Szyprowski

Hi Michael,

W dniu 18.02.2021 o 09:04, Michael Tretter pisze:
> On Wed, 10 Feb 2021 10:10:37 +0100, Frieder Schrempf wrote:
>> On 04.02.21 18:46, Daniel Vetter wrote:
>>> On Thu, Feb 4, 2021 at 6:26 PM Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
>>>> On Thu, Feb 04, 2021 at 06:19:22PM +0100, Daniel Vetter wrote:
>>>>> On Thu, Feb 4, 2021 at 5:28 PM Andrzej Hajda wrote:
>>>>>> W dniu 04.02.2021 o 17:05, Daniel Vetter pisze:
>>>>>>> On Thu, Feb 04, 2021 at 11:56:32AM +0100, Michael Tretter wrote:
>>>>>>>> On Thu, 04 Feb 2021 11:17:49 +0100, Daniel Vetter wrote:
>>>>>>>>> On Wed, Feb 3, 2021 at 9:32 PM Michael Tretter wrote:
>>>>>>>>>> On Mon, 01 Feb 2021 17:33:14 +0100, Michael Tretter wrote:
>>>>>>>>>>> On Tue, 15 Sep 2020 21:40:40 +0200, Andrzej Hajda wrote:
>>>>>>>>>>>> W dniu 14.09.2020 o 23:19, Andrzej Hajda pisze:
>>>>>>>>>>>>> On 14.09.2020 22:01, Michael Tretter wrote:
>>>>>>>>>>>>>> On Mon, 14 Sep 2020 14:31:19 +0200, Marek Szyprowski wrote:
>>>>>>>>>>>>>>> On 14.09.2020 10:29, Marek Szyprowski wrote:
>>>>>>>>>>>>>>>> On 11.09.2020 15:54, Michael Tretter wrote:
>>>>>>>>>>>>>>>>> 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@pengutronix.de>
>>>>>>>>>>>>>>>> This one (and the whole series applied) still fails on Exynos boards:
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping
>>>>>>>>>>>>>>>> operations
>>>>>>>>>>>>>>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
>>>>>>>>>>>>>>>> OF: graph: no port node found in /soc/dsi@11c80000
>>>>>>>>>>>>>>>> 8<--- cut here ---
>>>>>>>>>>>>>>>> Unable to handle kernel NULL pointer dereference at virtual address
>>>>>>>>>>>>>>>> 00000084
>>>>>>>>>>>>>>>> pgd = (ptrval)
>>>>>>>>>>>>>>>> [00000084] *pgd=00000000
>>>>>>>>>>>>>>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
>>>>>>>>>>>>>>>> Modules linked in:
>>>>>>>>>>>>>>>> CPU: 1 PID: 1 Comm: swapper/0 Not tainted
>>>>>>>>>>>>>>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec #1608
>>>>>>>>>>>>>>>> Hardware name: Samsung Exynos (Flattened Device Tree)
>>>>>>>>>>>>>>>> PC is at drm_bridge_attach+0x18/0x164
>>>>>>>>>>>>>>>> LR is at exynos_dsi_bind+0x88/0xa8
>>>>>>>>>>>>>>>> pc : [<c0628c08>]    lr : [<c064d560>]    psr: 20000013
>>>>>>>>>>>>>>>> sp : ef0dfca8  ip : 00000002  fp : c13190e0
>>>>>>>>>>>>>>>> r10: 00000000  r9 : ee46d580  r8 : c13190e0
>>>>>>>>>>>>>>>> r7 : ee438800  r6 : 00000018  r5 : ef253810  r4 : ef39e840
>>>>>>>>>>>>>>>> r3 : 00000000  r2 : 00000018  r1 : ef39e888  r0 : ef39e840
>>>>>>>>>>>>>>>> Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
>>>>>>>>>>>>>>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
>>>>>>>>>>>>>>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
>>>>>>>>>>>>>>>> Stack: (0xef0dfca8 to 0xef0e0000)
>>>>>>>>>>>>>>>> ...
>>>>>>>>>>>>>>>> [<c0628c08>] (drm_bridge_attach) from [<c064d560>]
>>>>>>>>>>>>>>>> (exynos_dsi_bind+0x88/0xa8)
>>>>>>>>>>>>>>>> [<c064d560>] (exynos_dsi_bind) from [<c066a800>]
>>>>>>>>>>>>>>>> (component_bind_all+0xfc/0x290)
>>>>>>>>>>>>>>>> [<c066a800>] (component_bind_all) from [<c0649dc0>]
>>>>>>>>>>>>>>>> (exynos_drm_bind+0xe4/0x19c)
>>>>>>>>>>>>>>>> [<c0649dc0>] (exynos_drm_bind) from [<c066ad74>]
>>>>>>>>>>>>>>>> (try_to_bring_up_master+0x1e4/0x2c4)
>>>>>>>>>>>>>>>> [<c066ad74>] (try_to_bring_up_master) from [<c066b2b4>]
>>>>>>>>>>>>>>>> (component_master_add_with_match+0xd4/0x108)
>>>>>>>>>>>>>>>> [<c066b2b4>] (component_master_add_with_match) from [<c0649ae8>]
>>>>>>>>>>>>>>>> (exynos_drm_platform_probe+0xe4/0x110)
>>>>>>>>>>>>>>>> [<c0649ae8>] (exynos_drm_platform_probe) from [<c0674e6c>]
>>>>>>>>>>>>>>>> (platform_drv_probe+0x6c/0xa4)
>>>>>>>>>>>>>>>> [<c0674e6c>] (platform_drv_probe) from [<c067242c>]
>>>>>>>>>>>>>>>> (really_probe+0x200/0x4fc)
>>>>>>>>>>>>>>>> [<c067242c>] (really_probe) from [<c06728f0>]
>>>>>>>>>>>>>>>> (driver_probe_device+0x78/0x1fc)
>>>>>>>>>>>>>>>> [<c06728f0>] (driver_probe_device) from [<c0672cd8>]
>>>>>>>>>>>>>>>> (device_driver_attach+0x58/0x60)
>>>>>>>>>>>>>>>> [<c0672cd8>] (device_driver_attach) from [<c0672dbc>]
>>>>>>>>>>>>>>>> (__driver_attach+0xdc/0x174)
>>>>>>>>>>>>>>>> [<c0672dbc>] (__driver_attach) from [<c06701b4>]
>>>>>>>>>>>>>>>> (bus_for_each_dev+0x68/0xb4)
>>>>>>>>>>>>>>>> [<c06701b4>] (bus_for_each_dev) from [<c06714e8>]
>>>>>>>>>>>>>>>> (bus_add_driver+0x158/0x214)
>>>>>>>>>>>>>>>> [<c06714e8>] (bus_add_driver) from [<c0673c1c>]
>>>>>>>>>>>>>>>> (driver_register+0x78/0x110)
>>>>>>>>>>>>>>>> [<c0673c1c>] (driver_register) from [<c0649ca8>]
>>>>>>>>>>>>>>>> (exynos_drm_init+0xe4/0x118)
>>>>>>>>>>>>>>>> [<c0649ca8>] (exynos_drm_init) from [<c0102484>]
>>>>>>>>>>>>>>>> (do_one_initcall+0x8c/0x42c)
>>>>>>>>>>>>>>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
>>>>>>>>>>>>>>>> (kernel_init_freeable+0x190/0x1dc)
>>>>>>>>>>>>>>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7880>]
>>>>>>>>>>>>>>>> (kernel_init+0x8/0x118)
>>>>>>>>>>>>>>>> [<c0af7880>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
>>>>>>>>>>>>>>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
>>>>>>>>>>>>>>>> ...
>>>>>>>>>>>>>>>> ---[ end trace ee27f313f9ed9da1 ]---
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> # arm-linux-gnueabi-addr2line -e vmlinux c0628c08
>>>>>>>>>>>>>>>> drivers/gpu/drm/drm_bridge.c:184 (discriminator 1)
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> I will try to debug it a bit more today.
>>>>>>>>>>>>>>> The above crash has been caused by lack of in_bridge initialization to
>>>>>>>>>>>>>>> NULL in exynos_dsi_bind() in this patch. However, fixing it reveals
>>>>>>>>>>>>>>> another issue:
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
>>>>>>>>>>>>>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
>>>>>>>>>>>>>>> OF: graph: no port node found in /soc/dsi@11c80000
>>>>>>>>>>>>>>> 8<--- cut here ---
>>>>>>>>>>>>>>> Unable to handle kernel NULL pointer dereference at virtual address
>>>>>>>>>>>>>>> 00000280
>>>>>>>>>>>>>>> pgd = (ptrval)
>>>>>>>>>>>>>>> [00000280] *pgd=00000000
>>>>>>>>>>>>>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
>>>>>>>>>>>>>>> Modules linked in:
>>>>>>>>>>>>>>> CPU: 0 PID: 1 Comm: swapper/0 Not tainted
>>>>>>>>>>>>>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec-dirty #1613
>>>>>>>>>>>>>>> Hardware name: Samsung Exynos (Flattened Device Tree)
>>>>>>>>>>>>>>> PC is at __mutex_lock+0x54/0xb18
>>>>>>>>>>>>>>> LR is at lock_is_held_type+0x80/0x138
>>>>>>>>>>>>>>> pc : [<c0afc920>]    lr : [<c0af63e8>]    psr: 60000013
>>>>>>>>>>>>>>> sp : ef0dfd30  ip : 33937b74  fp : c13193c8
>>>>>>>>>>>>>>> r10: c1208eec  r9 : 00000000  r8 : ee45f808
>>>>>>>>>>>>>>> r7 : c19561a4  r6 : 00000000  r5 : 00000000  r4 : 0000024c
>>>>>>>>>>>>>>> r3 : 00000000  r2 : 00204140  r1 : c124f13c  r0 : 00000000
>>>>>>>>>>>>>>> Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
>>>>>>>>>>>>>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
>>>>>>>>>>>>>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
>>>>>>>>>>>>>>> Stack: (0xef0dfd30 to 0xef0e0000)
>>>>>>>>>>>>>>> ...
>>>>>>>>>>>>>>> [<c0afc920>] (__mutex_lock) from [<c0afd400>]
>>>>>>>>>>>>>>> (mutex_lock_nested+0x1c/0x24)
>>>>>>>>>>>>>>> [<c0afd400>] (mutex_lock_nested) from [<c064d4b8>]
>>>>>>>>>>>>>>> (__exynos_dsi_host_attach+0x20/0x6c)
>>>>>>>>>>>>>>> [<c064d4b8>] (__exynos_dsi_host_attach) from [<c064d914>]
>>>>>>>>>>>>>>> (exynos_dsi_host_attach+0x70/0x194)
>>>>>>>>>>>>>>> [<c064d914>] (exynos_dsi_host_attach) from [<c0656b64>]
>>>>>>>>>>>>>>> (s6e8aa0_probe+0x1b0/0x218)
>>>>>>>>>>>>>>> [<c0656b64>] (s6e8aa0_probe) from [<c0672530>]
>>>>>>>>>>>>>>> (really_probe+0x200/0x4fc)
>>>>>>>>>>>>>>> [<c0672530>] (really_probe) from [<c06729f4>]
>>>>>>>>>>>>>>> (driver_probe_device+0x78/0x1fc)
>>>>>>>>>>>>>>> [<c06729f4>] (driver_probe_device) from [<c0672ddc>]
>>>>>>>>>>>>>>> (device_driver_attach+0x58/0x60)
>>>>>>>>>>>>>>> [<c0672ddc>] (device_driver_attach) from [<c0672ec0>]
>>>>>>>>>>>>>>> (__driver_attach+0xdc/0x174)
>>>>>>>>>>>>>>> [<c0672ec0>] (__driver_attach) from [<c06702b8>]
>>>>>>>>>>>>>>> (bus_for_each_dev+0x68/0xb4)
>>>>>>>>>>>>>>> [<c06702b8>] (bus_for_each_dev) from [<c06715ec>]
>>>>>>>>>>>>>>> (bus_add_driver+0x158/0x214)
>>>>>>>>>>>>>>> [<c06715ec>] (bus_add_driver) from [<c0673d20>]
>>>>>>>>>>>>>>> (driver_register+0x78/0x110)
>>>>>>>>>>>>>>> [<c0673d20>] (driver_register) from [<c0102484>]
>>>>>>>>>>>>>>> (do_one_initcall+0x8c/0x42c)
>>>>>>>>>>>>>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
>>>>>>>>>>>>>>> (kernel_init_freeable+0x190/0x1dc)
>>>>>>>>>>>>>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7988>]
>>>>>>>>>>>>>>> (kernel_init+0x8/0x118)
>>>>>>>>>>>>>>> [<c0af7988>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
>>>>>>>>>>>>>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
>>>>>>>>>>>>>>> ...
>>>>>>>>>>>>>>> ---[ end trace c06e996ec2e8234d ]---
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> This means that dsi->encoder.dev is not initialized in
>>>>>>>>>>>>>>> __exynos_dsi_host_attach().
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> This happens, because drm_bridge_attach() in exynos_dsi_bind() returned
>>>>>>>>>>>>>>> earlier -517 (deferred probe), what causes cleanup of encoder and
>>>>>>>>>>>>>>> release of all drm resources.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Then however, the panel tries to register itself and
>>>>>>>>>>>>>>> exynos_dsi_host_attach() tries to access the released encoder (which is
>>>>>>>>>>>>>>> zeroed in drm_encoder_release) and rest of resources, what causes
>>>>>>>>>>>>>>> failure.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> It looks that something is missing. Maybe mipi host has to be
>>>>>>>>>>>>>>> registered
>>>>>>>>>>>>>>> later, when bridge is ready? I have no idea how it is handled before
>>>>>>>>>>>>>>> this patch. Andrzej, could you comment it a bit?
>>>>>>>>>>>>>> I intentionally changed the order, because if another bridge follows
>>>>>>>>>>>>>> in the
>>>>>>>>>>>>>> pipeline, the probe of the drm driver has to be deferred until some
>>>>>>>>>>>>>> bridge
>>>>>>>>>>>>>> provides a connector. The next bridge registers itself via the
>>>>>>>>>>>>>> host_attach
>>>>>>>>>>>>>> function and the deferral is ensured via the bind for the bind/unbind
>>>>>>>>>>>>>> API or
>>>>>>>>>>>>>> the bridge_attach function otherwise.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> On the other hand, the bridge does not have an encoder until the mipi
>>>>>>>>>>>>>> device
>>>>>>>>>>>>>> has been attached.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> As a solution, the exynos dsi driver must initialize the encoder in
>>>>>>>>>>>>>> exynos_dsi_probe instead of in exynos_dsi_bind and access the encoder
>>>>>>>>>>>>>> via
>>>>>>>>>>>>>> exynos_dsi instead of the bridge.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Can you try to move everything except samsung_dsim_bind from
>>>>>>>>>>>>>> exynos_dsi_bind
>>>>>>>>>>>>>> to exynos_dsi_probe (respectively for unbind) and report if it fixes the
>>>>>>>>>>>>>> crash.
>>>>>>>>>>>>> The original behaviour is that encoder (exynos_dsi) is registered
>>>>>>>>>>>>> regardless of sink presence (initially panel, later also bridge) - it
>>>>>>>>>>>>> avoids multiple issues with deferred probe, device driver bind/unbind
>>>>>>>>>>>>> and module load/unload. Appearance or disappearance of sink is
>>>>>>>>>>>>> reported to host nicely via DSI attach/detach callbacks - and it is
>>>>>>>>>>>>> reflected in drm world as change state of the connector.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Registering DSI host in bind and unregistering in unbind assures that
>>>>>>>>>>>>> if mipi_dsi device is attached/detached the drm device is always
>>>>>>>>>>>>> present - it makes device/driver binding race free and allows to avoid
>>>>>>>>>>>>> additional locking.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Moving DSI host registration to probe changes everything, for sure it
>>>>>>>>>>>>> breaks the nice feature of DSI attach/detach callbacks and apparently
>>>>>>>>>>>>> can cause different issues depending on device bind order.
>>>>>>>>>>>>>
>>>>>>>>>>>>> I will try to look at the patches tomorrow and maybe I can find more
>>>>>>>>>>>>> constructive comments :)
>>>>>>>>>>>> As I said yesterday, exynos_dsi driver uses dsi host attach/detach
>>>>>>>>>>>> callbacks to control appearance/disappearance of downstream device. It
>>>>>>>>>>>> allows to:
>>>>>>>>>>>>
>>>>>>>>>>>> 1. Safely bind/unbind different device drivers at any time and at any
>>>>>>>>>>>> order, without killing exynos_drm and/or crashing system.
>>>>>>>>>>>>
>>>>>>>>>>>> 2. Avoid issues with late drm init - on some platforms exynos_drm device
>>>>>>>>>>>> appeared too late, due to deferred probe, and resulted in black screen
>>>>>>>>>>>> in userspace.
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> Now if we want to convert exynos_dsi to drm_bridge I see following options:
>>>>>>>>>>>>
>>>>>>>>>>>> A. Forgot about callbacks and make the exynos_drm to defer probing until
>>>>>>>>>>>> exynos_dsi bridge is available, probably it will cause later exynos_drm
>>>>>>>>>>>> appearance, thus probably black screen on some targets. So for sure it
>>>>>>>>>>>> will be suboptimal. Making it bridge unbind safe would be another
>>>>>>>>>>>> problem, but most developers do not care about it so why should we? :)
>>>>>>>>>>>>
>>>>>>>>>>>> B. Try to mimic current behaviour - exynos_dsi register bridge ASAP,
>>>>>>>>>>>> even if downstream devices are not yet attached, on attach/detach notify
>>>>>>>>>>>> drm about it via connector status change, for this dsi_host registration
>>>>>>>>>>>> should be performed from drm_bridge attach, I guess.
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> Option A is more standard, but is unsafe and causes other issues.
>>>>>>>>>>>>
>>>>>>>>>>>> Option B keeps current behaviour.
>>>>>>>>>>> Maybe we can have both, but I am not sure, if I am missing something:
>>>>>>>>>>>
>>>>>>>>>>> I still prefer option A for the samsung-dsim driver, because it is more
>>>>>>>>>>> standard, simpler and avoids issues with encoders, connectors or handling
>>>>>>>>>>> hotplug.
>>>>>>>>>>>
>>>>>>>>>>> The idea is to use two bridges in the exynos-dsi driver: One bridge in the
>>>>>>>>>>> samsung-dsim driver which implements option A and defers probing of the drm
>>>>>>>>>>> driver until the next bridge is attached. And a second bridge in the
>>>>>>>>>>> exynos_dsi that attaches to the first bridge (thus, allowing the exynos_drm
>>>>>>>>>>> device to appear) and implements the hotplug handling for notifying drm via
>>>>>>>>>>> connector status change.
>>>>>>>>>>>
>>>>>>>>>>> The driver for the i.MX8M would use the samsung-dsim bridge without an
>>>>>>>>>>> additional bridge.
>>>>>>>>>>>
>>>>>>>>>>> This allows the samsung-dsim driver to expose the standard behavior while the
>>>>>>>>>>> exynos_dsi may stick to the existing behavior for the exynos_drm driver.
>>>>>>>>>>>
>>>>>>>>>>> I hope this makes sense and does not sound too crazy. It might be difficult to
>>>>>>>>>>> get the probing and mipi host/device registration correct, but I will try, if
>>>>>>>>>>> this can work.
>>>>>>>>>> Adding two bridges for being able to support hotplugging adds many special
>>>>>>>>>> cases to the bridge driver and still requires more custom API to correctly add
>>>>>>>>>> the second bridge. I don't think that this a viable path to go.
>>>>>>>>> Just jumping in here: You cannot hotplug/hotremove anything from a
>>>>>>>>> drm_device after drm_dev_register has been called, except
>>>>>>>>> drm_connector. I didn't dig into details here so not sure whether you
>>>>>>>>> want to late-bind your bridge after drm_dev_register is called or not,
>>>>>>>>> so might just be fyi and not relevant to the discussion.
>>>>>>>> Thanks. AFAIC that is exactly what is currently implemented in the exynos_drm
>>>>>>>> driver (i.e. Option B)
>>>>>>>>
>>>>>>>> exynos_dsi_bind configures the encoder and registers a DSI host. Afterwards,
>>>>>>>> exynos_drm_bind (as component_master_ops) calls drm_dev_register. Later, a DSI
>>>>>>>> device might attach to the DSI host and call exynos_dsi_host_attach. In
>>>>>>>> exynos_dsi_host_attach, the driver finds the drm_bridge for the DSI device and
>>>>>>>> attaches this bridge to the encoder _after_ drm_dev_register has been called.
>>>>>>>> This is invalid behavior, right?
>>>>>>> Definitely not supported, I don't think we have the right locks in place
>>>>>>> to make sure this works.
>>>>>>>
>>>>>>> Now if your _only_ adding a drm_bridge (and not an encoder or anything
>>>>>>> like that), and you are adding the drm_connector correctly (like a
>>>>>>> hotplugged DP MST sink), then that would at least work from a uapi pov.
>>>>>>> Because drm_bridge isn't exposed as an uapi object.
>>>>>>>
>>>>>>> But yeah, as-is, don't :-)
>>>>>>>
>>>>>>> The solution here is a bunch of EPROBE_DEFER handling until all your
>>>>>>> bridges are loaded, with or without the assistance of component.c
>>>>>>> framework. Only then call drm_dev_register.
>>>>>> I have impression we have similar conversation already.
>>>>>>
>>>>>> As you stated drm_bridge and drm_panel are not exposed to userspace so
>>>>>> there shouldn't be problem with them from uapi PoV.
>>>>>>
>>>>>> On the other side drm_panel or drm_bridge are not used until pipeline
>>>>>> enters connected state (at least they were not some time ago :) ). The
>>>>>> issue is that bridge exposes drm_connector, but as you stated (again :)
>>>>>> ) connectors can be hotplugged, so in theory it should work. Practical
>>>>>> tests shows that it also works, but bugs can be still there.
>>>>>>
>>>>>> Bunch of EPROBE_DEFER was very slow (as a result userspace timeouted and
>>>>>> decided there is no display), and does not handle unbinding/re-binding
>>>>>> drivers.
>>>>> Rebinding drivers should be fixed now, with a bunch of fixes in driver
>>>>> core. If not, we need to fix this more.
>>>>>
>>>>> Also, EPROBE_DEFER is how this is supposed to work. If it's too slow,
>>>>> we need to fix EPROBE_DEFER (there's ideas for pre-sorting that never
>>>>> seem to go anywhere), not paper over it with bad architecture in
>>>>> drivers.
>>>> I've heard this argument multiple times, but it sounds more like an
>>>> attempt to ignore the problem and hope it will fall on someone else's
>>>> plate :-) Improvement in the probe deferral mechanism are certainly an
>>>> option to explore, but as far as I can tell nobody has proven that this
>>>> mechanism is or will be able to solve all problems related to probe
>>>> ordering dependencies. I wouldn't rule out the need for different
>>>> solutions for some of the issues.
>>> Then build another one. But adding hotplug for stuff that is there,
>>> and shouldn't be hotplugged, just because it's easier on driver
>>> writers and harder on userspace isn't really a good approach.
>>> -Daniel
>> I think it is quite clear that replacing or reworking the deferral mechanism
>> is out of scope for this discussion, which is why I would like to come back
>> to the original issue and sum this up as far as I understand it (which is
>> not really far when it comes to the details):
>>
>> We have the existing exynos driver that avoids the standard deferral
>> mechanism in favor of something that works but Daniel describes as
>> "definitely not supported".
>>
>> We have a proposal from Michael for converting the driver to the standard
>> drm_bridge behavior and more work from Michael and Marek based on this to
>> implement the platform specific parts for i.MX8MM.
>>
>>  From the i.MX8MM POV this approach already received some testing and looks
>> good as far as I can judge. Upstreaming this solution is blocked because of
>> objections from the Samsung maintainers.
>>
>> Sorry if I'm being blunt or naive, but where to go from here?
>>
> Maybe some more information by the Samsung maintainers would help:
>
> If I understand correctly, the main reason for the non-standard behavior is a
> userspace application that runs into a timeout if the drm-device does not
> appear in time. Correct? Is there something we can do about that?
>
> The other reason is the convenience of binding and unbinding a bridge driver,
> while the drm device is kept available. Correct? Is this used in development,
> testing, or production?
>
> Is there anything else that prevents the exynos drm from switching to the
> standard behavior?
>
> Would a exynos drm specific wrapper, which uses a standard bridge driver but
> exposes the non-standard behavior, be acceptable? (Unfortunately, my first try
> on something like that felt really awkward and didn't really work.)

Even if we drop this 'non-standard' behaviour, your task will be still 
quite difficult to fulfil - you are trying to completely rewrite core 
component of Exynos display pipeline without hardware to test.

ExynosDSI is used in almost all Exynos platforms supported mainline (ls 
-1 arch/arm*/boot/dts/exynos*.dts | wc shows 35). It has different hw 
versions (4 compatibles) and is used in different configurations (video 
mode, command mode, with hw/sw trigger, connected to panels/bridges) and 
for sure with big heritage, since it was one of the 1st DSI drivers.

Rewriting such driver is challenging, even with access to hw.

So maybe it would be better to move common parts in your and exynos 
driver to 'shared library' and use it in both drivers - this way you 
have bigger chances to avoid traps.


Regards

Andrzej


>
> Michael
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://protect2.fireeye.com/v1/url?k=08365e03-57ad66fe-0837d54c-000babff317b-bdb6592fde86cf24&q=1&e=db1ed315-991b-408c-827a-4b6cbf2b4e3e&u=https%3A%2F%2Flists.freedesktop.org%2Fmailman%2Flistinfo%2Fdri-devel
>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 10/16] drm/exynos: implement a drm bridge
  2021-02-18 16:02                                     ` Andrzej Hajda
@ 2021-02-23 12:07                                       ` Daniel Vetter
  2021-04-20 11:42                                         ` Frieder Schrempf
  0 siblings, 1 reply; 61+ messages in thread
From: Daniel Vetter @ 2021-02-23 12:07 UTC (permalink / raw)
  To: Andrzej Hajda, airlied
  Cc: Marek Vasut, aisheng.dong, linux-samsung-soc, Laurent Pinchart,
	ch, Neil Armstrong, aford173, dri-devel,
	Bartlomiej Zolnierkiewicz, Seung-Woo Kim, Michael Tretter,
	Frieder Schrempf, Joonyoung Shim, Kyungmin Park, dl-linux-imx,
	Sascha Hauer, Krzysztof Kozlowski, sylvester.nawrocki,
	Marek Szyprowski, Shawn Guo, abel.vesa

On Thu, Feb 18, 2021 at 5:02 PM Andrzej Hajda <a.hajda@samsung.com> wrote:
>
> Hi Michael,
>
> W dniu 18.02.2021 o 09:04, Michael Tretter pisze:
> > On Wed, 10 Feb 2021 10:10:37 +0100, Frieder Schrempf wrote:
> >> On 04.02.21 18:46, Daniel Vetter wrote:
> >>> On Thu, Feb 4, 2021 at 6:26 PM Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
> >>>> On Thu, Feb 04, 2021 at 06:19:22PM +0100, Daniel Vetter wrote:
> >>>>> On Thu, Feb 4, 2021 at 5:28 PM Andrzej Hajda wrote:
> >>>>>> W dniu 04.02.2021 o 17:05, Daniel Vetter pisze:
> >>>>>>> On Thu, Feb 04, 2021 at 11:56:32AM +0100, Michael Tretter wrote:
> >>>>>>>> On Thu, 04 Feb 2021 11:17:49 +0100, Daniel Vetter wrote:
> >>>>>>>>> On Wed, Feb 3, 2021 at 9:32 PM Michael Tretter wrote:
> >>>>>>>>>> On Mon, 01 Feb 2021 17:33:14 +0100, Michael Tretter wrote:
> >>>>>>>>>>> On Tue, 15 Sep 2020 21:40:40 +0200, Andrzej Hajda wrote:
> >>>>>>>>>>>> W dniu 14.09.2020 o 23:19, Andrzej Hajda pisze:
> >>>>>>>>>>>>> On 14.09.2020 22:01, Michael Tretter wrote:
> >>>>>>>>>>>>>> On Mon, 14 Sep 2020 14:31:19 +0200, Marek Szyprowski wrote:
> >>>>>>>>>>>>>>> On 14.09.2020 10:29, Marek Szyprowski wrote:
> >>>>>>>>>>>>>>>> On 11.09.2020 15:54, Michael Tretter wrote:
> >>>>>>>>>>>>>>>>> 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@pengutronix.de>
> >>>>>>>>>>>>>>>> This one (and the whole series applied) still fails on Exynos boards:
> >>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping
> >>>>>>>>>>>>>>>> operations
> >>>>>>>>>>>>>>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> >>>>>>>>>>>>>>>> OF: graph: no port node found in /soc/dsi@11c80000
> >>>>>>>>>>>>>>>> 8<--- cut here ---
> >>>>>>>>>>>>>>>> Unable to handle kernel NULL pointer dereference at virtual address
> >>>>>>>>>>>>>>>> 00000084
> >>>>>>>>>>>>>>>> pgd = (ptrval)
> >>>>>>>>>>>>>>>> [00000084] *pgd=00000000
> >>>>>>>>>>>>>>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> >>>>>>>>>>>>>>>> Modules linked in:
> >>>>>>>>>>>>>>>> CPU: 1 PID: 1 Comm: swapper/0 Not tainted
> >>>>>>>>>>>>>>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec #1608
> >>>>>>>>>>>>>>>> Hardware name: Samsung Exynos (Flattened Device Tree)
> >>>>>>>>>>>>>>>> PC is at drm_bridge_attach+0x18/0x164
> >>>>>>>>>>>>>>>> LR is at exynos_dsi_bind+0x88/0xa8
> >>>>>>>>>>>>>>>> pc : [<c0628c08>]    lr : [<c064d560>]    psr: 20000013
> >>>>>>>>>>>>>>>> sp : ef0dfca8  ip : 00000002  fp : c13190e0
> >>>>>>>>>>>>>>>> r10: 00000000  r9 : ee46d580  r8 : c13190e0
> >>>>>>>>>>>>>>>> r7 : ee438800  r6 : 00000018  r5 : ef253810  r4 : ef39e840
> >>>>>>>>>>>>>>>> r3 : 00000000  r2 : 00000018  r1 : ef39e888  r0 : ef39e840
> >>>>>>>>>>>>>>>> Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> >>>>>>>>>>>>>>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> >>>>>>>>>>>>>>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> >>>>>>>>>>>>>>>> Stack: (0xef0dfca8 to 0xef0e0000)
> >>>>>>>>>>>>>>>> ...
> >>>>>>>>>>>>>>>> [<c0628c08>] (drm_bridge_attach) from [<c064d560>]
> >>>>>>>>>>>>>>>> (exynos_dsi_bind+0x88/0xa8)
> >>>>>>>>>>>>>>>> [<c064d560>] (exynos_dsi_bind) from [<c066a800>]
> >>>>>>>>>>>>>>>> (component_bind_all+0xfc/0x290)
> >>>>>>>>>>>>>>>> [<c066a800>] (component_bind_all) from [<c0649dc0>]
> >>>>>>>>>>>>>>>> (exynos_drm_bind+0xe4/0x19c)
> >>>>>>>>>>>>>>>> [<c0649dc0>] (exynos_drm_bind) from [<c066ad74>]
> >>>>>>>>>>>>>>>> (try_to_bring_up_master+0x1e4/0x2c4)
> >>>>>>>>>>>>>>>> [<c066ad74>] (try_to_bring_up_master) from [<c066b2b4>]
> >>>>>>>>>>>>>>>> (component_master_add_with_match+0xd4/0x108)
> >>>>>>>>>>>>>>>> [<c066b2b4>] (component_master_add_with_match) from [<c0649ae8>]
> >>>>>>>>>>>>>>>> (exynos_drm_platform_probe+0xe4/0x110)
> >>>>>>>>>>>>>>>> [<c0649ae8>] (exynos_drm_platform_probe) from [<c0674e6c>]
> >>>>>>>>>>>>>>>> (platform_drv_probe+0x6c/0xa4)
> >>>>>>>>>>>>>>>> [<c0674e6c>] (platform_drv_probe) from [<c067242c>]
> >>>>>>>>>>>>>>>> (really_probe+0x200/0x4fc)
> >>>>>>>>>>>>>>>> [<c067242c>] (really_probe) from [<c06728f0>]
> >>>>>>>>>>>>>>>> (driver_probe_device+0x78/0x1fc)
> >>>>>>>>>>>>>>>> [<c06728f0>] (driver_probe_device) from [<c0672cd8>]
> >>>>>>>>>>>>>>>> (device_driver_attach+0x58/0x60)
> >>>>>>>>>>>>>>>> [<c0672cd8>] (device_driver_attach) from [<c0672dbc>]
> >>>>>>>>>>>>>>>> (__driver_attach+0xdc/0x174)
> >>>>>>>>>>>>>>>> [<c0672dbc>] (__driver_attach) from [<c06701b4>]
> >>>>>>>>>>>>>>>> (bus_for_each_dev+0x68/0xb4)
> >>>>>>>>>>>>>>>> [<c06701b4>] (bus_for_each_dev) from [<c06714e8>]
> >>>>>>>>>>>>>>>> (bus_add_driver+0x158/0x214)
> >>>>>>>>>>>>>>>> [<c06714e8>] (bus_add_driver) from [<c0673c1c>]
> >>>>>>>>>>>>>>>> (driver_register+0x78/0x110)
> >>>>>>>>>>>>>>>> [<c0673c1c>] (driver_register) from [<c0649ca8>]
> >>>>>>>>>>>>>>>> (exynos_drm_init+0xe4/0x118)
> >>>>>>>>>>>>>>>> [<c0649ca8>] (exynos_drm_init) from [<c0102484>]
> >>>>>>>>>>>>>>>> (do_one_initcall+0x8c/0x42c)
> >>>>>>>>>>>>>>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
> >>>>>>>>>>>>>>>> (kernel_init_freeable+0x190/0x1dc)
> >>>>>>>>>>>>>>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7880>]
> >>>>>>>>>>>>>>>> (kernel_init+0x8/0x118)
> >>>>>>>>>>>>>>>> [<c0af7880>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> >>>>>>>>>>>>>>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
> >>>>>>>>>>>>>>>> ...
> >>>>>>>>>>>>>>>> ---[ end trace ee27f313f9ed9da1 ]---
> >>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>> # arm-linux-gnueabi-addr2line -e vmlinux c0628c08
> >>>>>>>>>>>>>>>> drivers/gpu/drm/drm_bridge.c:184 (discriminator 1)
> >>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>> I will try to debug it a bit more today.
> >>>>>>>>>>>>>>> The above crash has been caused by lack of in_bridge initialization to
> >>>>>>>>>>>>>>> NULL in exynos_dsi_bind() in this patch. However, fixing it reveals
> >>>>>>>>>>>>>>> another issue:
> >>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
> >>>>>>>>>>>>>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> >>>>>>>>>>>>>>> OF: graph: no port node found in /soc/dsi@11c80000
> >>>>>>>>>>>>>>> 8<--- cut here ---
> >>>>>>>>>>>>>>> Unable to handle kernel NULL pointer dereference at virtual address
> >>>>>>>>>>>>>>> 00000280
> >>>>>>>>>>>>>>> pgd = (ptrval)
> >>>>>>>>>>>>>>> [00000280] *pgd=00000000
> >>>>>>>>>>>>>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> >>>>>>>>>>>>>>> Modules linked in:
> >>>>>>>>>>>>>>> CPU: 0 PID: 1 Comm: swapper/0 Not tainted
> >>>>>>>>>>>>>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec-dirty #1613
> >>>>>>>>>>>>>>> Hardware name: Samsung Exynos (Flattened Device Tree)
> >>>>>>>>>>>>>>> PC is at __mutex_lock+0x54/0xb18
> >>>>>>>>>>>>>>> LR is at lock_is_held_type+0x80/0x138
> >>>>>>>>>>>>>>> pc : [<c0afc920>]    lr : [<c0af63e8>]    psr: 60000013
> >>>>>>>>>>>>>>> sp : ef0dfd30  ip : 33937b74  fp : c13193c8
> >>>>>>>>>>>>>>> r10: c1208eec  r9 : 00000000  r8 : ee45f808
> >>>>>>>>>>>>>>> r7 : c19561a4  r6 : 00000000  r5 : 00000000  r4 : 0000024c
> >>>>>>>>>>>>>>> r3 : 00000000  r2 : 00204140  r1 : c124f13c  r0 : 00000000
> >>>>>>>>>>>>>>> Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> >>>>>>>>>>>>>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> >>>>>>>>>>>>>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> >>>>>>>>>>>>>>> Stack: (0xef0dfd30 to 0xef0e0000)
> >>>>>>>>>>>>>>> ...
> >>>>>>>>>>>>>>> [<c0afc920>] (__mutex_lock) from [<c0afd400>]
> >>>>>>>>>>>>>>> (mutex_lock_nested+0x1c/0x24)
> >>>>>>>>>>>>>>> [<c0afd400>] (mutex_lock_nested) from [<c064d4b8>]
> >>>>>>>>>>>>>>> (__exynos_dsi_host_attach+0x20/0x6c)
> >>>>>>>>>>>>>>> [<c064d4b8>] (__exynos_dsi_host_attach) from [<c064d914>]
> >>>>>>>>>>>>>>> (exynos_dsi_host_attach+0x70/0x194)
> >>>>>>>>>>>>>>> [<c064d914>] (exynos_dsi_host_attach) from [<c0656b64>]
> >>>>>>>>>>>>>>> (s6e8aa0_probe+0x1b0/0x218)
> >>>>>>>>>>>>>>> [<c0656b64>] (s6e8aa0_probe) from [<c0672530>]
> >>>>>>>>>>>>>>> (really_probe+0x200/0x4fc)
> >>>>>>>>>>>>>>> [<c0672530>] (really_probe) from [<c06729f4>]
> >>>>>>>>>>>>>>> (driver_probe_device+0x78/0x1fc)
> >>>>>>>>>>>>>>> [<c06729f4>] (driver_probe_device) from [<c0672ddc>]
> >>>>>>>>>>>>>>> (device_driver_attach+0x58/0x60)
> >>>>>>>>>>>>>>> [<c0672ddc>] (device_driver_attach) from [<c0672ec0>]
> >>>>>>>>>>>>>>> (__driver_attach+0xdc/0x174)
> >>>>>>>>>>>>>>> [<c0672ec0>] (__driver_attach) from [<c06702b8>]
> >>>>>>>>>>>>>>> (bus_for_each_dev+0x68/0xb4)
> >>>>>>>>>>>>>>> [<c06702b8>] (bus_for_each_dev) from [<c06715ec>]
> >>>>>>>>>>>>>>> (bus_add_driver+0x158/0x214)
> >>>>>>>>>>>>>>> [<c06715ec>] (bus_add_driver) from [<c0673d20>]
> >>>>>>>>>>>>>>> (driver_register+0x78/0x110)
> >>>>>>>>>>>>>>> [<c0673d20>] (driver_register) from [<c0102484>]
> >>>>>>>>>>>>>>> (do_one_initcall+0x8c/0x42c)
> >>>>>>>>>>>>>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
> >>>>>>>>>>>>>>> (kernel_init_freeable+0x190/0x1dc)
> >>>>>>>>>>>>>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7988>]
> >>>>>>>>>>>>>>> (kernel_init+0x8/0x118)
> >>>>>>>>>>>>>>> [<c0af7988>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> >>>>>>>>>>>>>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
> >>>>>>>>>>>>>>> ...
> >>>>>>>>>>>>>>> ---[ end trace c06e996ec2e8234d ]---
> >>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>> This means that dsi->encoder.dev is not initialized in
> >>>>>>>>>>>>>>> __exynos_dsi_host_attach().
> >>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>> This happens, because drm_bridge_attach() in exynos_dsi_bind() returned
> >>>>>>>>>>>>>>> earlier -517 (deferred probe), what causes cleanup of encoder and
> >>>>>>>>>>>>>>> release of all drm resources.
> >>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>> Then however, the panel tries to register itself and
> >>>>>>>>>>>>>>> exynos_dsi_host_attach() tries to access the released encoder (which is
> >>>>>>>>>>>>>>> zeroed in drm_encoder_release) and rest of resources, what causes
> >>>>>>>>>>>>>>> failure.
> >>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>> It looks that something is missing. Maybe mipi host has to be
> >>>>>>>>>>>>>>> registered
> >>>>>>>>>>>>>>> later, when bridge is ready? I have no idea how it is handled before
> >>>>>>>>>>>>>>> this patch. Andrzej, could you comment it a bit?
> >>>>>>>>>>>>>> I intentionally changed the order, because if another bridge follows
> >>>>>>>>>>>>>> in the
> >>>>>>>>>>>>>> pipeline, the probe of the drm driver has to be deferred until some
> >>>>>>>>>>>>>> bridge
> >>>>>>>>>>>>>> provides a connector. The next bridge registers itself via the
> >>>>>>>>>>>>>> host_attach
> >>>>>>>>>>>>>> function and the deferral is ensured via the bind for the bind/unbind
> >>>>>>>>>>>>>> API or
> >>>>>>>>>>>>>> the bridge_attach function otherwise.
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> On the other hand, the bridge does not have an encoder until the mipi
> >>>>>>>>>>>>>> device
> >>>>>>>>>>>>>> has been attached.
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> As a solution, the exynos dsi driver must initialize the encoder in
> >>>>>>>>>>>>>> exynos_dsi_probe instead of in exynos_dsi_bind and access the encoder
> >>>>>>>>>>>>>> via
> >>>>>>>>>>>>>> exynos_dsi instead of the bridge.
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> Can you try to move everything except samsung_dsim_bind from
> >>>>>>>>>>>>>> exynos_dsi_bind
> >>>>>>>>>>>>>> to exynos_dsi_probe (respectively for unbind) and report if it fixes the
> >>>>>>>>>>>>>> crash.
> >>>>>>>>>>>>> The original behaviour is that encoder (exynos_dsi) is registered
> >>>>>>>>>>>>> regardless of sink presence (initially panel, later also bridge) - it
> >>>>>>>>>>>>> avoids multiple issues with deferred probe, device driver bind/unbind
> >>>>>>>>>>>>> and module load/unload. Appearance or disappearance of sink is
> >>>>>>>>>>>>> reported to host nicely via DSI attach/detach callbacks - and it is
> >>>>>>>>>>>>> reflected in drm world as change state of the connector.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> Registering DSI host in bind and unregistering in unbind assures that
> >>>>>>>>>>>>> if mipi_dsi device is attached/detached the drm device is always
> >>>>>>>>>>>>> present - it makes device/driver binding race free and allows to avoid
> >>>>>>>>>>>>> additional locking.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> Moving DSI host registration to probe changes everything, for sure it
> >>>>>>>>>>>>> breaks the nice feature of DSI attach/detach callbacks and apparently
> >>>>>>>>>>>>> can cause different issues depending on device bind order.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> I will try to look at the patches tomorrow and maybe I can find more
> >>>>>>>>>>>>> constructive comments :)
> >>>>>>>>>>>> As I said yesterday, exynos_dsi driver uses dsi host attach/detach
> >>>>>>>>>>>> callbacks to control appearance/disappearance of downstream device. It
> >>>>>>>>>>>> allows to:
> >>>>>>>>>>>>
> >>>>>>>>>>>> 1. Safely bind/unbind different device drivers at any time and at any
> >>>>>>>>>>>> order, without killing exynos_drm and/or crashing system.
> >>>>>>>>>>>>
> >>>>>>>>>>>> 2. Avoid issues with late drm init - on some platforms exynos_drm device
> >>>>>>>>>>>> appeared too late, due to deferred probe, and resulted in black screen
> >>>>>>>>>>>> in userspace.
> >>>>>>>>>>>>
> >>>>>>>>>>>>
> >>>>>>>>>>>> Now if we want to convert exynos_dsi to drm_bridge I see following options:
> >>>>>>>>>>>>
> >>>>>>>>>>>> A. Forgot about callbacks and make the exynos_drm to defer probing until
> >>>>>>>>>>>> exynos_dsi bridge is available, probably it will cause later exynos_drm
> >>>>>>>>>>>> appearance, thus probably black screen on some targets. So for sure it
> >>>>>>>>>>>> will be suboptimal. Making it bridge unbind safe would be another
> >>>>>>>>>>>> problem, but most developers do not care about it so why should we? :)
> >>>>>>>>>>>>
> >>>>>>>>>>>> B. Try to mimic current behaviour - exynos_dsi register bridge ASAP,
> >>>>>>>>>>>> even if downstream devices are not yet attached, on attach/detach notify
> >>>>>>>>>>>> drm about it via connector status change, for this dsi_host registration
> >>>>>>>>>>>> should be performed from drm_bridge attach, I guess.
> >>>>>>>>>>>>
> >>>>>>>>>>>>
> >>>>>>>>>>>> Option A is more standard, but is unsafe and causes other issues.
> >>>>>>>>>>>>
> >>>>>>>>>>>> Option B keeps current behaviour.
> >>>>>>>>>>> Maybe we can have both, but I am not sure, if I am missing something:
> >>>>>>>>>>>
> >>>>>>>>>>> I still prefer option A for the samsung-dsim driver, because it is more
> >>>>>>>>>>> standard, simpler and avoids issues with encoders, connectors or handling
> >>>>>>>>>>> hotplug.
> >>>>>>>>>>>
> >>>>>>>>>>> The idea is to use two bridges in the exynos-dsi driver: One bridge in the
> >>>>>>>>>>> samsung-dsim driver which implements option A and defers probing of the drm
> >>>>>>>>>>> driver until the next bridge is attached. And a second bridge in the
> >>>>>>>>>>> exynos_dsi that attaches to the first bridge (thus, allowing the exynos_drm
> >>>>>>>>>>> device to appear) and implements the hotplug handling for notifying drm via
> >>>>>>>>>>> connector status change.
> >>>>>>>>>>>
> >>>>>>>>>>> The driver for the i.MX8M would use the samsung-dsim bridge without an
> >>>>>>>>>>> additional bridge.
> >>>>>>>>>>>
> >>>>>>>>>>> This allows the samsung-dsim driver to expose the standard behavior while the
> >>>>>>>>>>> exynos_dsi may stick to the existing behavior for the exynos_drm driver.
> >>>>>>>>>>>
> >>>>>>>>>>> I hope this makes sense and does not sound too crazy. It might be difficult to
> >>>>>>>>>>> get the probing and mipi host/device registration correct, but I will try, if
> >>>>>>>>>>> this can work.
> >>>>>>>>>> Adding two bridges for being able to support hotplugging adds many special
> >>>>>>>>>> cases to the bridge driver and still requires more custom API to correctly add
> >>>>>>>>>> the second bridge. I don't think that this a viable path to go.
> >>>>>>>>> Just jumping in here: You cannot hotplug/hotremove anything from a
> >>>>>>>>> drm_device after drm_dev_register has been called, except
> >>>>>>>>> drm_connector. I didn't dig into details here so not sure whether you
> >>>>>>>>> want to late-bind your bridge after drm_dev_register is called or not,
> >>>>>>>>> so might just be fyi and not relevant to the discussion.
> >>>>>>>> Thanks. AFAIC that is exactly what is currently implemented in the exynos_drm
> >>>>>>>> driver (i.e. Option B)
> >>>>>>>>
> >>>>>>>> exynos_dsi_bind configures the encoder and registers a DSI host. Afterwards,
> >>>>>>>> exynos_drm_bind (as component_master_ops) calls drm_dev_register. Later, a DSI
> >>>>>>>> device might attach to the DSI host and call exynos_dsi_host_attach. In
> >>>>>>>> exynos_dsi_host_attach, the driver finds the drm_bridge for the DSI device and
> >>>>>>>> attaches this bridge to the encoder _after_ drm_dev_register has been called.
> >>>>>>>> This is invalid behavior, right?
> >>>>>>> Definitely not supported, I don't think we have the right locks in place
> >>>>>>> to make sure this works.
> >>>>>>>
> >>>>>>> Now if your _only_ adding a drm_bridge (and not an encoder or anything
> >>>>>>> like that), and you are adding the drm_connector correctly (like a
> >>>>>>> hotplugged DP MST sink), then that would at least work from a uapi pov.
> >>>>>>> Because drm_bridge isn't exposed as an uapi object.
> >>>>>>>
> >>>>>>> But yeah, as-is, don't :-)
> >>>>>>>
> >>>>>>> The solution here is a bunch of EPROBE_DEFER handling until all your
> >>>>>>> bridges are loaded, with or without the assistance of component.c
> >>>>>>> framework. Only then call drm_dev_register.
> >>>>>> I have impression we have similar conversation already.
> >>>>>>
> >>>>>> As you stated drm_bridge and drm_panel are not exposed to userspace so
> >>>>>> there shouldn't be problem with them from uapi PoV.
> >>>>>>
> >>>>>> On the other side drm_panel or drm_bridge are not used until pipeline
> >>>>>> enters connected state (at least they were not some time ago :) ). The
> >>>>>> issue is that bridge exposes drm_connector, but as you stated (again :)
> >>>>>> ) connectors can be hotplugged, so in theory it should work. Practical
> >>>>>> tests shows that it also works, but bugs can be still there.
> >>>>>>
> >>>>>> Bunch of EPROBE_DEFER was very slow (as a result userspace timeouted and
> >>>>>> decided there is no display), and does not handle unbinding/re-binding
> >>>>>> drivers.
> >>>>> Rebinding drivers should be fixed now, with a bunch of fixes in driver
> >>>>> core. If not, we need to fix this more.
> >>>>>
> >>>>> Also, EPROBE_DEFER is how this is supposed to work. If it's too slow,
> >>>>> we need to fix EPROBE_DEFER (there's ideas for pre-sorting that never
> >>>>> seem to go anywhere), not paper over it with bad architecture in
> >>>>> drivers.
> >>>> I've heard this argument multiple times, but it sounds more like an
> >>>> attempt to ignore the problem and hope it will fall on someone else's
> >>>> plate :-) Improvement in the probe deferral mechanism are certainly an
> >>>> option to explore, but as far as I can tell nobody has proven that this
> >>>> mechanism is or will be able to solve all problems related to probe
> >>>> ordering dependencies. I wouldn't rule out the need for different
> >>>> solutions for some of the issues.
> >>> Then build another one. But adding hotplug for stuff that is there,
> >>> and shouldn't be hotplugged, just because it's easier on driver
> >>> writers and harder on userspace isn't really a good approach.
> >>> -Daniel
> >> I think it is quite clear that replacing or reworking the deferral mechanism
> >> is out of scope for this discussion, which is why I would like to come back
> >> to the original issue and sum this up as far as I understand it (which is
> >> not really far when it comes to the details):
> >>
> >> We have the existing exynos driver that avoids the standard deferral
> >> mechanism in favor of something that works but Daniel describes as
> >> "definitely not supported".
> >>
> >> We have a proposal from Michael for converting the driver to the standard
> >> drm_bridge behavior and more work from Michael and Marek based on this to
> >> implement the platform specific parts for i.MX8MM.
> >>
> >>  From the i.MX8MM POV this approach already received some testing and looks
> >> good as far as I can judge. Upstreaming this solution is blocked because of
> >> objections from the Samsung maintainers.
> >>
> >> Sorry if I'm being blunt or naive, but where to go from here?
> >>
> > Maybe some more information by the Samsung maintainers would help:
> >
> > If I understand correctly, the main reason for the non-standard behavior is a
> > userspace application that runs into a timeout if the drm-device does not
> > appear in time. Correct? Is there something we can do about that?
> >
> > The other reason is the convenience of binding and unbinding a bridge driver,
> > while the drm device is kept available. Correct? Is this used in development,
> > testing, or production?
> >
> > Is there anything else that prevents the exynos drm from switching to the
> > standard behavior?
> >
> > Would a exynos drm specific wrapper, which uses a standard bridge driver but
> > exposes the non-standard behavior, be acceptable? (Unfortunately, my first try
> > on something like that felt really awkward and didn't really work.)
>
> Even if we drop this 'non-standard' behaviour, your task will be still
> quite difficult to fulfil - you are trying to completely rewrite core
> component of Exynos display pipeline without hardware to test.
>
> ExynosDSI is used in almost all Exynos platforms supported mainline (ls
> -1 arch/arm*/boot/dts/exynos*.dts | wc shows 35). It has different hw
> versions (4 compatibles) and is used in different configurations (video
> mode, command mode, with hw/sw trigger, connected to panels/bridges) and
> for sure with big heritage, since it was one of the 1st DSI drivers.
>
> Rewriting such driver is challenging, even with access to hw.
>
> So maybe it would be better to move common parts in your and exynos
> driver to 'shared library' and use it in both drivers - this way you
> have bigger chances to avoid traps.

If exynos really can't be fixed up in a reasonable way, then I think
sharing code doesn't make much sense - you drag the new driver down
with the old one that's just hanging in there the wrong way round. For
that case just copypaste the exynos code into a new clean drm_bridge
driver, and done.

That would also mean that new exynos support in drm/exynos would need
to be stalled until this is sorted out (at least for new platforms),
since continuing the old way really doesn't sound so great. Wouldn't
be the first time we just end up with a driver fork because the old
one has too much heritage and is too hard to change.

Note that this can also be done within one driver codebase, e.g.
nouveau has still legacy modeset code for nv04-nv4x, and atomic from
nv50+ going forward.

Should be possible to find a pragmatic solution here going forward,
despite tons of hw and heritage. If we use existing hard to retest hw
support to stop new driver submissions from doing the right thing,
that's a clear failure, we need a better approach here.
-Daniel

>
>
> Regards
>
> Andrzej
>
>
> >
> > Michael
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > https://protect2.fireeye.com/v1/url?k=08365e03-57ad66fe-0837d54c-000babff317b-bdb6592fde86cf24&q=1&e=db1ed315-991b-408c-827a-4b6cbf2b4e3e&u=https%3A%2F%2Flists.freedesktop.org%2Fmailman%2Flistinfo%2Fdri-devel
> >
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel



-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 10/16] drm/exynos: implement a drm bridge
  2021-02-23 12:07                                       ` Daniel Vetter
@ 2021-04-20 11:42                                         ` Frieder Schrempf
  2021-04-20 14:27                                           ` Laurent Pinchart
  0 siblings, 1 reply; 61+ messages in thread
From: Frieder Schrempf @ 2021-04-20 11:42 UTC (permalink / raw)
  To: Daniel Vetter, Andrzej Hajda, airlied
  Cc: Marek Vasut, aisheng.dong, linux-samsung-soc, Laurent Pinchart,
	ch, Neil Armstrong, aford173, Bartlomiej Zolnierkiewicz,
	Seung-Woo Kim, Michael Tretter, dri-devel, Joonyoung Shim,
	Kyungmin Park, dl-linux-imx, Sascha Hauer, Krzysztof Kozlowski,
	sylvester.nawrocki, Marek Szyprowski, Shawn Guo, abel.vesa

On 23.02.21 13:07, Daniel Vetter wrote:
> On Thu, Feb 18, 2021 at 5:02 PM Andrzej Hajda <a.hajda@samsung.com> wrote:
>>
>> Hi Michael,
>>
>> W dniu 18.02.2021 o 09:04, Michael Tretter pisze:
>>> On Wed, 10 Feb 2021 10:10:37 +0100, Frieder Schrempf wrote:
>>>> On 04.02.21 18:46, Daniel Vetter wrote:
>>>>> On Thu, Feb 4, 2021 at 6:26 PM Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
>>>>>> On Thu, Feb 04, 2021 at 06:19:22PM +0100, Daniel Vetter wrote:
>>>>>>> On Thu, Feb 4, 2021 at 5:28 PM Andrzej Hajda wrote:
>>>>>>>> W dniu 04.02.2021 o 17:05, Daniel Vetter pisze:
>>>>>>>>> On Thu, Feb 04, 2021 at 11:56:32AM +0100, Michael Tretter wrote:
>>>>>>>>>> On Thu, 04 Feb 2021 11:17:49 +0100, Daniel Vetter wrote:
>>>>>>>>>>> On Wed, Feb 3, 2021 at 9:32 PM Michael Tretter wrote:
>>>>>>>>>>>> On Mon, 01 Feb 2021 17:33:14 +0100, Michael Tretter wrote:
>>>>>>>>>>>>> On Tue, 15 Sep 2020 21:40:40 +0200, Andrzej Hajda wrote:
>>>>>>>>>>>>>> W dniu 14.09.2020 o 23:19, Andrzej Hajda pisze:
>>>>>>>>>>>>>>> On 14.09.2020 22:01, Michael Tretter wrote:
>>>>>>>>>>>>>>>> On Mon, 14 Sep 2020 14:31:19 +0200, Marek Szyprowski wrote:
>>>>>>>>>>>>>>>>> On 14.09.2020 10:29, Marek Szyprowski wrote:
>>>>>>>>>>>>>>>>>> On 11.09.2020 15:54, Michael Tretter wrote:
>>>>>>>>>>>>>>>>>>> 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@pengutronix.de>
>>>>>>>>>>>>>>>>>> This one (and the whole series applied) still fails on Exynos boards:
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping
>>>>>>>>>>>>>>>>>> operations
>>>>>>>>>>>>>>>>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
>>>>>>>>>>>>>>>>>> OF: graph: no port node found in /soc/dsi@11c80000
>>>>>>>>>>>>>>>>>> 8<--- cut here ---
>>>>>>>>>>>>>>>>>> Unable to handle kernel NULL pointer dereference at virtual address
>>>>>>>>>>>>>>>>>> 00000084
>>>>>>>>>>>>>>>>>> pgd = (ptrval)
>>>>>>>>>>>>>>>>>> [00000084] *pgd=00000000
>>>>>>>>>>>>>>>>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
>>>>>>>>>>>>>>>>>> Modules linked in:
>>>>>>>>>>>>>>>>>> CPU: 1 PID: 1 Comm: swapper/0 Not tainted
>>>>>>>>>>>>>>>>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec #1608
>>>>>>>>>>>>>>>>>> Hardware name: Samsung Exynos (Flattened Device Tree)
>>>>>>>>>>>>>>>>>> PC is at drm_bridge_attach+0x18/0x164
>>>>>>>>>>>>>>>>>> LR is at exynos_dsi_bind+0x88/0xa8
>>>>>>>>>>>>>>>>>> pc : [<c0628c08>]    lr : [<c064d560>]    psr: 20000013
>>>>>>>>>>>>>>>>>> sp : ef0dfca8  ip : 00000002  fp : c13190e0
>>>>>>>>>>>>>>>>>> r10: 00000000  r9 : ee46d580  r8 : c13190e0
>>>>>>>>>>>>>>>>>> r7 : ee438800  r6 : 00000018  r5 : ef253810  r4 : ef39e840
>>>>>>>>>>>>>>>>>> r3 : 00000000  r2 : 00000018  r1 : ef39e888  r0 : ef39e840
>>>>>>>>>>>>>>>>>> Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
>>>>>>>>>>>>>>>>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
>>>>>>>>>>>>>>>>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
>>>>>>>>>>>>>>>>>> Stack: (0xef0dfca8 to 0xef0e0000)
>>>>>>>>>>>>>>>>>> ...
>>>>>>>>>>>>>>>>>> [<c0628c08>] (drm_bridge_attach) from [<c064d560>]
>>>>>>>>>>>>>>>>>> (exynos_dsi_bind+0x88/0xa8)
>>>>>>>>>>>>>>>>>> [<c064d560>] (exynos_dsi_bind) from [<c066a800>]
>>>>>>>>>>>>>>>>>> (component_bind_all+0xfc/0x290)
>>>>>>>>>>>>>>>>>> [<c066a800>] (component_bind_all) from [<c0649dc0>]
>>>>>>>>>>>>>>>>>> (exynos_drm_bind+0xe4/0x19c)
>>>>>>>>>>>>>>>>>> [<c0649dc0>] (exynos_drm_bind) from [<c066ad74>]
>>>>>>>>>>>>>>>>>> (try_to_bring_up_master+0x1e4/0x2c4)
>>>>>>>>>>>>>>>>>> [<c066ad74>] (try_to_bring_up_master) from [<c066b2b4>]
>>>>>>>>>>>>>>>>>> (component_master_add_with_match+0xd4/0x108)
>>>>>>>>>>>>>>>>>> [<c066b2b4>] (component_master_add_with_match) from [<c0649ae8>]
>>>>>>>>>>>>>>>>>> (exynos_drm_platform_probe+0xe4/0x110)
>>>>>>>>>>>>>>>>>> [<c0649ae8>] (exynos_drm_platform_probe) from [<c0674e6c>]
>>>>>>>>>>>>>>>>>> (platform_drv_probe+0x6c/0xa4)
>>>>>>>>>>>>>>>>>> [<c0674e6c>] (platform_drv_probe) from [<c067242c>]
>>>>>>>>>>>>>>>>>> (really_probe+0x200/0x4fc)
>>>>>>>>>>>>>>>>>> [<c067242c>] (really_probe) from [<c06728f0>]
>>>>>>>>>>>>>>>>>> (driver_probe_device+0x78/0x1fc)
>>>>>>>>>>>>>>>>>> [<c06728f0>] (driver_probe_device) from [<c0672cd8>]
>>>>>>>>>>>>>>>>>> (device_driver_attach+0x58/0x60)
>>>>>>>>>>>>>>>>>> [<c0672cd8>] (device_driver_attach) from [<c0672dbc>]
>>>>>>>>>>>>>>>>>> (__driver_attach+0xdc/0x174)
>>>>>>>>>>>>>>>>>> [<c0672dbc>] (__driver_attach) from [<c06701b4>]
>>>>>>>>>>>>>>>>>> (bus_for_each_dev+0x68/0xb4)
>>>>>>>>>>>>>>>>>> [<c06701b4>] (bus_for_each_dev) from [<c06714e8>]
>>>>>>>>>>>>>>>>>> (bus_add_driver+0x158/0x214)
>>>>>>>>>>>>>>>>>> [<c06714e8>] (bus_add_driver) from [<c0673c1c>]
>>>>>>>>>>>>>>>>>> (driver_register+0x78/0x110)
>>>>>>>>>>>>>>>>>> [<c0673c1c>] (driver_register) from [<c0649ca8>]
>>>>>>>>>>>>>>>>>> (exynos_drm_init+0xe4/0x118)
>>>>>>>>>>>>>>>>>> [<c0649ca8>] (exynos_drm_init) from [<c0102484>]
>>>>>>>>>>>>>>>>>> (do_one_initcall+0x8c/0x42c)
>>>>>>>>>>>>>>>>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
>>>>>>>>>>>>>>>>>> (kernel_init_freeable+0x190/0x1dc)
>>>>>>>>>>>>>>>>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7880>]
>>>>>>>>>>>>>>>>>> (kernel_init+0x8/0x118)
>>>>>>>>>>>>>>>>>> [<c0af7880>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
>>>>>>>>>>>>>>>>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
>>>>>>>>>>>>>>>>>> ...
>>>>>>>>>>>>>>>>>> ---[ end trace ee27f313f9ed9da1 ]---
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> # arm-linux-gnueabi-addr2line -e vmlinux c0628c08
>>>>>>>>>>>>>>>>>> drivers/gpu/drm/drm_bridge.c:184 (discriminator 1)
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> I will try to debug it a bit more today.
>>>>>>>>>>>>>>>>> The above crash has been caused by lack of in_bridge initialization to
>>>>>>>>>>>>>>>>> NULL in exynos_dsi_bind() in this patch. However, fixing it reveals
>>>>>>>>>>>>>>>>> another issue:
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
>>>>>>>>>>>>>>>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
>>>>>>>>>>>>>>>>> OF: graph: no port node found in /soc/dsi@11c80000
>>>>>>>>>>>>>>>>> 8<--- cut here ---
>>>>>>>>>>>>>>>>> Unable to handle kernel NULL pointer dereference at virtual address
>>>>>>>>>>>>>>>>> 00000280
>>>>>>>>>>>>>>>>> pgd = (ptrval)
>>>>>>>>>>>>>>>>> [00000280] *pgd=00000000
>>>>>>>>>>>>>>>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
>>>>>>>>>>>>>>>>> Modules linked in:
>>>>>>>>>>>>>>>>> CPU: 0 PID: 1 Comm: swapper/0 Not tainted
>>>>>>>>>>>>>>>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec-dirty #1613
>>>>>>>>>>>>>>>>> Hardware name: Samsung Exynos (Flattened Device Tree)
>>>>>>>>>>>>>>>>> PC is at __mutex_lock+0x54/0xb18
>>>>>>>>>>>>>>>>> LR is at lock_is_held_type+0x80/0x138
>>>>>>>>>>>>>>>>> pc : [<c0afc920>]    lr : [<c0af63e8>]    psr: 60000013
>>>>>>>>>>>>>>>>> sp : ef0dfd30  ip : 33937b74  fp : c13193c8
>>>>>>>>>>>>>>>>> r10: c1208eec  r9 : 00000000  r8 : ee45f808
>>>>>>>>>>>>>>>>> r7 : c19561a4  r6 : 00000000  r5 : 00000000  r4 : 0000024c
>>>>>>>>>>>>>>>>> r3 : 00000000  r2 : 00204140  r1 : c124f13c  r0 : 00000000
>>>>>>>>>>>>>>>>> Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
>>>>>>>>>>>>>>>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
>>>>>>>>>>>>>>>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
>>>>>>>>>>>>>>>>> Stack: (0xef0dfd30 to 0xef0e0000)
>>>>>>>>>>>>>>>>> ...
>>>>>>>>>>>>>>>>> [<c0afc920>] (__mutex_lock) from [<c0afd400>]
>>>>>>>>>>>>>>>>> (mutex_lock_nested+0x1c/0x24)
>>>>>>>>>>>>>>>>> [<c0afd400>] (mutex_lock_nested) from [<c064d4b8>]
>>>>>>>>>>>>>>>>> (__exynos_dsi_host_attach+0x20/0x6c)
>>>>>>>>>>>>>>>>> [<c064d4b8>] (__exynos_dsi_host_attach) from [<c064d914>]
>>>>>>>>>>>>>>>>> (exynos_dsi_host_attach+0x70/0x194)
>>>>>>>>>>>>>>>>> [<c064d914>] (exynos_dsi_host_attach) from [<c0656b64>]
>>>>>>>>>>>>>>>>> (s6e8aa0_probe+0x1b0/0x218)
>>>>>>>>>>>>>>>>> [<c0656b64>] (s6e8aa0_probe) from [<c0672530>]
>>>>>>>>>>>>>>>>> (really_probe+0x200/0x4fc)
>>>>>>>>>>>>>>>>> [<c0672530>] (really_probe) from [<c06729f4>]
>>>>>>>>>>>>>>>>> (driver_probe_device+0x78/0x1fc)
>>>>>>>>>>>>>>>>> [<c06729f4>] (driver_probe_device) from [<c0672ddc>]
>>>>>>>>>>>>>>>>> (device_driver_attach+0x58/0x60)
>>>>>>>>>>>>>>>>> [<c0672ddc>] (device_driver_attach) from [<c0672ec0>]
>>>>>>>>>>>>>>>>> (__driver_attach+0xdc/0x174)
>>>>>>>>>>>>>>>>> [<c0672ec0>] (__driver_attach) from [<c06702b8>]
>>>>>>>>>>>>>>>>> (bus_for_each_dev+0x68/0xb4)
>>>>>>>>>>>>>>>>> [<c06702b8>] (bus_for_each_dev) from [<c06715ec>]
>>>>>>>>>>>>>>>>> (bus_add_driver+0x158/0x214)
>>>>>>>>>>>>>>>>> [<c06715ec>] (bus_add_driver) from [<c0673d20>]
>>>>>>>>>>>>>>>>> (driver_register+0x78/0x110)
>>>>>>>>>>>>>>>>> [<c0673d20>] (driver_register) from [<c0102484>]
>>>>>>>>>>>>>>>>> (do_one_initcall+0x8c/0x42c)
>>>>>>>>>>>>>>>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
>>>>>>>>>>>>>>>>> (kernel_init_freeable+0x190/0x1dc)
>>>>>>>>>>>>>>>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7988>]
>>>>>>>>>>>>>>>>> (kernel_init+0x8/0x118)
>>>>>>>>>>>>>>>>> [<c0af7988>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
>>>>>>>>>>>>>>>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
>>>>>>>>>>>>>>>>> ...
>>>>>>>>>>>>>>>>> ---[ end trace c06e996ec2e8234d ]---
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> This means that dsi->encoder.dev is not initialized in
>>>>>>>>>>>>>>>>> __exynos_dsi_host_attach().
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> This happens, because drm_bridge_attach() in exynos_dsi_bind() returned
>>>>>>>>>>>>>>>>> earlier -517 (deferred probe), what causes cleanup of encoder and
>>>>>>>>>>>>>>>>> release of all drm resources.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Then however, the panel tries to register itself and
>>>>>>>>>>>>>>>>> exynos_dsi_host_attach() tries to access the released encoder (which is
>>>>>>>>>>>>>>>>> zeroed in drm_encoder_release) and rest of resources, what causes
>>>>>>>>>>>>>>>>> failure.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> It looks that something is missing. Maybe mipi host has to be
>>>>>>>>>>>>>>>>> registered
>>>>>>>>>>>>>>>>> later, when bridge is ready? I have no idea how it is handled before
>>>>>>>>>>>>>>>>> this patch. Andrzej, could you comment it a bit?
>>>>>>>>>>>>>>>> I intentionally changed the order, because if another bridge follows
>>>>>>>>>>>>>>>> in the
>>>>>>>>>>>>>>>> pipeline, the probe of the drm driver has to be deferred until some
>>>>>>>>>>>>>>>> bridge
>>>>>>>>>>>>>>>> provides a connector. The next bridge registers itself via the
>>>>>>>>>>>>>>>> host_attach
>>>>>>>>>>>>>>>> function and the deferral is ensured via the bind for the bind/unbind
>>>>>>>>>>>>>>>> API or
>>>>>>>>>>>>>>>> the bridge_attach function otherwise.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> On the other hand, the bridge does not have an encoder until the mipi
>>>>>>>>>>>>>>>> device
>>>>>>>>>>>>>>>> has been attached.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> As a solution, the exynos dsi driver must initialize the encoder in
>>>>>>>>>>>>>>>> exynos_dsi_probe instead of in exynos_dsi_bind and access the encoder
>>>>>>>>>>>>>>>> via
>>>>>>>>>>>>>>>> exynos_dsi instead of the bridge.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Can you try to move everything except samsung_dsim_bind from
>>>>>>>>>>>>>>>> exynos_dsi_bind
>>>>>>>>>>>>>>>> to exynos_dsi_probe (respectively for unbind) and report if it fixes the
>>>>>>>>>>>>>>>> crash.
>>>>>>>>>>>>>>> The original behaviour is that encoder (exynos_dsi) is registered
>>>>>>>>>>>>>>> regardless of sink presence (initially panel, later also bridge) - it
>>>>>>>>>>>>>>> avoids multiple issues with deferred probe, device driver bind/unbind
>>>>>>>>>>>>>>> and module load/unload. Appearance or disappearance of sink is
>>>>>>>>>>>>>>> reported to host nicely via DSI attach/detach callbacks - and it is
>>>>>>>>>>>>>>> reflected in drm world as change state of the connector.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Registering DSI host in bind and unregistering in unbind assures that
>>>>>>>>>>>>>>> if mipi_dsi device is attached/detached the drm device is always
>>>>>>>>>>>>>>> present - it makes device/driver binding race free and allows to avoid
>>>>>>>>>>>>>>> additional locking.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Moving DSI host registration to probe changes everything, for sure it
>>>>>>>>>>>>>>> breaks the nice feature of DSI attach/detach callbacks and apparently
>>>>>>>>>>>>>>> can cause different issues depending on device bind order.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> I will try to look at the patches tomorrow and maybe I can find more
>>>>>>>>>>>>>>> constructive comments :)
>>>>>>>>>>>>>> As I said yesterday, exynos_dsi driver uses dsi host attach/detach
>>>>>>>>>>>>>> callbacks to control appearance/disappearance of downstream device. It
>>>>>>>>>>>>>> allows to:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> 1. Safely bind/unbind different device drivers at any time and at any
>>>>>>>>>>>>>> order, without killing exynos_drm and/or crashing system.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> 2. Avoid issues with late drm init - on some platforms exynos_drm device
>>>>>>>>>>>>>> appeared too late, due to deferred probe, and resulted in black screen
>>>>>>>>>>>>>> in userspace.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Now if we want to convert exynos_dsi to drm_bridge I see following options:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> A. Forgot about callbacks and make the exynos_drm to defer probing until
>>>>>>>>>>>>>> exynos_dsi bridge is available, probably it will cause later exynos_drm
>>>>>>>>>>>>>> appearance, thus probably black screen on some targets. So for sure it
>>>>>>>>>>>>>> will be suboptimal. Making it bridge unbind safe would be another
>>>>>>>>>>>>>> problem, but most developers do not care about it so why should we? :)
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> B. Try to mimic current behaviour - exynos_dsi register bridge ASAP,
>>>>>>>>>>>>>> even if downstream devices are not yet attached, on attach/detach notify
>>>>>>>>>>>>>> drm about it via connector status change, for this dsi_host registration
>>>>>>>>>>>>>> should be performed from drm_bridge attach, I guess.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Option A is more standard, but is unsafe and causes other issues.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Option B keeps current behaviour.
>>>>>>>>>>>>> Maybe we can have both, but I am not sure, if I am missing something:
>>>>>>>>>>>>>
>>>>>>>>>>>>> I still prefer option A for the samsung-dsim driver, because it is more
>>>>>>>>>>>>> standard, simpler and avoids issues with encoders, connectors or handling
>>>>>>>>>>>>> hotplug.
>>>>>>>>>>>>>
>>>>>>>>>>>>> The idea is to use two bridges in the exynos-dsi driver: One bridge in the
>>>>>>>>>>>>> samsung-dsim driver which implements option A and defers probing of the drm
>>>>>>>>>>>>> driver until the next bridge is attached. And a second bridge in the
>>>>>>>>>>>>> exynos_dsi that attaches to the first bridge (thus, allowing the exynos_drm
>>>>>>>>>>>>> device to appear) and implements the hotplug handling for notifying drm via
>>>>>>>>>>>>> connector status change.
>>>>>>>>>>>>>
>>>>>>>>>>>>> The driver for the i.MX8M would use the samsung-dsim bridge without an
>>>>>>>>>>>>> additional bridge.
>>>>>>>>>>>>>
>>>>>>>>>>>>> This allows the samsung-dsim driver to expose the standard behavior while the
>>>>>>>>>>>>> exynos_dsi may stick to the existing behavior for the exynos_drm driver.
>>>>>>>>>>>>>
>>>>>>>>>>>>> I hope this makes sense and does not sound too crazy. It might be difficult to
>>>>>>>>>>>>> get the probing and mipi host/device registration correct, but I will try, if
>>>>>>>>>>>>> this can work.
>>>>>>>>>>>> Adding two bridges for being able to support hotplugging adds many special
>>>>>>>>>>>> cases to the bridge driver and still requires more custom API to correctly add
>>>>>>>>>>>> the second bridge. I don't think that this a viable path to go.
>>>>>>>>>>> Just jumping in here: You cannot hotplug/hotremove anything from a
>>>>>>>>>>> drm_device after drm_dev_register has been called, except
>>>>>>>>>>> drm_connector. I didn't dig into details here so not sure whether you
>>>>>>>>>>> want to late-bind your bridge after drm_dev_register is called or not,
>>>>>>>>>>> so might just be fyi and not relevant to the discussion.
>>>>>>>>>> Thanks. AFAIC that is exactly what is currently implemented in the exynos_drm
>>>>>>>>>> driver (i.e. Option B)
>>>>>>>>>>
>>>>>>>>>> exynos_dsi_bind configures the encoder and registers a DSI host. Afterwards,
>>>>>>>>>> exynos_drm_bind (as component_master_ops) calls drm_dev_register. Later, a DSI
>>>>>>>>>> device might attach to the DSI host and call exynos_dsi_host_attach. In
>>>>>>>>>> exynos_dsi_host_attach, the driver finds the drm_bridge for the DSI device and
>>>>>>>>>> attaches this bridge to the encoder _after_ drm_dev_register has been called.
>>>>>>>>>> This is invalid behavior, right?
>>>>>>>>> Definitely not supported, I don't think we have the right locks in place
>>>>>>>>> to make sure this works.
>>>>>>>>>
>>>>>>>>> Now if your _only_ adding a drm_bridge (and not an encoder or anything
>>>>>>>>> like that), and you are adding the drm_connector correctly (like a
>>>>>>>>> hotplugged DP MST sink), then that would at least work from a uapi pov.
>>>>>>>>> Because drm_bridge isn't exposed as an uapi object.
>>>>>>>>>
>>>>>>>>> But yeah, as-is, don't :-)
>>>>>>>>>
>>>>>>>>> The solution here is a bunch of EPROBE_DEFER handling until all your
>>>>>>>>> bridges are loaded, with or without the assistance of component.c
>>>>>>>>> framework. Only then call drm_dev_register.
>>>>>>>> I have impression we have similar conversation already.
>>>>>>>>
>>>>>>>> As you stated drm_bridge and drm_panel are not exposed to userspace so
>>>>>>>> there shouldn't be problem with them from uapi PoV.
>>>>>>>>
>>>>>>>> On the other side drm_panel or drm_bridge are not used until pipeline
>>>>>>>> enters connected state (at least they were not some time ago :) ). The
>>>>>>>> issue is that bridge exposes drm_connector, but as you stated (again :)
>>>>>>>> ) connectors can be hotplugged, so in theory it should work. Practical
>>>>>>>> tests shows that it also works, but bugs can be still there.
>>>>>>>>
>>>>>>>> Bunch of EPROBE_DEFER was very slow (as a result userspace timeouted and
>>>>>>>> decided there is no display), and does not handle unbinding/re-binding
>>>>>>>> drivers.
>>>>>>> Rebinding drivers should be fixed now, with a bunch of fixes in driver
>>>>>>> core. If not, we need to fix this more.
>>>>>>>
>>>>>>> Also, EPROBE_DEFER is how this is supposed to work. If it's too slow,
>>>>>>> we need to fix EPROBE_DEFER (there's ideas for pre-sorting that never
>>>>>>> seem to go anywhere), not paper over it with bad architecture in
>>>>>>> drivers.
>>>>>> I've heard this argument multiple times, but it sounds more like an
>>>>>> attempt to ignore the problem and hope it will fall on someone else's
>>>>>> plate :-) Improvement in the probe deferral mechanism are certainly an
>>>>>> option to explore, but as far as I can tell nobody has proven that this
>>>>>> mechanism is or will be able to solve all problems related to probe
>>>>>> ordering dependencies. I wouldn't rule out the need for different
>>>>>> solutions for some of the issues.
>>>>> Then build another one. But adding hotplug for stuff that is there,
>>>>> and shouldn't be hotplugged, just because it's easier on driver
>>>>> writers and harder on userspace isn't really a good approach.
>>>>> -Daniel
>>>> I think it is quite clear that replacing or reworking the deferral mechanism
>>>> is out of scope for this discussion, which is why I would like to come back
>>>> to the original issue and sum this up as far as I understand it (which is
>>>> not really far when it comes to the details):
>>>>
>>>> We have the existing exynos driver that avoids the standard deferral
>>>> mechanism in favor of something that works but Daniel describes as
>>>> "definitely not supported".
>>>>
>>>> We have a proposal from Michael for converting the driver to the standard
>>>> drm_bridge behavior and more work from Michael and Marek based on this to
>>>> implement the platform specific parts for i.MX8MM.
>>>>
>>>>   From the i.MX8MM POV this approach already received some testing and looks
>>>> good as far as I can judge. Upstreaming this solution is blocked because of
>>>> objections from the Samsung maintainers.
>>>>
>>>> Sorry if I'm being blunt or naive, but where to go from here?
>>>>
>>> Maybe some more information by the Samsung maintainers would help:
>>>
>>> If I understand correctly, the main reason for the non-standard behavior is a
>>> userspace application that runs into a timeout if the drm-device does not
>>> appear in time. Correct? Is there something we can do about that?
>>>
>>> The other reason is the convenience of binding and unbinding a bridge driver,
>>> while the drm device is kept available. Correct? Is this used in development,
>>> testing, or production?
>>>
>>> Is there anything else that prevents the exynos drm from switching to the
>>> standard behavior?
>>>
>>> Would a exynos drm specific wrapper, which uses a standard bridge driver but
>>> exposes the non-standard behavior, be acceptable? (Unfortunately, my first try
>>> on something like that felt really awkward and didn't really work.)
>>
>> Even if we drop this 'non-standard' behaviour, your task will be still
>> quite difficult to fulfil - you are trying to completely rewrite core
>> component of Exynos display pipeline without hardware to test.
>>
>> ExynosDSI is used in almost all Exynos platforms supported mainline (ls
>> -1 arch/arm*/boot/dts/exynos*.dts | wc shows 35). It has different hw
>> versions (4 compatibles) and is used in different configurations (video
>> mode, command mode, with hw/sw trigger, connected to panels/bridges) and
>> for sure with big heritage, since it was one of the 1st DSI drivers.
>>
>> Rewriting such driver is challenging, even with access to hw.
>>
>> So maybe it would be better to move common parts in your and exynos
>> driver to 'shared library' and use it in both drivers - this way you
>> have bigger chances to avoid traps.
> 
> If exynos really can't be fixed up in a reasonable way, then I think
> sharing code doesn't make much sense - you drag the new driver down
> with the old one that's just hanging in there the wrong way round. For
> that case just copypaste the exynos code into a new clean drm_bridge
> driver, and done.
> 
> That would also mean that new exynos support in drm/exynos would need
> to be stalled until this is sorted out (at least for new platforms),
> since continuing the old way really doesn't sound so great. Wouldn't
> be the first time we just end up with a driver fork because the old
> one has too much heritage and is too hard to change.
> 
> Note that this can also be done within one driver codebase, e.g.
> nouveau has still legacy modeset code for nv04-nv4x, and atomic from
> nv50+ going forward.
> 
> Should be possible to find a pragmatic solution here going forward,
> despite tons of hw and heritage. If we use existing hard to retest hw
> support to stop new driver submissions from doing the right thing,
> that's a clear failure, we need a better approach here.
> -Daniel
> 

Right, and I just wanted to add that there seems to be a similar (maybe 
less complex?) situation for the CSIS CSI controller. In that case we 
already have two separate drivers for pretty much the same hardware in 
the media subsystem, media/platform/exynos4-is/mipi-csis.c for the 
exynos and staging/media/imx/imx7-mipi-csis.c for the imx.

I don't know the history for this, but it just came to my mind that this 
case is related and it might be interesting for the scope of this 
discussion.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

* Re: [PATCH v2 10/16] drm/exynos: implement a drm bridge
  2021-04-20 11:42                                         ` Frieder Schrempf
@ 2021-04-20 14:27                                           ` Laurent Pinchart
  0 siblings, 0 replies; 61+ messages in thread
From: Laurent Pinchart @ 2021-04-20 14:27 UTC (permalink / raw)
  To: Frieder Schrempf
  Cc: ch, Neil Armstrong, dri-devel, Andrzej Hajda, Marek Szyprowski,
	Marek Vasut, linux-samsung-soc, Joonyoung Shim,
	Krzysztof Kozlowski, dl-linux-imx, Sascha Hauer,
	Bartlomiej Zolnierkiewicz, sylvester.nawrocki, aford173,
	abel.vesa, aisheng.dong, Seung-Woo Kim, Michael Tretter,
	Kyungmin Park, Shawn Guo

Hi Frieder,

On Tue, Apr 20, 2021 at 01:42:05PM +0200, Frieder Schrempf wrote:
> On 23.02.21 13:07, Daniel Vetter wrote:
> > On Thu, Feb 18, 2021 at 5:02 PM Andrzej Hajda <a.hajda@samsung.com> wrote:
> >> W dniu 18.02.2021 o 09:04, Michael Tretter pisze:
> >>> On Wed, 10 Feb 2021 10:10:37 +0100, Frieder Schrempf wrote:
> >>>> On 04.02.21 18:46, Daniel Vetter wrote:
> >>>>> On Thu, Feb 4, 2021 at 6:26 PM Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote:
> >>>>>> On Thu, Feb 04, 2021 at 06:19:22PM +0100, Daniel Vetter wrote:
> >>>>>>> On Thu, Feb 4, 2021 at 5:28 PM Andrzej Hajda wrote:
> >>>>>>>> W dniu 04.02.2021 o 17:05, Daniel Vetter pisze:
> >>>>>>>>> On Thu, Feb 04, 2021 at 11:56:32AM +0100, Michael Tretter wrote:
> >>>>>>>>>> On Thu, 04 Feb 2021 11:17:49 +0100, Daniel Vetter wrote:
> >>>>>>>>>>> On Wed, Feb 3, 2021 at 9:32 PM Michael Tretter wrote:
> >>>>>>>>>>>> On Mon, 01 Feb 2021 17:33:14 +0100, Michael Tretter wrote:
> >>>>>>>>>>>>> On Tue, 15 Sep 2020 21:40:40 +0200, Andrzej Hajda wrote:
> >>>>>>>>>>>>>> W dniu 14.09.2020 o 23:19, Andrzej Hajda pisze:
> >>>>>>>>>>>>>>> On 14.09.2020 22:01, Michael Tretter wrote:
> >>>>>>>>>>>>>>>> On Mon, 14 Sep 2020 14:31:19 +0200, Marek Szyprowski wrote:
> >>>>>>>>>>>>>>>>> On 14.09.2020 10:29, Marek Szyprowski wrote:
> >>>>>>>>>>>>>>>>>> On 11.09.2020 15:54, Michael Tretter wrote:
> >>>>>>>>>>>>>>>>>>> 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@pengutronix.de>
> >>>>>>>>>>>>>>>>>> This one (and the whole series applied) still fails on Exynos boards:
> >>>>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping
> >>>>>>>>>>>>>>>>>> operations
> >>>>>>>>>>>>>>>>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> >>>>>>>>>>>>>>>>>> OF: graph: no port node found in /soc/dsi@11c80000
> >>>>>>>>>>>>>>>>>> 8<--- cut here ---
> >>>>>>>>>>>>>>>>>> Unable to handle kernel NULL pointer dereference at virtual address
> >>>>>>>>>>>>>>>>>> 00000084
> >>>>>>>>>>>>>>>>>> pgd = (ptrval)
> >>>>>>>>>>>>>>>>>> [00000084] *pgd=00000000
> >>>>>>>>>>>>>>>>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> >>>>>>>>>>>>>>>>>> Modules linked in:
> >>>>>>>>>>>>>>>>>> CPU: 1 PID: 1 Comm: swapper/0 Not tainted
> >>>>>>>>>>>>>>>>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec #1608
> >>>>>>>>>>>>>>>>>> Hardware name: Samsung Exynos (Flattened Device Tree)
> >>>>>>>>>>>>>>>>>> PC is at drm_bridge_attach+0x18/0x164
> >>>>>>>>>>>>>>>>>> LR is at exynos_dsi_bind+0x88/0xa8
> >>>>>>>>>>>>>>>>>> pc : [<c0628c08>]    lr : [<c064d560>]    psr: 20000013
> >>>>>>>>>>>>>>>>>> sp : ef0dfca8  ip : 00000002  fp : c13190e0
> >>>>>>>>>>>>>>>>>> r10: 00000000  r9 : ee46d580  r8 : c13190e0
> >>>>>>>>>>>>>>>>>> r7 : ee438800  r6 : 00000018  r5 : ef253810  r4 : ef39e840
> >>>>>>>>>>>>>>>>>> r3 : 00000000  r2 : 00000018  r1 : ef39e888  r0 : ef39e840
> >>>>>>>>>>>>>>>>>> Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> >>>>>>>>>>>>>>>>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> >>>>>>>>>>>>>>>>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> >>>>>>>>>>>>>>>>>> Stack: (0xef0dfca8 to 0xef0e0000)
> >>>>>>>>>>>>>>>>>> ...
> >>>>>>>>>>>>>>>>>> [<c0628c08>] (drm_bridge_attach) from [<c064d560>]
> >>>>>>>>>>>>>>>>>> (exynos_dsi_bind+0x88/0xa8)
> >>>>>>>>>>>>>>>>>> [<c064d560>] (exynos_dsi_bind) from [<c066a800>]
> >>>>>>>>>>>>>>>>>> (component_bind_all+0xfc/0x290)
> >>>>>>>>>>>>>>>>>> [<c066a800>] (component_bind_all) from [<c0649dc0>]
> >>>>>>>>>>>>>>>>>> (exynos_drm_bind+0xe4/0x19c)
> >>>>>>>>>>>>>>>>>> [<c0649dc0>] (exynos_drm_bind) from [<c066ad74>]
> >>>>>>>>>>>>>>>>>> (try_to_bring_up_master+0x1e4/0x2c4)
> >>>>>>>>>>>>>>>>>> [<c066ad74>] (try_to_bring_up_master) from [<c066b2b4>]
> >>>>>>>>>>>>>>>>>> (component_master_add_with_match+0xd4/0x108)
> >>>>>>>>>>>>>>>>>> [<c066b2b4>] (component_master_add_with_match) from [<c0649ae8>]
> >>>>>>>>>>>>>>>>>> (exynos_drm_platform_probe+0xe4/0x110)
> >>>>>>>>>>>>>>>>>> [<c0649ae8>] (exynos_drm_platform_probe) from [<c0674e6c>]
> >>>>>>>>>>>>>>>>>> (platform_drv_probe+0x6c/0xa4)
> >>>>>>>>>>>>>>>>>> [<c0674e6c>] (platform_drv_probe) from [<c067242c>]
> >>>>>>>>>>>>>>>>>> (really_probe+0x200/0x4fc)
> >>>>>>>>>>>>>>>>>> [<c067242c>] (really_probe) from [<c06728f0>]
> >>>>>>>>>>>>>>>>>> (driver_probe_device+0x78/0x1fc)
> >>>>>>>>>>>>>>>>>> [<c06728f0>] (driver_probe_device) from [<c0672cd8>]
> >>>>>>>>>>>>>>>>>> (device_driver_attach+0x58/0x60)
> >>>>>>>>>>>>>>>>>> [<c0672cd8>] (device_driver_attach) from [<c0672dbc>]
> >>>>>>>>>>>>>>>>>> (__driver_attach+0xdc/0x174)
> >>>>>>>>>>>>>>>>>> [<c0672dbc>] (__driver_attach) from [<c06701b4>]
> >>>>>>>>>>>>>>>>>> (bus_for_each_dev+0x68/0xb4)
> >>>>>>>>>>>>>>>>>> [<c06701b4>] (bus_for_each_dev) from [<c06714e8>]
> >>>>>>>>>>>>>>>>>> (bus_add_driver+0x158/0x214)
> >>>>>>>>>>>>>>>>>> [<c06714e8>] (bus_add_driver) from [<c0673c1c>]
> >>>>>>>>>>>>>>>>>> (driver_register+0x78/0x110)
> >>>>>>>>>>>>>>>>>> [<c0673c1c>] (driver_register) from [<c0649ca8>]
> >>>>>>>>>>>>>>>>>> (exynos_drm_init+0xe4/0x118)
> >>>>>>>>>>>>>>>>>> [<c0649ca8>] (exynos_drm_init) from [<c0102484>]
> >>>>>>>>>>>>>>>>>> (do_one_initcall+0x8c/0x42c)
> >>>>>>>>>>>>>>>>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
> >>>>>>>>>>>>>>>>>> (kernel_init_freeable+0x190/0x1dc)
> >>>>>>>>>>>>>>>>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7880>]
> >>>>>>>>>>>>>>>>>> (kernel_init+0x8/0x118)
> >>>>>>>>>>>>>>>>>> [<c0af7880>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> >>>>>>>>>>>>>>>>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
> >>>>>>>>>>>>>>>>>> ...
> >>>>>>>>>>>>>>>>>> ---[ end trace ee27f313f9ed9da1 ]---
> >>>>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>>>> # arm-linux-gnueabi-addr2line -e vmlinux c0628c08
> >>>>>>>>>>>>>>>>>> drivers/gpu/drm/drm_bridge.c:184 (discriminator 1)
> >>>>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>>>> I will try to debug it a bit more today.
> >>>>>>>>>>>>>>>>> The above crash has been caused by lack of in_bridge initialization to
> >>>>>>>>>>>>>>>>> NULL in exynos_dsi_bind() in this patch. However, fixing it reveals
> >>>>>>>>>>>>>>>>> another issue:
> >>>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>>> [drm] Exynos DRM: using 11c00000.fimd device for DMA mapping operations
> >>>>>>>>>>>>>>>>> exynos-drm exynos-drm: bound 11c00000.fimd (ops fimd_component_ops)
> >>>>>>>>>>>>>>>>> OF: graph: no port node found in /soc/dsi@11c80000
> >>>>>>>>>>>>>>>>> 8<--- cut here ---
> >>>>>>>>>>>>>>>>> Unable to handle kernel NULL pointer dereference at virtual address
> >>>>>>>>>>>>>>>>> 00000280
> >>>>>>>>>>>>>>>>> pgd = (ptrval)
> >>>>>>>>>>>>>>>>> [00000280] *pgd=00000000
> >>>>>>>>>>>>>>>>> Internal error: Oops: 5 [#1] PREEMPT SMP ARM
> >>>>>>>>>>>>>>>>> Modules linked in:
> >>>>>>>>>>>>>>>>> CPU: 0 PID: 1 Comm: swapper/0 Not tainted
> >>>>>>>>>>>>>>>>> 5.9.0-rc4-next-20200911-00010-g417dc70d70ec-dirty #1613
> >>>>>>>>>>>>>>>>> Hardware name: Samsung Exynos (Flattened Device Tree)
> >>>>>>>>>>>>>>>>> PC is at __mutex_lock+0x54/0xb18
> >>>>>>>>>>>>>>>>> LR is at lock_is_held_type+0x80/0x138
> >>>>>>>>>>>>>>>>> pc : [<c0afc920>]    lr : [<c0af63e8>]    psr: 60000013
> >>>>>>>>>>>>>>>>> sp : ef0dfd30  ip : 33937b74  fp : c13193c8
> >>>>>>>>>>>>>>>>> r10: c1208eec  r9 : 00000000  r8 : ee45f808
> >>>>>>>>>>>>>>>>> r7 : c19561a4  r6 : 00000000  r5 : 00000000  r4 : 0000024c
> >>>>>>>>>>>>>>>>> r3 : 00000000  r2 : 00204140  r1 : c124f13c  r0 : 00000000
> >>>>>>>>>>>>>>>>> Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
> >>>>>>>>>>>>>>>>> Control: 10c5387d  Table: 4000404a  DAC: 00000051
> >>>>>>>>>>>>>>>>> Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
> >>>>>>>>>>>>>>>>> Stack: (0xef0dfd30 to 0xef0e0000)
> >>>>>>>>>>>>>>>>> ...
> >>>>>>>>>>>>>>>>> [<c0afc920>] (__mutex_lock) from [<c0afd400>]
> >>>>>>>>>>>>>>>>> (mutex_lock_nested+0x1c/0x24)
> >>>>>>>>>>>>>>>>> [<c0afd400>] (mutex_lock_nested) from [<c064d4b8>]
> >>>>>>>>>>>>>>>>> (__exynos_dsi_host_attach+0x20/0x6c)
> >>>>>>>>>>>>>>>>> [<c064d4b8>] (__exynos_dsi_host_attach) from [<c064d914>]
> >>>>>>>>>>>>>>>>> (exynos_dsi_host_attach+0x70/0x194)
> >>>>>>>>>>>>>>>>> [<c064d914>] (exynos_dsi_host_attach) from [<c0656b64>]
> >>>>>>>>>>>>>>>>> (s6e8aa0_probe+0x1b0/0x218)
> >>>>>>>>>>>>>>>>> [<c0656b64>] (s6e8aa0_probe) from [<c0672530>]
> >>>>>>>>>>>>>>>>> (really_probe+0x200/0x4fc)
> >>>>>>>>>>>>>>>>> [<c0672530>] (really_probe) from [<c06729f4>]
> >>>>>>>>>>>>>>>>> (driver_probe_device+0x78/0x1fc)
> >>>>>>>>>>>>>>>>> [<c06729f4>] (driver_probe_device) from [<c0672ddc>]
> >>>>>>>>>>>>>>>>> (device_driver_attach+0x58/0x60)
> >>>>>>>>>>>>>>>>> [<c0672ddc>] (device_driver_attach) from [<c0672ec0>]
> >>>>>>>>>>>>>>>>> (__driver_attach+0xdc/0x174)
> >>>>>>>>>>>>>>>>> [<c0672ec0>] (__driver_attach) from [<c06702b8>]
> >>>>>>>>>>>>>>>>> (bus_for_each_dev+0x68/0xb4)
> >>>>>>>>>>>>>>>>> [<c06702b8>] (bus_for_each_dev) from [<c06715ec>]
> >>>>>>>>>>>>>>>>> (bus_add_driver+0x158/0x214)
> >>>>>>>>>>>>>>>>> [<c06715ec>] (bus_add_driver) from [<c0673d20>]
> >>>>>>>>>>>>>>>>> (driver_register+0x78/0x110)
> >>>>>>>>>>>>>>>>> [<c0673d20>] (driver_register) from [<c0102484>]
> >>>>>>>>>>>>>>>>> (do_one_initcall+0x8c/0x42c)
> >>>>>>>>>>>>>>>>> [<c0102484>] (do_one_initcall) from [<c11011c0>]
> >>>>>>>>>>>>>>>>> (kernel_init_freeable+0x190/0x1dc)
> >>>>>>>>>>>>>>>>> [<c11011c0>] (kernel_init_freeable) from [<c0af7988>]
> >>>>>>>>>>>>>>>>> (kernel_init+0x8/0x118)
> >>>>>>>>>>>>>>>>> [<c0af7988>] (kernel_init) from [<c0100114>] (ret_from_fork+0x14/0x20)
> >>>>>>>>>>>>>>>>> Exception stack(0xef0dffb0 to 0xef0dfff8)
> >>>>>>>>>>>>>>>>> ...
> >>>>>>>>>>>>>>>>> ---[ end trace c06e996ec2e8234d ]---
> >>>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>>> This means that dsi->encoder.dev is not initialized in
> >>>>>>>>>>>>>>>>> __exynos_dsi_host_attach().
> >>>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>>> This happens, because drm_bridge_attach() in exynos_dsi_bind() returned
> >>>>>>>>>>>>>>>>> earlier -517 (deferred probe), what causes cleanup of encoder and
> >>>>>>>>>>>>>>>>> release of all drm resources.
> >>>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>>> Then however, the panel tries to register itself and
> >>>>>>>>>>>>>>>>> exynos_dsi_host_attach() tries to access the released encoder (which is
> >>>>>>>>>>>>>>>>> zeroed in drm_encoder_release) and rest of resources, what causes
> >>>>>>>>>>>>>>>>> failure.
> >>>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>>> It looks that something is missing. Maybe mipi host has to be
> >>>>>>>>>>>>>>>>> registered
> >>>>>>>>>>>>>>>>> later, when bridge is ready? I have no idea how it is handled before
> >>>>>>>>>>>>>>>>> this patch. Andrzej, could you comment it a bit?
> >>>>>>>>>>>>>>>> I intentionally changed the order, because if another bridge follows
> >>>>>>>>>>>>>>>> in the
> >>>>>>>>>>>>>>>> pipeline, the probe of the drm driver has to be deferred until some
> >>>>>>>>>>>>>>>> bridge
> >>>>>>>>>>>>>>>> provides a connector. The next bridge registers itself via the
> >>>>>>>>>>>>>>>> host_attach
> >>>>>>>>>>>>>>>> function and the deferral is ensured via the bind for the bind/unbind
> >>>>>>>>>>>>>>>> API or
> >>>>>>>>>>>>>>>> the bridge_attach function otherwise.
> >>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>> On the other hand, the bridge does not have an encoder until the mipi
> >>>>>>>>>>>>>>>> device
> >>>>>>>>>>>>>>>> has been attached.
> >>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>> As a solution, the exynos dsi driver must initialize the encoder in
> >>>>>>>>>>>>>>>> exynos_dsi_probe instead of in exynos_dsi_bind and access the encoder
> >>>>>>>>>>>>>>>> via
> >>>>>>>>>>>>>>>> exynos_dsi instead of the bridge.
> >>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>> Can you try to move everything except samsung_dsim_bind from
> >>>>>>>>>>>>>>>> exynos_dsi_bind
> >>>>>>>>>>>>>>>> to exynos_dsi_probe (respectively for unbind) and report if it fixes the
> >>>>>>>>>>>>>>>> crash.
> >>>>>>>>>>>>>>> The original behaviour is that encoder (exynos_dsi) is registered
> >>>>>>>>>>>>>>> regardless of sink presence (initially panel, later also bridge) - it
> >>>>>>>>>>>>>>> avoids multiple issues with deferred probe, device driver bind/unbind
> >>>>>>>>>>>>>>> and module load/unload. Appearance or disappearance of sink is
> >>>>>>>>>>>>>>> reported to host nicely via DSI attach/detach callbacks - and it is
> >>>>>>>>>>>>>>> reflected in drm world as change state of the connector.
> >>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>> Registering DSI host in bind and unregistering in unbind assures that
> >>>>>>>>>>>>>>> if mipi_dsi device is attached/detached the drm device is always
> >>>>>>>>>>>>>>> present - it makes device/driver binding race free and allows to avoid
> >>>>>>>>>>>>>>> additional locking.
> >>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>> Moving DSI host registration to probe changes everything, for sure it
> >>>>>>>>>>>>>>> breaks the nice feature of DSI attach/detach callbacks and apparently
> >>>>>>>>>>>>>>> can cause different issues depending on device bind order.
> >>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>> I will try to look at the patches tomorrow and maybe I can find more
> >>>>>>>>>>>>>>> constructive comments :)
> >>>>>>>>>>>>>> As I said yesterday, exynos_dsi driver uses dsi host attach/detach
> >>>>>>>>>>>>>> callbacks to control appearance/disappearance of downstream device. It
> >>>>>>>>>>>>>> allows to:
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> 1. Safely bind/unbind different device drivers at any time and at any
> >>>>>>>>>>>>>> order, without killing exynos_drm and/or crashing system.
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> 2. Avoid issues with late drm init - on some platforms exynos_drm device
> >>>>>>>>>>>>>> appeared too late, due to deferred probe, and resulted in black screen
> >>>>>>>>>>>>>> in userspace.
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> Now if we want to convert exynos_dsi to drm_bridge I see following options:
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> A. Forgot about callbacks and make the exynos_drm to defer probing until
> >>>>>>>>>>>>>> exynos_dsi bridge is available, probably it will cause later exynos_drm
> >>>>>>>>>>>>>> appearance, thus probably black screen on some targets. So for sure it
> >>>>>>>>>>>>>> will be suboptimal. Making it bridge unbind safe would be another
> >>>>>>>>>>>>>> problem, but most developers do not care about it so why should we? :)
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> B. Try to mimic current behaviour - exynos_dsi register bridge ASAP,
> >>>>>>>>>>>>>> even if downstream devices are not yet attached, on attach/detach notify
> >>>>>>>>>>>>>> drm about it via connector status change, for this dsi_host registration
> >>>>>>>>>>>>>> should be performed from drm_bridge attach, I guess.
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> Option A is more standard, but is unsafe and causes other issues.
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> Option B keeps current behaviour.
> >>>>>>>>>>>>> Maybe we can have both, but I am not sure, if I am missing something:
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> I still prefer option A for the samsung-dsim driver, because it is more
> >>>>>>>>>>>>> standard, simpler and avoids issues with encoders, connectors or handling
> >>>>>>>>>>>>> hotplug.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> The idea is to use two bridges in the exynos-dsi driver: One bridge in the
> >>>>>>>>>>>>> samsung-dsim driver which implements option A and defers probing of the drm
> >>>>>>>>>>>>> driver until the next bridge is attached. And a second bridge in the
> >>>>>>>>>>>>> exynos_dsi that attaches to the first bridge (thus, allowing the exynos_drm
> >>>>>>>>>>>>> device to appear) and implements the hotplug handling for notifying drm via
> >>>>>>>>>>>>> connector status change.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> The driver for the i.MX8M would use the samsung-dsim bridge without an
> >>>>>>>>>>>>> additional bridge.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> This allows the samsung-dsim driver to expose the standard behavior while the
> >>>>>>>>>>>>> exynos_dsi may stick to the existing behavior for the exynos_drm driver.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> I hope this makes sense and does not sound too crazy. It might be difficult to
> >>>>>>>>>>>>> get the probing and mipi host/device registration correct, but I will try, if
> >>>>>>>>>>>>> this can work.
> >>>>>>>>>>>> Adding two bridges for being able to support hotplugging adds many special
> >>>>>>>>>>>> cases to the bridge driver and still requires more custom API to correctly add
> >>>>>>>>>>>> the second bridge. I don't think that this a viable path to go.
> >>>>>>>>>>> Just jumping in here: You cannot hotplug/hotremove anything from a
> >>>>>>>>>>> drm_device after drm_dev_register has been called, except
> >>>>>>>>>>> drm_connector. I didn't dig into details here so not sure whether you
> >>>>>>>>>>> want to late-bind your bridge after drm_dev_register is called or not,
> >>>>>>>>>>> so might just be fyi and not relevant to the discussion.
> >>>>>>>>>> Thanks. AFAIC that is exactly what is currently implemented in the exynos_drm
> >>>>>>>>>> driver (i.e. Option B)
> >>>>>>>>>>
> >>>>>>>>>> exynos_dsi_bind configures the encoder and registers a DSI host. Afterwards,
> >>>>>>>>>> exynos_drm_bind (as component_master_ops) calls drm_dev_register. Later, a DSI
> >>>>>>>>>> device might attach to the DSI host and call exynos_dsi_host_attach. In
> >>>>>>>>>> exynos_dsi_host_attach, the driver finds the drm_bridge for the DSI device and
> >>>>>>>>>> attaches this bridge to the encoder _after_ drm_dev_register has been called.
> >>>>>>>>>> This is invalid behavior, right?
> >>>>>>>>> Definitely not supported, I don't think we have the right locks in place
> >>>>>>>>> to make sure this works.
> >>>>>>>>>
> >>>>>>>>> Now if your _only_ adding a drm_bridge (and not an encoder or anything
> >>>>>>>>> like that), and you are adding the drm_connector correctly (like a
> >>>>>>>>> hotplugged DP MST sink), then that would at least work from a uapi pov.
> >>>>>>>>> Because drm_bridge isn't exposed as an uapi object.
> >>>>>>>>>
> >>>>>>>>> But yeah, as-is, don't :-)
> >>>>>>>>>
> >>>>>>>>> The solution here is a bunch of EPROBE_DEFER handling until all your
> >>>>>>>>> bridges are loaded, with or without the assistance of component.c
> >>>>>>>>> framework. Only then call drm_dev_register.
> >>>>>>>> I have impression we have similar conversation already.
> >>>>>>>>
> >>>>>>>> As you stated drm_bridge and drm_panel are not exposed to userspace so
> >>>>>>>> there shouldn't be problem with them from uapi PoV.
> >>>>>>>>
> >>>>>>>> On the other side drm_panel or drm_bridge are not used until pipeline
> >>>>>>>> enters connected state (at least they were not some time ago :) ). The
> >>>>>>>> issue is that bridge exposes drm_connector, but as you stated (again :)
> >>>>>>>> ) connectors can be hotplugged, so in theory it should work. Practical
> >>>>>>>> tests shows that it also works, but bugs can be still there.
> >>>>>>>>
> >>>>>>>> Bunch of EPROBE_DEFER was very slow (as a result userspace timeouted and
> >>>>>>>> decided there is no display), and does not handle unbinding/re-binding
> >>>>>>>> drivers.
> >>>>>>> Rebinding drivers should be fixed now, with a bunch of fixes in driver
> >>>>>>> core. If not, we need to fix this more.
> >>>>>>>
> >>>>>>> Also, EPROBE_DEFER is how this is supposed to work. If it's too slow,
> >>>>>>> we need to fix EPROBE_DEFER (there's ideas for pre-sorting that never
> >>>>>>> seem to go anywhere), not paper over it with bad architecture in
> >>>>>>> drivers.
> >>>>>> I've heard this argument multiple times, but it sounds more like an
> >>>>>> attempt to ignore the problem and hope it will fall on someone else's
> >>>>>> plate :-) Improvement in the probe deferral mechanism are certainly an
> >>>>>> option to explore, but as far as I can tell nobody has proven that this
> >>>>>> mechanism is or will be able to solve all problems related to probe
> >>>>>> ordering dependencies. I wouldn't rule out the need for different
> >>>>>> solutions for some of the issues.
> >>>>> Then build another one. But adding hotplug for stuff that is there,
> >>>>> and shouldn't be hotplugged, just because it's easier on driver
> >>>>> writers and harder on userspace isn't really a good approach.
> >>>>> -Daniel
> >>>> I think it is quite clear that replacing or reworking the deferral mechanism
> >>>> is out of scope for this discussion, which is why I would like to come back
> >>>> to the original issue and sum this up as far as I understand it (which is
> >>>> not really far when it comes to the details):
> >>>>
> >>>> We have the existing exynos driver that avoids the standard deferral
> >>>> mechanism in favor of something that works but Daniel describes as
> >>>> "definitely not supported".
> >>>>
> >>>> We have a proposal from Michael for converting the driver to the standard
> >>>> drm_bridge behavior and more work from Michael and Marek based on this to
> >>>> implement the platform specific parts for i.MX8MM.
> >>>>
> >>>>   From the i.MX8MM POV this approach already received some testing and looks
> >>>> good as far as I can judge. Upstreaming this solution is blocked because of
> >>>> objections from the Samsung maintainers.
> >>>>
> >>>> Sorry if I'm being blunt or naive, but where to go from here?
> >>>>
> >>> Maybe some more information by the Samsung maintainers would help:
> >>>
> >>> If I understand correctly, the main reason for the non-standard behavior is a
> >>> userspace application that runs into a timeout if the drm-device does not
> >>> appear in time. Correct? Is there something we can do about that?
> >>>
> >>> The other reason is the convenience of binding and unbinding a bridge driver,
> >>> while the drm device is kept available. Correct? Is this used in development,
> >>> testing, or production?
> >>>
> >>> Is there anything else that prevents the exynos drm from switching to the
> >>> standard behavior?
> >>>
> >>> Would a exynos drm specific wrapper, which uses a standard bridge driver but
> >>> exposes the non-standard behavior, be acceptable? (Unfortunately, my first try
> >>> on something like that felt really awkward and didn't really work.)
> >>
> >> Even if we drop this 'non-standard' behaviour, your task will be still
> >> quite difficult to fulfil - you are trying to completely rewrite core
> >> component of Exynos display pipeline without hardware to test.
> >>
> >> ExynosDSI is used in almost all Exynos platforms supported mainline (ls
> >> -1 arch/arm*/boot/dts/exynos*.dts | wc shows 35). It has different hw
> >> versions (4 compatibles) and is used in different configurations (video
> >> mode, command mode, with hw/sw trigger, connected to panels/bridges) and
> >> for sure with big heritage, since it was one of the 1st DSI drivers.
> >>
> >> Rewriting such driver is challenging, even with access to hw.
> >>
> >> So maybe it would be better to move common parts in your and exynos
> >> driver to 'shared library' and use it in both drivers - this way you
> >> have bigger chances to avoid traps.
> > 
> > If exynos really can't be fixed up in a reasonable way, then I think
> > sharing code doesn't make much sense - you drag the new driver down
> > with the old one that's just hanging in there the wrong way round. For
> > that case just copypaste the exynos code into a new clean drm_bridge
> > driver, and done.
> > 
> > That would also mean that new exynos support in drm/exynos would need
> > to be stalled until this is sorted out (at least for new platforms),
> > since continuing the old way really doesn't sound so great. Wouldn't
> > be the first time we just end up with a driver fork because the old
> > one has too much heritage and is too hard to change.
> > 
> > Note that this can also be done within one driver codebase, e.g.
> > nouveau has still legacy modeset code for nv04-nv4x, and atomic from
> > nv50+ going forward.
> > 
> > Should be possible to find a pragmatic solution here going forward,
> > despite tons of hw and heritage. If we use existing hard to retest hw
> > support to stop new driver submissions from doing the right thing,
> > that's a clear failure, we need a better approach here.
> > -Daniel
> 
> Right, and I just wanted to add that there seems to be a similar (maybe 
> less complex?) situation for the CSIS CSI controller. In that case we 
> already have two separate drivers for pretty much the same hardware in 
> the media subsystem, media/platform/exynos4-is/mipi-csis.c for the 
> exynos and staging/media/imx/imx7-mipi-csis.c for the imx.

And we would have at least a third on in
staging/media/imx/imx8-mipi-csi2-sam.c if we followed the NXP BSP :-)
I've added support for i.MX8 to the imx7-mipi-csis driver recently, and
I'm half-tempted to merge it with the
media/platform/exynos4-is/mipi-csis.c driver at some point. Lack of
Exynos test hardware and documentation, as well as of time, will likely
prevent that from happening, but if someone wanted to give it a go, it
would be nice.

> I don't know the history for this, but it just came to my mind that this 
> case is related and it might be interesting for the scope of this 
> discussion.

I think staging/media/imx/imx7-mipi-csis.c was developed in the NXP BSP,
and we merged it upstream without realizing it was the same IP core as
media/platform/exynos4-is/mipi-csis.c.

-- 
Regards,

Laurent Pinchart
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 61+ messages in thread

end of thread, other threads:[~2021-04-20 14:27 UTC | newest]

Thread overview: 61+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <CGME20200911165401epcas1p3c7ee84dd01db93f472d6fa21c1100f29@epcas1p3.samsung.com>
2020-09-11 13:53 ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Michael Tretter
2020-09-11 13:53   ` [PATCH v2 01/16] drm/encoder: remove obsolete documentation of bridge Michael Tretter
2020-11-07 15:07     ` Adam Ford
2020-11-10  8:46       ` Michael Tretter
2020-11-07 22:17     ` Sam Ravnborg
2020-09-11 13:53   ` [PATCH v2 02/16] drm/exynos: remove in_bridge_node from exynos_dsi Michael Tretter
2020-11-07 22:19     ` Sam Ravnborg
2020-11-09  2:24       ` Inki Dae
2020-09-11 13:54   ` [PATCH v2 03/16] drm/exynos: use exynos_dsi as drvdata Michael Tretter
2020-11-07 22:24     ` Sam Ravnborg
2020-11-09  2:24       ` Inki Dae
2020-09-11 13:54   ` [PATCH v2 04/16] drm/exynos: extract helper functions for probe Michael Tretter
2020-11-07 22:27     ` Sam Ravnborg
2020-11-09  2:52       ` Inki Dae
2020-09-11 13:54   ` [PATCH v2 05/16] drm/exynos: move dsi host registration to probe Michael Tretter
2020-09-11 13:54   ` [PATCH v2 06/16] drm/exynos: shift register values to fields on write Michael Tretter
2020-11-07 22:39     ` Sam Ravnborg
2020-11-10  8:28       ` Michael Tretter
2020-09-11 13:54   ` [PATCH v2 07/16] drm/exynos: use identifier instead of register offsets Michael Tretter
2020-09-11 13:54   ` [PATCH v2 08/16] drm/exynos: add host_ops callback for platform drivers Michael Tretter
2020-09-15 17:07     ` Andrzej Hajda
2020-09-15 18:02       ` Michael Tretter
2020-09-16 22:01         ` Andrzej Hajda
2020-09-11 13:54   ` [PATCH v2 09/16] drm/exynos: add callback for tearing effect handler Michael Tretter
2020-09-11 13:54   ` [PATCH v2 10/16] drm/exynos: implement a drm bridge Michael Tretter
2020-09-14  8:29     ` Marek Szyprowski
2020-09-14 12:31       ` Marek Szyprowski
2020-09-14 20:01         ` Michael Tretter
2020-09-14 21:19           ` Andrzej Hajda
2020-09-15 19:40             ` Andrzej Hajda
2021-02-01 16:33               ` Michael Tretter
2021-02-03 20:31                 ` Michael Tretter
2021-02-04 10:17                   ` Daniel Vetter
2021-02-04 10:56                     ` Michael Tretter
2021-02-04 16:05                       ` Daniel Vetter
2021-02-04 16:28                         ` Andrzej Hajda
2021-02-04 17:19                           ` Daniel Vetter
2021-02-04 17:26                             ` Laurent Pinchart
2021-02-04 17:46                               ` Daniel Vetter
2021-02-10  9:10                                 ` Frieder Schrempf
2021-02-18  8:04                                   ` Michael Tretter
2021-02-18 16:02                                     ` Andrzej Hajda
2021-02-23 12:07                                       ` Daniel Vetter
2021-04-20 11:42                                         ` Frieder Schrempf
2021-04-20 14:27                                           ` Laurent Pinchart
2020-09-11 13:54   ` [PATCH v2 11/16] drm/exynos: convert encoder functions to bridge function Michael Tretter
2020-09-11 13:54   ` [PATCH v2 12/16] drm/exynos: configure mode on drm bridge Michael Tretter
2020-09-11 13:54   ` [PATCH v2 13/16] drm/exynos: get encoder from bridge whenever possible Michael Tretter
2020-09-11 13:54   ` [PATCH v2 14/16] drm/exynos: add API functions for platform drivers Michael Tretter
2020-09-11 13:54   ` [PATCH v2 15/16] drm/exynos: split out platform specific code Michael Tretter
2020-09-11 13:54   ` [PATCH v2 16/16] drm/exynos: move bridge driver to bridges Michael Tretter
2020-09-16  7:58     ` Daniel Vetter
2020-09-16  8:58       ` Michael Tretter
2020-09-16  9:03         ` Daniel Vetter
2020-11-09  3:15   ` [PATCH v2 00/16] drm/exynos: Convert driver to drm bridge Inki Dae
2020-11-10  8:13     ` Michael Tretter
2020-11-10 12:34       ` Marek Szyprowski
2020-11-11  3:04       ` Inki Dae
2020-11-11  3:11         ` Inki Dae
2020-11-11 10:18           ` Michael Tretter
2020-11-13  9:34             ` Inki Dae

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).