All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/8] drm/rockchip: migrate to common dw-mipi-dsi bridge and dual-dsi
@ 2018-06-18 10:27 Heiko Stuebner
  2018-06-18 10:27 ` [PATCH v2 1/8] drm/bridge/synopsys: dsi: move mipi_dsi_host_unregister to __dw_mipi_dsi_remove Heiko Stuebner
                   ` (8 more replies)
  0 siblings, 9 replies; 26+ messages in thread
From: Heiko Stuebner @ 2018-06-18 10:27 UTC (permalink / raw)
  To: dri-devel
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, philippe.cornu,
	yannick.fertre, linux-rockchip, nickey.yang, robh+dt,
	thierry.reding, laurent.pinchart, mka

The Rockchip DSI driver was separate till now, not using the common
bridge driver that was introduced a bit later. So this series migrates
over to use that common bridge driver and then also adds support for
dual-dsi to both the bridge and Rockchip glue code.

The bridge-migration itself is based on Nickeys earlier v8
work, but adapted to current kernels and with a new split between probe
and bind, so that we do not create and drop the dsi-host on each deferred
bind attempt.

changes in v2:
- rebase against newer drm code (dsi-bridge+rockchip changes)
- add SPDX header to new glue driver
- expect regular interface lanes from panel (like 4) not the double number
  Similar to tegra
- keep links to both master and slave


The dual-dsi setup follows the port description introduced by Archit [0],
in that the panel defines two input ports that get connected to both
dsi-controllers instances. So on Gru-Scarlett this looks for example
like:

&mipi_dsi {
	status = "okay";
	clock-master;

	ports {
		mipi_out: port@1 {
			reg = <1>;

			mipi_out_panel: endpoint {
				remote-endpoint = <&mipi_in_panel>;
			};
		};
	};

	mipi_panel: panel@0 {
		/* 2 different panels are used, compatibles are in dts files */
		reg = <0>;
		backlight = <&backlight>;
		enable-gpios = <&gpio4 25 GPIO_ACTIVE_HIGH>;
		pinctrl-names = "default";
		pinctrl-0 = <&display_rst_l>;

		ports {
			#address-cells = <1>;
			#size-cells = <0>;

			port@0 {
				reg = <0>;

				mipi_in_panel: endpoint {
					remote-endpoint = <&mipi_out_panel>;
				};
			};

			port@1 {
				reg = <1>;

				mipi1_in_panel: endpoint@1 {
					remote-endpoint = <&mipi1_out_panel>;
				};
			};
		};
	};
};

&mipi_dsi1 {
	status = "okay";

	ports {
		mipi1_out: port@1 {
			reg = <1>;

			mipi1_out_panel: endpoint {
				remote-endpoint = <&mipi1_in_panel>;
			};
		};
	};
};


The driver internal setup is pretty similar to what tegra does with
its ganged-mode [1][2]. But here a new helper function allows to traverse
the devicetree from one controller port through the panel to find
another dsi-controller using that same panel. This way we don't need
a special phandle-property to link the controllers together.

For the CRTC it is still one single display to handle, only with
an additional switch that enables the dual-dsi output.


For practical purposes it is possible to just pick half the series
(till patch 5) to get the migration to the bridge driver first,
so that we can get rid of the dw-dsi copy in the Rockchip driver.

But of course Acks / Reviews of the dsi-bridge changes would be needed.


[0] https://patchwork.kernel.org/patch/10172381/
[1] https://lkml.org/lkml/2014/8/5/396
[2] https://patchwork.kernel.org/patch/5075161/

Heiko Stuebner (5):
  drm/bridge/synopsys: dsi: move mipi_dsi_host_unregister to
    __dw_mipi_dsi_remove
  drm/bridge/synopsys: dsi: don't call __dw_mipi_dsi_probe from
    dw_mipi_dsi_bind
  drm/bridge/synopsys: dsi: defer probing if panel not available in
    bridge-attach
  drm/dsi: add helper function to find the second host in a dual-dsi
    setup
  drm/rockchip: dsi: add dual mipi support

Nickey Yang (3):
  dt-bindings: display: rockchip: update DSI controller
  drm/rockchip: dsi: migrate to use dw-mipi-dsi bridge driver
  drm/bridge/synopsys: dsi: add dual-dsi support

 .../display/rockchip/dw_mipi_dsi_rockchip.txt |   23 +-
 drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c |  115 +-
 drivers/gpu/drm/drm_mipi_dsi.c                |   56 +
 drivers/gpu/drm/rockchip/Kconfig              |    2 +-
 drivers/gpu/drm/rockchip/Makefile             |    2 +-
 .../gpu/drm/rockchip/dw-mipi-dsi-rockchip.c   |  992 ++++++++++++
 drivers/gpu/drm/rockchip/dw-mipi-dsi.c        | 1349 -----------------
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c   |    2 +-
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h   |    3 +-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c   |    3 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h   |    4 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c   |    1 +
 include/drm/bridge/dw_mipi_dsi.h              |    6 +-
 include/drm/drm_mipi_dsi.h                    |    2 +
 14 files changed, 1179 insertions(+), 1381 deletions(-)
 create mode 100644 drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
 delete mode 100644 drivers/gpu/drm/rockchip/dw-mipi-dsi.c

-- 
2.17.0

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

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

* [PATCH v2 1/8] drm/bridge/synopsys: dsi: move mipi_dsi_host_unregister to __dw_mipi_dsi_remove
  2018-06-18 10:27 [PATCH v2 0/8] drm/rockchip: migrate to common dw-mipi-dsi bridge and dual-dsi Heiko Stuebner
@ 2018-06-18 10:27 ` Heiko Stuebner
  2018-07-03 10:40   ` Andrzej Hajda
  2018-07-03 11:58   ` Philippe CORNU
  2018-06-18 10:28 ` [PATCH v2 2/8] drm/bridge/synopsys: dsi: don't call __dw_mipi_dsi_probe from dw_mipi_dsi_bind Heiko Stuebner
                   ` (7 subsequent siblings)
  8 siblings, 2 replies; 26+ messages in thread
From: Heiko Stuebner @ 2018-06-18 10:27 UTC (permalink / raw)
  To: dri-devel
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, philippe.cornu,
	yannick.fertre, linux-rockchip, nickey.yang, robh+dt,
	thierry.reding, laurent.pinchart, mka

Right now the host is only unregistered when the driver is used via the
bridge api and not via the component api, leading to the host staying
registered in cases like probe deferral.

So move the host unregister to the general remove function, so that it
gets cleaned up in all cases.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
index fd7999642cf8..07cde255cab2 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
@@ -941,6 +941,8 @@ __dw_mipi_dsi_probe(struct platform_device *pdev,
 
 static void __dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi)
 {
+	mipi_dsi_host_unregister(&dsi->dsi_host);
+
 	pm_runtime_disable(dsi->dev);
 }
 
@@ -957,8 +959,6 @@ EXPORT_SYMBOL_GPL(dw_mipi_dsi_probe);
 
 void dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi)
 {
-	mipi_dsi_host_unregister(&dsi->dsi_host);
-
 	__dw_mipi_dsi_remove(dsi);
 }
 EXPORT_SYMBOL_GPL(dw_mipi_dsi_remove);
-- 
2.17.0

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

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

* [PATCH v2 2/8] drm/bridge/synopsys: dsi: don't call __dw_mipi_dsi_probe from dw_mipi_dsi_bind
  2018-06-18 10:27 [PATCH v2 0/8] drm/rockchip: migrate to common dw-mipi-dsi bridge and dual-dsi Heiko Stuebner
  2018-06-18 10:27 ` [PATCH v2 1/8] drm/bridge/synopsys: dsi: move mipi_dsi_host_unregister to __dw_mipi_dsi_remove Heiko Stuebner
@ 2018-06-18 10:28 ` Heiko Stuebner
  2018-07-03 12:16   ` Andrzej Hajda
  2018-06-18 10:28 ` [PATCH v2 3/8] drm/bridge/synopsys: dsi: defer probing if panel not available in bridge-attach Heiko Stuebner
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 26+ messages in thread
From: Heiko Stuebner @ 2018-06-18 10:28 UTC (permalink / raw)
  To: dri-devel
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, philippe.cornu,
	yannick.fertre, linux-rockchip, nickey.yang, robh+dt,
	thierry.reding, laurent.pinchart, mka

__dw_mipi_dsi_probe() does all the grabbing of resources and does it using
devm-helpers. So this is happening on each try of master bringup possibly
slowing down things a lot.

Drivers using the component framework may instead want call dw_mipi_dsi_probe
separately in their probe function setup resources early. That way the dsi
bus also gets created earlier and also not recreated on each bind-try, so
that attached panels can load their modules and be probed way before the
bridge-attach in the bind call.

So drop the call to __dw_mipi_dsi_probe and modify the function to take
a struct dw_mipi_dsi instead of the platform-device.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 15 +++------------
 include/drm/bridge/dw_mipi_dsi.h              |  5 +----
 2 files changed, 4 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
index 07cde255cab2..bb4aeca5c0f9 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
@@ -966,31 +966,22 @@ EXPORT_SYMBOL_GPL(dw_mipi_dsi_remove);
 /*
  * Bind/unbind API, used from platforms based on the component framework.
  */
-struct dw_mipi_dsi *
-dw_mipi_dsi_bind(struct platform_device *pdev, struct drm_encoder *encoder,
-		 const struct dw_mipi_dsi_plat_data *plat_data)
+int dw_mipi_dsi_bind(struct dw_mipi_dsi *dsi, struct drm_encoder *encoder)
 {
-	struct dw_mipi_dsi *dsi;
 	int ret;
 
-	dsi = __dw_mipi_dsi_probe(pdev, plat_data);
-	if (IS_ERR(dsi))
-		return dsi;
-
 	ret = drm_bridge_attach(encoder, &dsi->bridge, NULL);
 	if (ret) {
-		dw_mipi_dsi_remove(dsi);
 		DRM_ERROR("Failed to initialize bridge with drm\n");
-		return ERR_PTR(ret);
+		return ret;
 	}
 
-	return dsi;
+	return ret;
 }
 EXPORT_SYMBOL_GPL(dw_mipi_dsi_bind);
 
 void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi)
 {
-	__dw_mipi_dsi_remove(dsi);
 }
 EXPORT_SYMBOL_GPL(dw_mipi_dsi_unbind);
 
diff --git a/include/drm/bridge/dw_mipi_dsi.h b/include/drm/bridge/dw_mipi_dsi.h
index d9c6d549f971..6d7f8eb5d9f2 100644
--- a/include/drm/bridge/dw_mipi_dsi.h
+++ b/include/drm/bridge/dw_mipi_dsi.h
@@ -35,10 +35,7 @@ struct dw_mipi_dsi *dw_mipi_dsi_probe(struct platform_device *pdev,
 				      const struct dw_mipi_dsi_plat_data
 				      *plat_data);
 void dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi);
-struct dw_mipi_dsi *dw_mipi_dsi_bind(struct platform_device *pdev,
-				     struct drm_encoder *encoder,
-				     const struct dw_mipi_dsi_plat_data
-				     *plat_data);
+int dw_mipi_dsi_bind(struct dw_mipi_dsi *dsi, struct drm_encoder *encoder);
 void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi);
 
 #endif /* __DW_MIPI_DSI__ */
-- 
2.17.0

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

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

* [PATCH v2 3/8] drm/bridge/synopsys: dsi: defer probing if panel not available in bridge-attach
  2018-06-18 10:27 [PATCH v2 0/8] drm/rockchip: migrate to common dw-mipi-dsi bridge and dual-dsi Heiko Stuebner
  2018-06-18 10:27 ` [PATCH v2 1/8] drm/bridge/synopsys: dsi: move mipi_dsi_host_unregister to __dw_mipi_dsi_remove Heiko Stuebner
  2018-06-18 10:28 ` [PATCH v2 2/8] drm/bridge/synopsys: dsi: don't call __dw_mipi_dsi_probe from dw_mipi_dsi_bind Heiko Stuebner
@ 2018-06-18 10:28 ` Heiko Stuebner
  2018-07-03 11:57   ` Philippe CORNU
  2018-07-03 12:42   ` Andrzej Hajda
  2018-06-18 10:28 ` [PATCH v2 4/8] dt-bindings: display: rockchip: update DSI controller Heiko Stuebner
                   ` (5 subsequent siblings)
  8 siblings, 2 replies; 26+ messages in thread
From: Heiko Stuebner @ 2018-06-18 10:28 UTC (permalink / raw)
  To: dri-devel
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, philippe.cornu,
	yannick.fertre, linux-rockchip, nickey.yang, robh+dt,
	thierry.reding, laurent.pinchart, mka

When the panel-driver is build as a module it currently fails hard as the
panel cannot be probed directly:

dw_mipi_dsi_bind()
  __dw_mipi_dsi_probe()
    creates dsi bus
    creates panel device
    triggers panel module load
    panel not probed (module not loaded or panel probe slow)
  drm_bridge_attach
    fails with -EINVAL due to empty panel_bridge

So emit a -EPROBE_DEFER in that case to give the driver another
chance at getting the display later.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
index bb4aeca5c0f9..bd503f000ed5 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
@@ -835,6 +835,9 @@ static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge)
 		return -ENODEV;
 	}
 
+	if (!dsi->panel_bridge)
+		return -EPROBE_DEFER;
+
 	/* Set the encoder type as caller does not know it */
 	bridge->encoder->encoder_type = DRM_MODE_ENCODER_DSI;
 
-- 
2.17.0

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

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

* [PATCH v2 4/8] dt-bindings: display: rockchip: update DSI controller
  2018-06-18 10:27 [PATCH v2 0/8] drm/rockchip: migrate to common dw-mipi-dsi bridge and dual-dsi Heiko Stuebner
                   ` (2 preceding siblings ...)
  2018-06-18 10:28 ` [PATCH v2 3/8] drm/bridge/synopsys: dsi: defer probing if panel not available in bridge-attach Heiko Stuebner
@ 2018-06-18 10:28 ` Heiko Stuebner
  2018-06-18 10:30   ` Heiko Stuebner
  2018-06-18 10:28 ` [PATCH v2 5/8] drm/rockchip: dsi: migrate to use dw-mipi-dsi bridge driver Heiko Stuebner
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 26+ messages in thread
From: Heiko Stuebner @ 2018-06-18 10:28 UTC (permalink / raw)
  To: dri-devel
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, philippe.cornu,
	yannick.fertre, linux-rockchip, nickey.yang, robh+dt,
	thierry.reding, laurent.pinchart, mka

From: Nickey Yang <nickey.yang@rock-chips.com>

This patch update describe panel/port links, including
unit addresses in documentation of device tree bindings
for the rockchip DSI controller based on the Synopsys
DesignWare MIPI DSI host controller.

Signed-off-by: Nickey Yang <nickey.yang@rock-chips.com>
Reviewed-by: Brian Norris <briannorris@chromium.org>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 .../display/rockchip/dw_mipi_dsi_rockchip.txt | 23 +++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
index 6bb59ab39f2f..ce4c1fc9116c 100644
--- a/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
+++ b/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
@@ -14,6 +14,8 @@ Required properties:
 - rockchip,grf: this soc should set GRF regs to mux vopl/vopb.
 - ports: contain a port node with endpoint definitions as defined in [2].
   For vopb,set the reg = <0> and set the reg = <1> for vopl.
+- video port 0 for the VOP input, the remote endpoint maybe vopb or vopl
+- video port 1 for either a panel or subsequent encoder
 
 Optional properties:
 - power-domains: a phandle to mipi dsi power domain node.
@@ -40,11 +42,12 @@ Example:
 		ports {
 			#address-cells = <1>;
 			#size-cells = <0>;
-			reg = <1>;
 
-			mipi_in: port {
+			mipi_in: port@0 {
+				reg = <0>;
 				#address-cells = <1>;
 				#size-cells = <0>;
+
 				mipi_in_vopb: endpoint@0 {
 					reg = <0>;
 					remote-endpoint = <&vopb_out_mipi>;
@@ -54,6 +57,16 @@ Example:
 					remote-endpoint = <&vopl_out_mipi>;
 				};
 			};
+
+			mipi_out: port@1 {
+				reg = <1>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				mipi_out_panel: endpoint {
+					remote-endpoint = <&panel_in_mipi>;
+				};
+			};
 		};
 
 		panel {
@@ -64,5 +77,11 @@ Example:
 			pinctrl-names = "default";
 			pinctrl-0 = <&lcd_en>;
 			backlight = <&backlight>;
+
+			port {
+				panel_in_mipi: endpoint {
+					remote-endpoint = <&mipi_out_panel>;
+				};
+			};
 		};
 	};
-- 
2.17.0

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

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

* [PATCH v2 5/8] drm/rockchip: dsi: migrate to use dw-mipi-dsi bridge driver
  2018-06-18 10:27 [PATCH v2 0/8] drm/rockchip: migrate to common dw-mipi-dsi bridge and dual-dsi Heiko Stuebner
                   ` (3 preceding siblings ...)
  2018-06-18 10:28 ` [PATCH v2 4/8] dt-bindings: display: rockchip: update DSI controller Heiko Stuebner
@ 2018-06-18 10:28 ` Heiko Stuebner
  2018-07-03 13:06   ` Andrzej Hajda
  2018-06-18 10:28 ` [PATCH v2 6/8] drm/dsi: add helper function to find the second host in a dual-dsi setup Heiko Stuebner
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 26+ messages in thread
From: Heiko Stuebner @ 2018-06-18 10:28 UTC (permalink / raw)
  To: dri-devel
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, philippe.cornu,
	yannick.fertre, linux-rockchip, nickey.yang, robh+dt,
	thierry.reding, laurent.pinchart, mka

From: Nickey Yang <nickey.yang@rock-chips.com>

Add the ROCKCHIP DSI controller driver that uses the Synopsys DesignWare
MIPI DSI host controller bridge and remove the old separate one.

changes:

v2:
   add err_pllref, remove unnecessary encoder.enable & disable
   correct spelling mistakes
v3:
   call dw_mipi_dsi_unbind() in dw_mipi_dsi_rockchip_unbind()
   fix typo, use of_device_get_match_data(),
   change some bind() logic into probe()
   add 'dev_set_drvdata()'
v4:
  return -EINVAL when can not get best_freq
  add a clarifying comment when get vco
  add review tag
v5:
  keep our power domain enabled while touching GRF
v6:
  change func name dw_mipi_encoder_disable to
  dw_mipi_dsi_encoder_disable
v7:
  none
v8: Heiko
  add Archit's Review tag
  adapt to recent changes in the original rockchip-dsi driver
  beautify grf-handling
  split hw-setup (resources, dsi-host) from bind into probe
v2-new: Heiko
  add SPDX header instead of license blurb
  drop old versioning to not confuse people


Signed-off-by: Nickey Yang <nickey.yang@rock-chips.com>
Signed-off-by: Brian Norris <briannorris@chromium.org>
Reviewed-by: Brian Norris <briannorris@chromium.org>
Reviewed-by: Sean Paul <seanpaul@chromium.org>
Reviewed-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 drivers/gpu/drm/rockchip/Kconfig              |    2 +-
 drivers/gpu/drm/rockchip/Makefile             |    2 +-
 .../gpu/drm/rockchip/dw-mipi-dsi-rockchip.c   |  927 +++++++++++
 drivers/gpu/drm/rockchip/dw-mipi-dsi.c        | 1349 -----------------
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c   |    2 +-
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h   |    2 +-
 6 files changed, 931 insertions(+), 1353 deletions(-)
 create mode 100644 drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
 delete mode 100644 drivers/gpu/drm/rockchip/dw-mipi-dsi.c

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 0ccc76217ee4..9eb4795596d3 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -7,7 +7,7 @@ config DRM_ROCKCHIP
 	select VIDEOMODE_HELPERS
 	select DRM_ANALOGIX_DP if ROCKCHIP_ANALOGIX_DP
 	select DRM_DW_HDMI if ROCKCHIP_DW_HDMI
-	select DRM_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI
+	select DRM_DW_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI
 	select SND_SOC_HDMI_CODEC if ROCKCHIP_CDN_DP && SND_SOC
 	help
 	  Choose this option if you have a Rockchip soc chipset.
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index a314e2109e76..0f22dad1c996 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -11,7 +11,7 @@ rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
 rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
 rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
 rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
-rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
+rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi-rockchip.o
 rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
 rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o
 
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
new file mode 100644
index 000000000000..12e4dacc7970
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
@@ -0,0 +1,927 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author:
+ *      Chris Zhong <zyw@rock-chips.com>
+ *      Nickey Yang <nickey.yang@rock-chips.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <drm/drmP.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/bridge/dw_mipi_dsi.h>
+#include <video/mipi_display.h>
+#include <linux/regmap.h>
+#include <drm/drm_of.h>
+#include <linux/mfd/syscon.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_vop.h"
+
+#define DSI_PHY_RSTZ			0xa0
+#define PHY_DISFORCEPLL			0
+#define PHY_ENFORCEPLL			BIT(3)
+#define PHY_DISABLECLK			0
+#define PHY_ENABLECLK			BIT(2)
+#define PHY_RSTZ			0
+#define PHY_UNRSTZ			BIT(1)
+#define PHY_SHUTDOWNZ			0
+#define PHY_UNSHUTDOWNZ			BIT(0)
+
+#define DSI_PHY_IF_CFG			0xa4
+#define N_LANES(n)			((((n) - 1) & 0x3) << 0)
+#define PHY_STOP_WAIT_TIME(cycle)	(((cycle) & 0xff) << 8)
+
+#define DSI_PHY_STATUS			0xb0
+#define LOCK				BIT(0)
+#define STOP_STATE_CLK_LANE		BIT(2)
+
+#define DSI_PHY_TST_CTRL0		0xb4
+#define PHY_TESTCLK			BIT(1)
+#define PHY_UNTESTCLK			0
+#define PHY_TESTCLR			BIT(0)
+#define PHY_UNTESTCLR			0
+
+#define DSI_PHY_TST_CTRL1		0xb8
+#define PHY_TESTEN			BIT(16)
+#define PHY_UNTESTEN			0
+#define PHY_TESTDOUT(n)			(((n) & 0xff) << 8)
+#define PHY_TESTDIN(n)			(((n) & 0xff) << 0)
+
+#define DSI_INT_ST0			0xbc
+#define DSI_INT_ST1			0xc0
+#define DSI_INT_MSK0			0xc4
+#define DSI_INT_MSK1			0xc8
+
+#define PHY_STATUS_TIMEOUT_US		10000
+#define CMD_PKT_STATUS_TIMEOUT_US	20000
+
+#define BYPASS_VCO_RANGE	BIT(7)
+#define VCO_RANGE_CON_SEL(val)	(((val) & 0x7) << 3)
+#define VCO_IN_CAP_CON_DEFAULT	(0x0 << 1)
+#define VCO_IN_CAP_CON_LOW	(0x1 << 1)
+#define VCO_IN_CAP_CON_HIGH	(0x2 << 1)
+#define REF_BIAS_CUR_SEL	BIT(0)
+
+#define CP_CURRENT_3UA	0x1
+#define CP_CURRENT_4_5UA	0x2
+#define CP_CURRENT_7_5UA	0x6
+#define CP_CURRENT_6UA	0x9
+#define CP_CURRENT_12UA	0xb
+#define CP_CURRENT_SEL(val)	((val) & 0xf)
+#define CP_PROGRAM_EN		BIT(7)
+
+#define LPF_RESISTORS_15_5KOHM	0x1
+#define LPF_RESISTORS_13KOHM	0x2
+#define LPF_RESISTORS_11_5KOHM	0x4
+#define LPF_RESISTORS_10_5KOHM	0x8
+#define LPF_RESISTORS_8KOHM	0x10
+#define LPF_PROGRAM_EN		BIT(6)
+#define LPF_RESISTORS_SEL(val)	((val) & 0x3f)
+
+#define HSFREQRANGE_SEL(val)	(((val) & 0x3f) << 1)
+
+#define INPUT_DIVIDER(val)	(((val) - 1) & 0x7f)
+#define LOW_PROGRAM_EN		0
+#define HIGH_PROGRAM_EN		BIT(7)
+#define LOOP_DIV_LOW_SEL(val)	(((val) - 1) & 0x1f)
+#define LOOP_DIV_HIGH_SEL(val)	((((val) - 1) >> 5) & 0xf)
+#define PLL_LOOP_DIV_EN		BIT(5)
+#define PLL_INPUT_DIV_EN	BIT(4)
+
+#define POWER_CONTROL		BIT(6)
+#define INTERNAL_REG_CURRENT	BIT(3)
+#define BIAS_BLOCK_ON		BIT(2)
+#define BANDGAP_ON		BIT(0)
+
+#define TER_RESISTOR_HIGH	BIT(7)
+#define	TER_RESISTOR_LOW	0
+#define LEVEL_SHIFTERS_ON	BIT(6)
+#define TER_CAL_DONE		BIT(5)
+#define SETRD_MAX		(0x7 << 2)
+#define POWER_MANAGE		BIT(1)
+#define TER_RESISTORS_ON	BIT(0)
+
+#define BIASEXTR_SEL(val)	((val) & 0x7)
+#define BANDGAP_SEL(val)	((val) & 0x7)
+#define TLP_PROGRAM_EN		BIT(7)
+#define THS_PRE_PROGRAM_EN	BIT(7)
+#define THS_ZERO_PROGRAM_EN	BIT(6)
+
+#define PLL_BIAS_CUR_SEL_CAP_VCO_CONTROL		0x10
+#define PLL_CP_CONTROL_PLL_LOCK_BYPASS			0x11
+#define PLL_LPF_AND_CP_CONTROL				0x12
+#define PLL_INPUT_DIVIDER_RATIO				0x17
+#define PLL_LOOP_DIVIDER_RATIO				0x18
+#define PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL	0x19
+#define BANDGAP_AND_BIAS_CONTROL			0x20
+#define TERMINATION_RESISTER_CONTROL			0x21
+#define AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY		0x22
+#define HS_RX_CONTROL_OF_LANE_0				0x44
+#define HS_TX_CLOCK_LANE_REQUEST_STATE_TIME_CONTROL	0x60
+#define HS_TX_CLOCK_LANE_PREPARE_STATE_TIME_CONTROL	0x61
+#define HS_TX_CLOCK_LANE_HS_ZERO_STATE_TIME_CONTROL	0x62
+#define HS_TX_CLOCK_LANE_TRAIL_STATE_TIME_CONTROL	0x63
+#define HS_TX_CLOCK_LANE_EXIT_STATE_TIME_CONTROL	0x64
+#define HS_TX_CLOCK_LANE_POST_TIME_CONTROL		0x65
+#define HS_TX_DATA_LANE_REQUEST_STATE_TIME_CONTROL	0x70
+#define HS_TX_DATA_LANE_PREPARE_STATE_TIME_CONTROL	0x71
+#define HS_TX_DATA_LANE_HS_ZERO_STATE_TIME_CONTROL	0x72
+#define HS_TX_DATA_LANE_TRAIL_STATE_TIME_CONTROL	0x73
+#define HS_TX_DATA_LANE_EXIT_STATE_TIME_CONTROL		0x74
+
+#define DW_MIPI_NEEDS_PHY_CFG_CLK	BIT(0)
+#define DW_MIPI_NEEDS_GRF_CLK		BIT(1)
+
+#define RK3288_GRF_SOC_CON6		0x025c
+#define RK3288_DSI0_LCDC_SEL		BIT(6)
+#define RK3288_DSI1_LCDC_SEL		BIT(9)
+
+#define RK3399_GRF_SOC_CON20		0x6250
+#define RK3399_DSI0_LCDC_SEL		BIT(0)
+#define RK3399_DSI1_LCDC_SEL		BIT(4)
+
+#define RK3399_GRF_SOC_CON22		0x6258
+#define RK3399_DSI0_TURNREQUEST		(0xf << 12)
+#define RK3399_DSI0_TURNDISABLE		(0xf << 8)
+#define RK3399_DSI0_FORCETXSTOPMODE	(0xf << 4)
+#define RK3399_DSI0_FORCERXMODE		(0xf << 0)
+
+#define RK3399_GRF_SOC_CON23		0x625c
+#define RK3399_DSI1_TURNDISABLE		(0xf << 12)
+#define RK3399_DSI1_FORCETXSTOPMODE	(0xf << 8)
+#define RK3399_DSI1_FORCERXMODE		(0xf << 4)
+#define RK3399_DSI1_ENABLE		(0xf << 0)
+
+#define RK3399_GRF_SOC_CON24		0x6260
+#define RK3399_TXRX_MASTERSLAVEZ	BIT(7)
+#define RK3399_TXRX_ENABLECLK		BIT(6)
+#define RK3399_TXRX_BASEDIR		BIT(5)
+
+#define HIWORD_UPDATE(val, mask)	(val | (mask) << 16)
+
+#define to_dsi(nm)	container_of(nm, struct dw_mipi_dsi_rockchip, nm)
+
+enum {
+	BANDGAP_97_07,
+	BANDGAP_98_05,
+	BANDGAP_99_02,
+	BANDGAP_100_00,
+	BANDGAP_93_17,
+	BANDGAP_94_15,
+	BANDGAP_95_12,
+	BANDGAP_96_10,
+};
+
+enum {
+	BIASEXTR_87_1,
+	BIASEXTR_91_5,
+	BIASEXTR_95_9,
+	BIASEXTR_100,
+	BIASEXTR_105_94,
+	BIASEXTR_111_88,
+	BIASEXTR_118_8,
+	BIASEXTR_127_7,
+};
+
+struct rockchip_dw_dsi_chip_data {
+	u32 reg;
+
+	u32 lcdsel_grf_reg;
+	u32 lcdsel_big;
+	u32 lcdsel_lit;
+
+	u32 enable_grf_reg;
+	u32 enable;
+
+	u32 lanecfg1_grf_reg;
+	u32 lanecfg1;
+	u32 lanecfg2_grf_reg;
+	u32 lanecfg2;
+
+	unsigned int flags;
+	unsigned int max_data_lanes;
+};
+
+struct dw_mipi_dsi_rockchip {
+	struct device *dev;
+	struct drm_encoder encoder;
+	void __iomem *base;
+
+	struct regmap *grf_regmap;
+	struct clk *pllref_clk;
+	struct clk *grf_clk;
+	struct clk *phy_cfg_clk;
+
+	unsigned int lane_mbps; /* per lane */
+	u16 input_div;
+	u16 feedback_div;
+	u32 format;
+
+	struct dw_mipi_dsi *dmd;
+	const struct rockchip_dw_dsi_chip_data *cdata;
+	struct dw_mipi_dsi_plat_data pdata;
+};
+
+struct dphy_pll_parameter_map {
+	unsigned int max_mbps;
+	u8 hsfreqrange;
+	u8 icpctrl;
+	u8 lpfctrl;
+};
+
+/* The table is based on 27MHz DPHY pll reference clock. */
+static const struct dphy_pll_parameter_map dppa_map[] = {
+	{  89, 0x00, CP_CURRENT_3UA, LPF_RESISTORS_13KOHM },
+	{  99, 0x10, CP_CURRENT_3UA, LPF_RESISTORS_13KOHM },
+	{ 109, 0x20, CP_CURRENT_3UA, LPF_RESISTORS_13KOHM },
+	{ 129, 0x01, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM },
+	{ 139, 0x11, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM },
+	{ 149, 0x21, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM },
+	{ 169, 0x02, CP_CURRENT_6UA, LPF_RESISTORS_13KOHM },
+	{ 179, 0x12, CP_CURRENT_6UA, LPF_RESISTORS_13KOHM },
+	{ 199, 0x22, CP_CURRENT_6UA, LPF_RESISTORS_13KOHM },
+	{ 219, 0x03, CP_CURRENT_4_5UA, LPF_RESISTORS_13KOHM },
+	{ 239, 0x13, CP_CURRENT_4_5UA, LPF_RESISTORS_13KOHM },
+	{ 249, 0x23, CP_CURRENT_4_5UA, LPF_RESISTORS_13KOHM },
+	{ 269, 0x04, CP_CURRENT_6UA, LPF_RESISTORS_11_5KOHM },
+	{ 299, 0x14, CP_CURRENT_6UA, LPF_RESISTORS_11_5KOHM },
+	{ 329, 0x05, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM },
+	{ 359, 0x15, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM },
+	{ 399, 0x25, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM },
+	{ 449, 0x06, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
+	{ 499, 0x16, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
+	{ 549, 0x07, CP_CURRENT_7_5UA, LPF_RESISTORS_10_5KOHM },
+	{ 599, 0x17, CP_CURRENT_7_5UA, LPF_RESISTORS_10_5KOHM },
+	{ 649, 0x08, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
+	{ 699, 0x18, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
+	{ 749, 0x09, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
+	{ 799, 0x19, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
+	{ 849, 0x29, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
+	{ 899, 0x39, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
+	{ 949, 0x0a, CP_CURRENT_12UA, LPF_RESISTORS_8KOHM },
+	{ 999, 0x1a, CP_CURRENT_12UA, LPF_RESISTORS_8KOHM },
+	{1049, 0x2a, CP_CURRENT_12UA, LPF_RESISTORS_8KOHM },
+	{1099, 0x3a, CP_CURRENT_12UA, LPF_RESISTORS_8KOHM },
+	{1149, 0x0b, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM },
+	{1199, 0x1b, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM },
+	{1249, 0x2b, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM },
+	{1299, 0x3b, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM },
+	{1349, 0x0c, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM },
+	{1399, 0x1c, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM },
+	{1449, 0x2c, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM },
+	{1500, 0x3c, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM }
+};
+
+static int max_mbps_to_parameter(unsigned int max_mbps)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(dppa_map); i++)
+		if (dppa_map[i].max_mbps >= max_mbps)
+			return i;
+
+	return -EINVAL;
+}
+
+static inline void dsi_write(struct dw_mipi_dsi_rockchip *dsi, u32 reg, u32 val)
+{
+	writel(val, dsi->base + reg);
+}
+
+static inline u32 dsi_read(struct dw_mipi_dsi_rockchip *dsi, u32 reg)
+{
+	return readl(dsi->base + reg);
+}
+
+static inline void dsi_set(struct dw_mipi_dsi_rockchip *dsi, u32 reg, u32 mask)
+{
+	dsi_write(dsi, reg, dsi_read(dsi, reg) | mask);
+}
+
+static inline void dsi_update_bits(struct dw_mipi_dsi_rockchip *dsi, u32 reg,
+				   u32 mask, u32 val)
+{
+	dsi_write(dsi, reg, (dsi_read(dsi, reg) & ~mask) | val);
+}
+
+static void dw_mipi_dsi_phy_write(struct dw_mipi_dsi_rockchip *dsi,
+				  u8 test_code,
+				  u8 test_data)
+{
+	/*
+	 * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
+	 * is latched internally as the current test code. Test data is
+	 * programmed internally by rising edge on TESTCLK.
+	 */
+	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
+
+	dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_TESTEN | PHY_TESTDOUT(0) |
+					  PHY_TESTDIN(test_code));
+
+	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR);
+
+	dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_UNTESTEN | PHY_TESTDOUT(0) |
+					  PHY_TESTDIN(test_data));
+
+	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
+}
+
+/**
+ * ns2bc - Nanoseconds to byte clock cycles
+ */
+static inline unsigned int ns2bc(struct dw_mipi_dsi_rockchip *dsi, int ns)
+{
+	return DIV_ROUND_UP(ns * dsi->lane_mbps / 8, 1000);
+}
+
+/**
+ * ns2ui - Nanoseconds to UI time periods
+ */
+static inline unsigned int ns2ui(struct dw_mipi_dsi_rockchip *dsi, int ns)
+{
+	return DIV_ROUND_UP(ns * dsi->lane_mbps, 1000);
+}
+
+static int dw_mipi_dsi_phy_init(void *priv_data)
+{
+	struct dw_mipi_dsi_rockchip *dsi = priv_data;
+	int ret, i, vco;
+
+	/*
+	 * Get vco from frequency(lane_mbps)
+	 * vco	frequency table
+	 * 000 - between   80 and  200 MHz
+	 * 001 - between  200 and  300 MHz
+	 * 010 - between  300 and  500 MHz
+	 * 011 - between  500 and  700 MHz
+	 * 100 - between  700 and  900 MHz
+	 * 101 - between  900 and 1100 MHz
+	 * 110 - between 1100 and 1300 MHz
+	 * 111 - between 1300 and 1500 MHz
+	 */
+	vco = (dsi->lane_mbps < 200) ? 0 : (dsi->lane_mbps + 100) / 200;
+
+	i = max_mbps_to_parameter(dsi->lane_mbps);
+	if (i < 0) {
+		DRM_DEV_ERROR(dsi->dev,
+			      "failed to get parameter for %dmbps clock\n",
+			      dsi->lane_mbps);
+		return i;
+	}
+
+	ret = clk_prepare_enable(dsi->phy_cfg_clk);
+	if (ret) {
+		DRM_DEV_ERROR(dsi->dev, "Failed to enable phy_cfg_clk\n");
+		return ret;
+	}
+
+	dw_mipi_dsi_phy_write(dsi, PLL_BIAS_CUR_SEL_CAP_VCO_CONTROL,
+			      BYPASS_VCO_RANGE |
+			      VCO_RANGE_CON_SEL(vco) |
+			      VCO_IN_CAP_CON_LOW |
+			      REF_BIAS_CUR_SEL);
+
+	dw_mipi_dsi_phy_write(dsi, PLL_CP_CONTROL_PLL_LOCK_BYPASS,
+			      CP_CURRENT_SEL(dppa_map[i].icpctrl));
+	dw_mipi_dsi_phy_write(dsi, PLL_LPF_AND_CP_CONTROL,
+			      CP_PROGRAM_EN | LPF_PROGRAM_EN |
+			      LPF_RESISTORS_SEL(dppa_map[i].lpfctrl));
+
+	dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_0,
+			      HSFREQRANGE_SEL(dppa_map[i].hsfreqrange));
+
+	dw_mipi_dsi_phy_write(dsi, PLL_INPUT_DIVIDER_RATIO,
+			      INPUT_DIVIDER(dsi->input_div));
+	dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO,
+			      LOOP_DIV_LOW_SEL(dsi->feedback_div) |
+			      LOW_PROGRAM_EN);
+	/*
+	 * We need set PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL immediately
+	 * to make the configured LSB effective according to IP simulation
+	 * and lab test results.
+	 * Only in this way can we get correct mipi phy pll frequency.
+	 */
+	dw_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL,
+			      PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN);
+	dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO,
+			      LOOP_DIV_HIGH_SEL(dsi->feedback_div) |
+			      HIGH_PROGRAM_EN);
+	dw_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL,
+			      PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN);
+
+	dw_mipi_dsi_phy_write(dsi, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY,
+			      LOW_PROGRAM_EN | BIASEXTR_SEL(BIASEXTR_127_7));
+	dw_mipi_dsi_phy_write(dsi, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY,
+			      HIGH_PROGRAM_EN | BANDGAP_SEL(BANDGAP_96_10));
+
+	dw_mipi_dsi_phy_write(dsi, BANDGAP_AND_BIAS_CONTROL,
+			      POWER_CONTROL | INTERNAL_REG_CURRENT |
+			      BIAS_BLOCK_ON | BANDGAP_ON);
+
+	dw_mipi_dsi_phy_write(dsi, TERMINATION_RESISTER_CONTROL,
+			      TER_RESISTOR_LOW | TER_CAL_DONE |
+			      SETRD_MAX | TER_RESISTORS_ON);
+	dw_mipi_dsi_phy_write(dsi, TERMINATION_RESISTER_CONTROL,
+			      TER_RESISTOR_HIGH | LEVEL_SHIFTERS_ON |
+			      SETRD_MAX | POWER_MANAGE |
+			      TER_RESISTORS_ON);
+
+	dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_REQUEST_STATE_TIME_CONTROL,
+			      TLP_PROGRAM_EN | ns2bc(dsi, 500));
+	dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_PREPARE_STATE_TIME_CONTROL,
+			      THS_PRE_PROGRAM_EN | ns2ui(dsi, 40));
+	dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_HS_ZERO_STATE_TIME_CONTROL,
+			      THS_ZERO_PROGRAM_EN | ns2bc(dsi, 300));
+	dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_TRAIL_STATE_TIME_CONTROL,
+			      THS_PRE_PROGRAM_EN | ns2ui(dsi, 100));
+	dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_EXIT_STATE_TIME_CONTROL,
+			      BIT(5) | ns2bc(dsi, 100));
+	dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_POST_TIME_CONTROL,
+			      BIT(5) | (ns2bc(dsi, 60) + 7));
+
+	dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_REQUEST_STATE_TIME_CONTROL,
+			      TLP_PROGRAM_EN | ns2bc(dsi, 500));
+	dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_PREPARE_STATE_TIME_CONTROL,
+			      THS_PRE_PROGRAM_EN | (ns2ui(dsi, 50) + 20));
+	dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_HS_ZERO_STATE_TIME_CONTROL,
+			      THS_ZERO_PROGRAM_EN | (ns2bc(dsi, 140) + 2));
+	dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_TRAIL_STATE_TIME_CONTROL,
+			      THS_PRE_PROGRAM_EN | (ns2ui(dsi, 60) + 8));
+	dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_EXIT_STATE_TIME_CONTROL,
+			      BIT(5) | ns2bc(dsi, 100));
+
+	clk_disable_unprepare(dsi->phy_cfg_clk);
+
+	return ret;
+}
+
+static int
+dw_mipi_dsi_get_lane_mbps(void *priv_data, struct drm_display_mode *mode,
+			  unsigned long mode_flags, u32 lanes, u32 format,
+			  unsigned int *lane_mbps)
+{
+	struct dw_mipi_dsi_rockchip *dsi = priv_data;
+	int bpp;
+	unsigned long mpclk, tmp;
+	unsigned int target_mbps = 1000;
+	unsigned int max_mbps = dppa_map[ARRAY_SIZE(dppa_map) - 1].max_mbps;
+	unsigned long best_freq = 0;
+	unsigned long fvco_min, fvco_max, fin, fout;
+	unsigned int min_prediv, max_prediv;
+	unsigned int _prediv, uninitialized_var(best_prediv);
+	unsigned long _fbdiv, uninitialized_var(best_fbdiv);
+	unsigned long min_delta = ULONG_MAX;
+
+	dsi->format = format;
+	bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
+	if (bpp < 0) {
+		DRM_DEV_ERROR(dsi->dev,
+			      "failed to get bpp for pixel format %d\n",
+			      dsi->format);
+		return bpp;
+	}
+
+	mpclk = DIV_ROUND_UP(mode->clock, MSEC_PER_SEC);
+	if (mpclk) {
+		/* take 1 / 0.8, since mbps must big than bandwidth of RGB */
+		tmp = mpclk * (bpp / lanes) * 10 / 8;
+		if (tmp < max_mbps)
+			target_mbps = tmp;
+		else
+			DRM_DEV_ERROR(dsi->dev,
+				      "DPHY clock frequency is out of range\n");
+	}
+
+	fin = clk_get_rate(dsi->pllref_clk);
+	fout = target_mbps * USEC_PER_SEC;
+
+	/* constraint: 5Mhz <= Fref / N <= 40MHz */
+	min_prediv = DIV_ROUND_UP(fin, 40 * USEC_PER_SEC);
+	max_prediv = fin / (5 * USEC_PER_SEC);
+
+	/* constraint: 80MHz <= Fvco <= 1500Mhz */
+	fvco_min = 80 * USEC_PER_SEC;
+	fvco_max = 1500 * USEC_PER_SEC;
+
+	for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) {
+		u64 tmp;
+		u32 delta;
+		/* Fvco = Fref * M / N */
+		tmp = (u64)fout * _prediv;
+		do_div(tmp, fin);
+		_fbdiv = tmp;
+		/*
+		 * Due to the use of a "by 2 pre-scaler," the range of the
+		 * feedback multiplication value M is limited to even division
+		 * numbers, and m must be greater than 6, not bigger than 512.
+		 */
+		if (_fbdiv < 6 || _fbdiv > 512)
+			continue;
+
+		_fbdiv += _fbdiv % 2;
+
+		tmp = (u64)_fbdiv * fin;
+		do_div(tmp, _prediv);
+		if (tmp < fvco_min || tmp > fvco_max)
+			continue;
+
+		delta = abs(fout - tmp);
+		if (delta < min_delta) {
+			best_prediv = _prediv;
+			best_fbdiv = _fbdiv;
+			min_delta = delta;
+			best_freq = tmp;
+		}
+	}
+
+	if (best_freq) {
+		dsi->lane_mbps = DIV_ROUND_UP(best_freq, USEC_PER_SEC);
+		*lane_mbps = dsi->lane_mbps;
+		dsi->input_div = best_prediv;
+		dsi->feedback_div = best_fbdiv;
+	} else {
+		DRM_DEV_ERROR(dsi->dev, "Can not find best_freq for DPHY\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct dw_mipi_dsi_phy_ops dw_mipi_dsi_rockchip_phy_ops = {
+	.init = dw_mipi_dsi_phy_init,
+	.get_lane_mbps = dw_mipi_dsi_get_lane_mbps,
+};
+
+static void dw_mipi_dsi_rockchip_config(struct dw_mipi_dsi_rockchip *dsi,
+					int mux)
+{
+	pm_runtime_get_sync(dsi->dev);
+
+	if (dsi->cdata->lcdsel_grf_reg)
+		regmap_write(dsi->grf_regmap, dsi->cdata->lcdsel_grf_reg,
+			mux ? dsi->cdata->lcdsel_lit : dsi->cdata->lcdsel_big);
+
+	if (dsi->cdata->lanecfg1_grf_reg)
+		regmap_write(dsi->grf_regmap, dsi->cdata->lanecfg1_grf_reg,
+					      dsi->cdata->lanecfg1);
+
+	if (dsi->cdata->lanecfg2_grf_reg)
+		regmap_write(dsi->grf_regmap, dsi->cdata->lanecfg2_grf_reg,
+					      dsi->cdata->lanecfg2);
+
+	if (dsi->cdata->enable_grf_reg)
+		regmap_write(dsi->grf_regmap, dsi->cdata->enable_grf_reg,
+					      dsi->cdata->enable);
+}
+
+static void dw_mipi_dsi_encoder_mode_set(struct drm_encoder *encoder,
+					 struct drm_display_mode *mode,
+					 struct drm_display_mode *adjusted)
+{
+	struct dw_mipi_dsi_rockchip *dsi = to_dsi(encoder);
+	int ret, mux;
+
+	mux = drm_of_encoder_active_endpoint_id(dsi->dev->of_node,
+						&dsi->encoder);
+	if (mux < 0)
+		return;
+
+	/*
+	 * For the RK3399, the clk of grf must be enabled before writing grf
+	 * register. And for RK3288 or other soc, this grf_clk must be NULL,
+	 * the clk_prepare_enable return true directly.
+	 */
+	ret = clk_prepare_enable(dsi->grf_clk);
+	if (ret) {
+		DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret);
+		return;
+	}
+
+	dw_mipi_dsi_rockchip_config(dsi, mux);
+
+	clk_disable_unprepare(dsi->grf_clk);
+}
+
+static int
+dw_mipi_dsi_encoder_atomic_check(struct drm_encoder *encoder,
+				 struct drm_crtc_state *crtc_state,
+				 struct drm_connector_state *conn_state)
+{
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+	struct dw_mipi_dsi_rockchip *dsi = to_dsi(encoder);
+
+	switch (dsi->format) {
+	case MIPI_DSI_FMT_RGB888:
+		s->output_mode = ROCKCHIP_OUT_MODE_P888;
+		break;
+	case MIPI_DSI_FMT_RGB666:
+		s->output_mode = ROCKCHIP_OUT_MODE_P666;
+		break;
+	case MIPI_DSI_FMT_RGB565:
+		s->output_mode = ROCKCHIP_OUT_MODE_P565;
+		break;
+	default:
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	s->output_type = DRM_MODE_CONNECTOR_DSI;
+
+	return 0;
+}
+
+static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct dw_mipi_dsi_rockchip *dsi = to_dsi(encoder);
+
+	pm_runtime_put(dsi->dev);
+}
+
+static const struct drm_encoder_helper_funcs
+dw_mipi_dsi_encoder_helper_funcs = {
+	.mode_set = dw_mipi_dsi_encoder_mode_set,
+	.atomic_check = dw_mipi_dsi_encoder_atomic_check,
+	.disable = dw_mipi_dsi_encoder_disable,
+};
+
+static const struct drm_encoder_funcs dw_mipi_dsi_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static int rockchip_dsi_drm_create_encoder(struct dw_mipi_dsi_rockchip *dsi,
+					   struct drm_device *drm_dev)
+{
+	struct drm_encoder *encoder = &dsi->encoder;
+	int ret;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+							     dsi->dev->of_node);
+
+	ret = drm_encoder_init(drm_dev, encoder, &dw_mipi_dsi_encoder_funcs,
+			       DRM_MODE_ENCODER_DSI, NULL);
+	if (ret) {
+		DRM_ERROR("Failed to initialize encoder with drm\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &dw_mipi_dsi_encoder_helper_funcs);
+
+	return 0;
+}
+
+static int dw_mipi_dsi_rockchip_bind(struct device *dev,
+				     struct device *master,
+				     void *data)
+{
+	struct dw_mipi_dsi_rockchip *dsi = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
+	struct drm_bridge *bridge;
+	struct drm_panel *panel;
+	int ret;
+
+	/*
+	 * Handle probe-deferrals due to missing display.
+	 */
+	ret = drm_of_find_panel_or_bridge(dsi->dev->of_node, 1, 0,
+					  &panel, &bridge);
+	if (ret)
+		return ret;
+
+	ret = rockchip_dsi_drm_create_encoder(dsi, drm_dev);
+	if (ret) {
+		DRM_DEV_ERROR(dev, "Failed to create drm encoder\n");
+		return ret;
+	}
+
+	ret = dw_mipi_dsi_bind(dsi->dmd, &dsi->encoder);
+	if (ret) {
+		DRM_DEV_ERROR(dev, "Failed to bind: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void dw_mipi_dsi_rockchip_unbind(struct device *dev,
+					struct device *master,
+					void *data)
+{
+	struct dw_mipi_dsi_rockchip *dsi = dev_get_drvdata(dev);
+
+	dw_mipi_dsi_unbind(dsi->dmd);
+}
+
+static const struct component_ops dw_mipi_dsi_rockchip_ops = {
+	.bind	= dw_mipi_dsi_rockchip_bind,
+	.unbind	= dw_mipi_dsi_rockchip_unbind,
+};
+
+static int dw_mipi_dsi_rockchip_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct dw_mipi_dsi_rockchip *dsi;
+	struct resource *res;
+	const struct rockchip_dw_dsi_chip_data *cdata =
+				of_device_get_match_data(dev);
+	int ret, i;
+
+	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
+	if (!dsi)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	dsi->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(dsi->base)) {
+		DRM_DEV_ERROR(dev, "Unable to get dsi registers\n");
+		return PTR_ERR(dsi->base);
+	}
+
+	i = 0;
+	while (cdata[i].reg) {
+		if (cdata[i].reg == res->start) {
+			dsi->cdata = &cdata[i];
+			break;
+		}
+
+		i++;
+	}
+
+	if (!dsi->cdata) {
+		dev_err(dev, "no dsi-config for %s node\n", np->name);
+		return -EINVAL;
+	}
+
+	dsi->pllref_clk = devm_clk_get(dev, "ref");
+	if (IS_ERR(dsi->pllref_clk)) {
+		ret = PTR_ERR(dsi->pllref_clk);
+		DRM_DEV_ERROR(dev,
+			      "Unable to get pll reference clock: %d\n", ret);
+		return ret;
+	}
+
+	if (dsi->cdata->flags & DW_MIPI_NEEDS_PHY_CFG_CLK) {
+		dsi->phy_cfg_clk = devm_clk_get(dev, "phy_cfg");
+		if (IS_ERR(dsi->phy_cfg_clk)) {
+			ret = PTR_ERR(dsi->phy_cfg_clk);
+			DRM_DEV_ERROR(dev,
+				      "Unable to get phy_cfg_clk: %d\n", ret);
+			return ret;
+		}
+	}
+
+	if (dsi->cdata->flags & DW_MIPI_NEEDS_GRF_CLK) {
+		dsi->grf_clk = devm_clk_get(dev, "grf");
+		if (IS_ERR(dsi->grf_clk)) {
+			ret = PTR_ERR(dsi->grf_clk);
+			DRM_DEV_ERROR(dev, "Unable to get grf_clk: %d\n", ret);
+			return ret;
+		}
+	}
+
+	dsi->grf_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+	if (IS_ERR(dsi->grf_regmap)) {
+		DRM_DEV_ERROR(dsi->dev, "Unable to get rockchip,grf\n");
+		return PTR_ERR(dsi->grf_regmap);
+	}
+
+	dsi->dev = dev;
+	dsi->pdata.base = dsi->base;
+	dsi->pdata.max_data_lanes = dsi->cdata->max_data_lanes;
+	dsi->pdata.phy_ops = &dw_mipi_dsi_rockchip_phy_ops;
+	dsi->pdata.priv_data = dsi;
+	platform_set_drvdata(pdev, dsi);
+
+	ret = clk_prepare_enable(dsi->pllref_clk);
+	if (ret) {
+		DRM_DEV_ERROR(dev, "Failed to enable pllref_clk: %d\n", ret);
+		return ret;
+	}
+
+	dsi->dmd = dw_mipi_dsi_probe(pdev, &dsi->pdata);
+	if (IS_ERR(dsi->dmd)) {
+		ret = PTR_ERR(dsi->dmd);
+		if (ret != -EPROBE_DEFER)
+			DRM_DEV_ERROR(dev,
+				      "Failed to probe dw_mipi_dsi: %d\n", ret);
+		goto err_clkdisable;
+	}
+
+	ret = component_add(&pdev->dev, &dw_mipi_dsi_rockchip_ops);
+	if (ret) {
+		DRM_DEV_ERROR(dev, "Failed to register component: %d\n", ret);
+		goto err_dsiprobe;
+	}
+
+	return 0;
+
+err_dsiprobe:
+	dw_mipi_dsi_remove(dsi->dmd);
+err_clkdisable:
+	clk_disable_unprepare(dsi->pllref_clk);
+	return ret;
+}
+
+static int dw_mipi_dsi_rockchip_remove(struct platform_device *pdev)
+{
+	struct dw_mipi_dsi_rockchip *dsi = platform_get_drvdata(pdev);
+
+	component_del(&pdev->dev, &dw_mipi_dsi_rockchip_ops);
+	dw_mipi_dsi_remove(dsi->dmd);
+	clk_disable_unprepare(dsi->pllref_clk);
+
+	return 0;
+}
+
+static const struct rockchip_dw_dsi_chip_data rk3288_chip_data[] = {
+	{
+		.reg = 0xff960000,
+		.lcdsel_grf_reg = RK3288_GRF_SOC_CON6,
+		.lcdsel_big = HIWORD_UPDATE(0, RK3288_DSI0_LCDC_SEL),
+		.lcdsel_lit = HIWORD_UPDATE(RK3288_DSI0_LCDC_SEL, RK3288_DSI0_LCDC_SEL),
+
+		.max_data_lanes = 4,
+	},
+	{
+		.reg = 0xff964000,
+		.lcdsel_grf_reg = RK3288_GRF_SOC_CON6,
+		.lcdsel_big = HIWORD_UPDATE(0, RK3288_DSI1_LCDC_SEL),
+		.lcdsel_lit = HIWORD_UPDATE(RK3288_DSI1_LCDC_SEL, RK3288_DSI1_LCDC_SEL),
+
+		.max_data_lanes = 4,
+	},
+	{ /* sentinel */ }
+};
+
+static const struct rockchip_dw_dsi_chip_data rk3399_chip_data[] = {
+	{
+		.reg = 0xff960000,
+		.lcdsel_grf_reg = RK3399_GRF_SOC_CON20,
+		.lcdsel_big = HIWORD_UPDATE(0, RK3399_DSI0_LCDC_SEL),
+		.lcdsel_lit = HIWORD_UPDATE(RK3399_DSI0_LCDC_SEL,
+					    RK3399_DSI0_LCDC_SEL),
+
+		.lanecfg1_grf_reg = RK3399_GRF_SOC_CON22,
+		.lanecfg1 = HIWORD_UPDATE(0, RK3399_DSI0_TURNREQUEST |
+					     RK3399_DSI0_TURNDISABLE |
+					     RK3399_DSI0_FORCETXSTOPMODE |
+					     RK3399_DSI0_FORCERXMODE),
+
+		.flags = DW_MIPI_NEEDS_PHY_CFG_CLK | DW_MIPI_NEEDS_GRF_CLK,
+		.max_data_lanes = 4,
+	},
+	{
+		.reg = 0xff968000,
+		.lcdsel_grf_reg = RK3399_GRF_SOC_CON20,
+		.lcdsel_big = HIWORD_UPDATE(0, RK3399_DSI1_LCDC_SEL),
+		.lcdsel_lit = HIWORD_UPDATE(RK3399_DSI1_LCDC_SEL,
+					    RK3399_DSI1_LCDC_SEL),
+
+		.lanecfg1_grf_reg = RK3399_GRF_SOC_CON23,
+		.lanecfg1 = HIWORD_UPDATE(0, RK3399_DSI1_TURNDISABLE |
+					     RK3399_DSI1_FORCETXSTOPMODE |
+					     RK3399_DSI1_FORCERXMODE |
+					     RK3399_DSI1_ENABLE),
+
+		.lanecfg2_grf_reg = RK3399_GRF_SOC_CON24,
+		.lanecfg2 = HIWORD_UPDATE(RK3399_TXRX_MASTERSLAVEZ |
+					  RK3399_TXRX_ENABLECLK,
+					  RK3399_TXRX_MASTERSLAVEZ |
+					  RK3399_TXRX_ENABLECLK |
+					  RK3399_TXRX_BASEDIR),
+
+		.enable_grf_reg = RK3399_GRF_SOC_CON23,
+		.enable = HIWORD_UPDATE(RK3399_DSI1_ENABLE, RK3399_DSI1_ENABLE),
+
+		.flags = DW_MIPI_NEEDS_PHY_CFG_CLK | DW_MIPI_NEEDS_GRF_CLK,
+		.max_data_lanes = 4,
+	},
+	{ /* sentinel */ }
+};
+
+static const struct of_device_id dw_mipi_dsi_rockchip_dt_ids[] = {
+	{
+	 .compatible = "rockchip,rk3288-mipi-dsi",
+	 .data = &rk3288_chip_data,
+	}, {
+	 .compatible = "rockchip,rk3399-mipi-dsi",
+	 .data = &rk3399_chip_data,
+	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dw_mipi_dsi_rockchip_dt_ids);
+
+struct platform_driver dw_mipi_dsi_rockchip_driver = {
+	.probe		= dw_mipi_dsi_rockchip_probe,
+	.remove		= dw_mipi_dsi_rockchip_remove,
+	.driver		= {
+		.of_match_table = dw_mipi_dsi_rockchip_dt_ids,
+		.name	= "dw-mipi-dsi-rockchip",
+	},
+};
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
deleted file mode 100644
index d53d5a09547f..000000000000
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
+++ /dev/null
@@ -1,1349 +0,0 @@
-/*
- * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-#include <linux/clk.h>
-#include <linux/component.h>
-#include <linux/iopoll.h>
-#include <linux/math64.h>
-#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/regmap.h>
-#include <linux/reset.h>
-#include <linux/mfd/syscon.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_crtc.h>
-#include <drm/drm_crtc_helper.h>
-#include <drm/drm_mipi_dsi.h>
-#include <drm/drm_of.h>
-#include <drm/drm_panel.h>
-#include <drm/drmP.h>
-#include <video/mipi_display.h>
-
-#include "rockchip_drm_drv.h"
-#include "rockchip_drm_vop.h"
-
-#define DRIVER_NAME    "dw-mipi-dsi"
-
-#define RK3288_GRF_SOC_CON6		0x025c
-#define RK3288_DSI0_SEL_VOP_LIT		BIT(6)
-#define RK3288_DSI1_SEL_VOP_LIT		BIT(9)
-
-#define RK3399_GRF_SOC_CON20		0x6250
-#define RK3399_DSI0_SEL_VOP_LIT		BIT(0)
-#define RK3399_DSI1_SEL_VOP_LIT		BIT(4)
-
-/* disable turnrequest, turndisable, forcetxstopmode, forcerxmode */
-#define RK3399_GRF_SOC_CON22		0x6258
-#define RK3399_GRF_DSI_MODE		0xffff0000
-
-#define DSI_VERSION			0x00
-#define DSI_PWR_UP			0x04
-#define RESET				0
-#define POWERUP				BIT(0)
-
-#define DSI_CLKMGR_CFG			0x08
-#define TO_CLK_DIVIDSION(div)		(((div) & 0xff) << 8)
-#define TX_ESC_CLK_DIVIDSION(div)	(((div) & 0xff) << 0)
-
-#define DSI_DPI_VCID			0x0c
-#define DPI_VID(vid)			(((vid) & 0x3) << 0)
-
-#define DSI_DPI_COLOR_CODING		0x10
-#define EN18_LOOSELY			BIT(8)
-#define DPI_COLOR_CODING_16BIT_1	0x0
-#define DPI_COLOR_CODING_16BIT_2	0x1
-#define DPI_COLOR_CODING_16BIT_3	0x2
-#define DPI_COLOR_CODING_18BIT_1	0x3
-#define DPI_COLOR_CODING_18BIT_2	0x4
-#define DPI_COLOR_CODING_24BIT		0x5
-
-#define DSI_DPI_CFG_POL			0x14
-#define COLORM_ACTIVE_LOW		BIT(4)
-#define SHUTD_ACTIVE_LOW		BIT(3)
-#define HSYNC_ACTIVE_LOW		BIT(2)
-#define VSYNC_ACTIVE_LOW		BIT(1)
-#define DATAEN_ACTIVE_LOW		BIT(0)
-
-#define DSI_DPI_LP_CMD_TIM		0x18
-#define OUTVACT_LPCMD_TIME(p)		(((p) & 0xff) << 16)
-#define INVACT_LPCMD_TIME(p)		((p) & 0xff)
-
-#define DSI_DBI_CFG			0x20
-#define DSI_DBI_CMDSIZE			0x28
-
-#define DSI_PCKHDL_CFG			0x2c
-#define EN_CRC_RX			BIT(4)
-#define EN_ECC_RX			BIT(3)
-#define EN_BTA				BIT(2)
-#define EN_EOTP_RX			BIT(1)
-#define EN_EOTP_TX			BIT(0)
-
-#define DSI_MODE_CFG			0x34
-#define ENABLE_VIDEO_MODE		0
-#define ENABLE_CMD_MODE			BIT(0)
-
-#define DSI_VID_MODE_CFG		0x38
-#define FRAME_BTA_ACK			BIT(14)
-#define ENABLE_LOW_POWER		(0x3f << 8)
-#define ENABLE_LOW_POWER_MASK		(0x3f << 8)
-#define VID_MODE_TYPE_NON_BURST_SYNC_PULSES	0x0
-#define VID_MODE_TYPE_NON_BURST_SYNC_EVENTS	0x1
-#define VID_MODE_TYPE_BURST			0x2
-#define VID_MODE_TYPE_MASK			0x3
-
-#define DSI_VID_PKT_SIZE		0x3c
-#define VID_PKT_SIZE(p)			(((p) & 0x3fff) << 0)
-#define VID_PKT_MAX_SIZE		0x3fff
-
-#define DSI_VID_HSA_TIME		0x48
-#define DSI_VID_HBP_TIME		0x4c
-#define DSI_VID_HLINE_TIME		0x50
-#define DSI_VID_VSA_LINES		0x54
-#define DSI_VID_VBP_LINES		0x58
-#define DSI_VID_VFP_LINES		0x5c
-#define DSI_VID_VACTIVE_LINES		0x60
-#define DSI_CMD_MODE_CFG		0x68
-#define MAX_RD_PKT_SIZE_LP		BIT(24)
-#define DCS_LW_TX_LP			BIT(19)
-#define DCS_SR_0P_TX_LP			BIT(18)
-#define DCS_SW_1P_TX_LP			BIT(17)
-#define DCS_SW_0P_TX_LP			BIT(16)
-#define GEN_LW_TX_LP			BIT(14)
-#define GEN_SR_2P_TX_LP			BIT(13)
-#define GEN_SR_1P_TX_LP			BIT(12)
-#define GEN_SR_0P_TX_LP			BIT(11)
-#define GEN_SW_2P_TX_LP			BIT(10)
-#define GEN_SW_1P_TX_LP			BIT(9)
-#define GEN_SW_0P_TX_LP			BIT(8)
-#define EN_ACK_RQST			BIT(1)
-#define EN_TEAR_FX			BIT(0)
-
-#define CMD_MODE_ALL_LP			(MAX_RD_PKT_SIZE_LP | \
-					 DCS_LW_TX_LP | \
-					 DCS_SR_0P_TX_LP | \
-					 DCS_SW_1P_TX_LP | \
-					 DCS_SW_0P_TX_LP | \
-					 GEN_LW_TX_LP | \
-					 GEN_SR_2P_TX_LP | \
-					 GEN_SR_1P_TX_LP | \
-					 GEN_SR_0P_TX_LP | \
-					 GEN_SW_2P_TX_LP | \
-					 GEN_SW_1P_TX_LP | \
-					 GEN_SW_0P_TX_LP)
-
-#define DSI_GEN_HDR			0x6c
-#define GEN_HDATA(data)			(((data) & 0xffff) << 8)
-#define GEN_HDATA_MASK			(0xffff << 8)
-#define GEN_HTYPE(type)			(((type) & 0xff) << 0)
-#define GEN_HTYPE_MASK			0xff
-
-#define DSI_GEN_PLD_DATA		0x70
-
-#define DSI_CMD_PKT_STATUS		0x74
-#define GEN_CMD_EMPTY			BIT(0)
-#define GEN_CMD_FULL			BIT(1)
-#define GEN_PLD_W_EMPTY			BIT(2)
-#define GEN_PLD_W_FULL			BIT(3)
-#define GEN_PLD_R_EMPTY			BIT(4)
-#define GEN_PLD_R_FULL			BIT(5)
-#define GEN_RD_CMD_BUSY			BIT(6)
-
-#define DSI_TO_CNT_CFG			0x78
-#define HSTX_TO_CNT(p)			(((p) & 0xffff) << 16)
-#define LPRX_TO_CNT(p)			((p) & 0xffff)
-
-#define DSI_BTA_TO_CNT			0x8c
-#define DSI_LPCLK_CTRL			0x94
-#define AUTO_CLKLANE_CTRL		BIT(1)
-#define PHY_TXREQUESTCLKHS		BIT(0)
-
-#define DSI_PHY_TMR_LPCLK_CFG		0x98
-#define PHY_CLKHS2LP_TIME(lbcc)		(((lbcc) & 0x3ff) << 16)
-#define PHY_CLKLP2HS_TIME(lbcc)		((lbcc) & 0x3ff)
-
-#define DSI_PHY_TMR_CFG			0x9c
-#define PHY_HS2LP_TIME(lbcc)		(((lbcc) & 0xff) << 24)
-#define PHY_LP2HS_TIME(lbcc)		(((lbcc) & 0xff) << 16)
-#define MAX_RD_TIME(lbcc)		((lbcc) & 0x7fff)
-
-#define DSI_PHY_RSTZ			0xa0
-#define PHY_DISFORCEPLL			0
-#define PHY_ENFORCEPLL			BIT(3)
-#define PHY_DISABLECLK			0
-#define PHY_ENABLECLK			BIT(2)
-#define PHY_RSTZ			0
-#define PHY_UNRSTZ			BIT(1)
-#define PHY_SHUTDOWNZ			0
-#define PHY_UNSHUTDOWNZ			BIT(0)
-
-#define DSI_PHY_IF_CFG			0xa4
-#define N_LANES(n)			((((n) - 1) & 0x3) << 0)
-#define PHY_STOP_WAIT_TIME(cycle)	(((cycle) & 0xff) << 8)
-
-#define DSI_PHY_STATUS			0xb0
-#define LOCK				BIT(0)
-#define STOP_STATE_CLK_LANE		BIT(2)
-
-#define DSI_PHY_TST_CTRL0		0xb4
-#define PHY_TESTCLK			BIT(1)
-#define PHY_UNTESTCLK			0
-#define PHY_TESTCLR			BIT(0)
-#define PHY_UNTESTCLR			0
-
-#define DSI_PHY_TST_CTRL1		0xb8
-#define PHY_TESTEN			BIT(16)
-#define PHY_UNTESTEN			0
-#define PHY_TESTDOUT(n)			(((n) & 0xff) << 8)
-#define PHY_TESTDIN(n)			(((n) & 0xff) << 0)
-
-#define DSI_INT_ST0			0xbc
-#define DSI_INT_ST1			0xc0
-#define DSI_INT_MSK0			0xc4
-#define DSI_INT_MSK1			0xc8
-
-#define PHY_STATUS_TIMEOUT_US		10000
-#define CMD_PKT_STATUS_TIMEOUT_US	20000
-
-#define BYPASS_VCO_RANGE	BIT(7)
-#define VCO_RANGE_CON_SEL(val)	(((val) & 0x7) << 3)
-#define VCO_IN_CAP_CON_DEFAULT	(0x0 << 1)
-#define VCO_IN_CAP_CON_LOW	(0x1 << 1)
-#define VCO_IN_CAP_CON_HIGH	(0x2 << 1)
-#define REF_BIAS_CUR_SEL	BIT(0)
-
-#define CP_CURRENT_3MA		BIT(3)
-#define CP_PROGRAM_EN		BIT(7)
-#define LPF_PROGRAM_EN		BIT(6)
-#define LPF_RESISTORS_20_KOHM	0
-
-#define HSFREQRANGE_SEL(val)	(((val) & 0x3f) << 1)
-
-#define INPUT_DIVIDER(val)	(((val) - 1) & 0x7f)
-#define LOW_PROGRAM_EN		0
-#define HIGH_PROGRAM_EN		BIT(7)
-#define LOOP_DIV_LOW_SEL(val)	(((val) - 1) & 0x1f)
-#define LOOP_DIV_HIGH_SEL(val)	((((val) - 1) >> 5) & 0x1f)
-#define PLL_LOOP_DIV_EN		BIT(5)
-#define PLL_INPUT_DIV_EN	BIT(4)
-
-#define POWER_CONTROL		BIT(6)
-#define INTERNAL_REG_CURRENT	BIT(3)
-#define BIAS_BLOCK_ON		BIT(2)
-#define BANDGAP_ON		BIT(0)
-
-#define TER_RESISTOR_HIGH	BIT(7)
-#define	TER_RESISTOR_LOW	0
-#define LEVEL_SHIFTERS_ON	BIT(6)
-#define TER_CAL_DONE		BIT(5)
-#define SETRD_MAX		(0x7 << 2)
-#define POWER_MANAGE		BIT(1)
-#define TER_RESISTORS_ON	BIT(0)
-
-#define BIASEXTR_SEL(val)	((val) & 0x7)
-#define BANDGAP_SEL(val)	((val) & 0x7)
-#define TLP_PROGRAM_EN		BIT(7)
-#define THS_PRE_PROGRAM_EN	BIT(7)
-#define THS_ZERO_PROGRAM_EN	BIT(6)
-
-#define DW_MIPI_NEEDS_PHY_CFG_CLK	BIT(0)
-#define DW_MIPI_NEEDS_GRF_CLK		BIT(1)
-
-enum {
-	BANDGAP_97_07,
-	BANDGAP_98_05,
-	BANDGAP_99_02,
-	BANDGAP_100_00,
-	BANDGAP_93_17,
-	BANDGAP_94_15,
-	BANDGAP_95_12,
-	BANDGAP_96_10,
-};
-
-enum {
-	BIASEXTR_87_1,
-	BIASEXTR_91_5,
-	BIASEXTR_95_9,
-	BIASEXTR_100,
-	BIASEXTR_105_94,
-	BIASEXTR_111_88,
-	BIASEXTR_118_8,
-	BIASEXTR_127_7,
-};
-
-struct dw_mipi_dsi_plat_data {
-	u32 dsi0_en_bit;
-	u32 dsi1_en_bit;
-	u32 grf_switch_reg;
-	u32 grf_dsi0_mode;
-	u32 grf_dsi0_mode_reg;
-	unsigned int flags;
-	unsigned int max_data_lanes;
-};
-
-struct dw_mipi_dsi {
-	struct drm_encoder encoder;
-	struct drm_connector connector;
-	struct mipi_dsi_host dsi_host;
-	struct drm_panel *panel;
-	struct device *dev;
-	struct regmap *grf_regmap;
-	void __iomem *base;
-
-	struct clk *grf_clk;
-	struct clk *pllref_clk;
-	struct clk *pclk;
-	struct clk *phy_cfg_clk;
-
-	int dpms_mode;
-	unsigned int lane_mbps; /* per lane */
-	u32 channel;
-	u32 lanes;
-	u32 format;
-	u16 input_div;
-	u16 feedback_div;
-	unsigned long mode_flags;
-
-	const struct dw_mipi_dsi_plat_data *pdata;
-};
-
-enum dw_mipi_dsi_mode {
-	DW_MIPI_DSI_CMD_MODE,
-	DW_MIPI_DSI_VID_MODE,
-};
-
-struct dphy_pll_testdin_map {
-	unsigned int max_mbps;
-	u8 testdin;
-};
-
-/* The table is based on 27MHz DPHY pll reference clock. */
-static const struct dphy_pll_testdin_map dptdin_map[] = {
-	{  90, 0x00}, { 100, 0x10}, { 110, 0x20}, { 130, 0x01},
-	{ 140, 0x11}, { 150, 0x21}, { 170, 0x02}, { 180, 0x12},
-	{ 200, 0x22}, { 220, 0x03}, { 240, 0x13}, { 250, 0x23},
-	{ 270, 0x04}, { 300, 0x14}, { 330, 0x05}, { 360, 0x15},
-	{ 400, 0x25}, { 450, 0x06}, { 500, 0x16}, { 550, 0x07},
-	{ 600, 0x17}, { 650, 0x08}, { 700, 0x18}, { 750, 0x09},
-	{ 800, 0x19}, { 850, 0x29}, { 900, 0x39}, { 950, 0x0a},
-	{1000, 0x1a}, {1050, 0x2a}, {1100, 0x3a}, {1150, 0x0b},
-	{1200, 0x1b}, {1250, 0x2b}, {1300, 0x3b}, {1350, 0x0c},
-	{1400, 0x1c}, {1450, 0x2c}, {1500, 0x3c}
-};
-
-static int max_mbps_to_testdin(unsigned int max_mbps)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(dptdin_map); i++)
-		if (dptdin_map[i].max_mbps > max_mbps)
-			return dptdin_map[i].testdin;
-
-	return -EINVAL;
-}
-
-/*
- * The controller should generate 2 frames before
- * preparing the peripheral.
- */
-static void dw_mipi_dsi_wait_for_two_frames(struct drm_display_mode *mode)
-{
-	int refresh, two_frames;
-
-	refresh = drm_mode_vrefresh(mode);
-	two_frames = DIV_ROUND_UP(MSEC_PER_SEC, refresh) * 2;
-	msleep(two_frames);
-}
-
-static inline struct dw_mipi_dsi *host_to_dsi(struct mipi_dsi_host *host)
-{
-	return container_of(host, struct dw_mipi_dsi, dsi_host);
-}
-
-static inline struct dw_mipi_dsi *con_to_dsi(struct drm_connector *con)
-{
-	return container_of(con, struct dw_mipi_dsi, connector);
-}
-
-static inline struct dw_mipi_dsi *encoder_to_dsi(struct drm_encoder *encoder)
-{
-	return container_of(encoder, struct dw_mipi_dsi, encoder);
-}
-
-static inline void dsi_write(struct dw_mipi_dsi *dsi, u32 reg, u32 val)
-{
-	writel(val, dsi->base + reg);
-}
-
-static inline u32 dsi_read(struct dw_mipi_dsi *dsi, u32 reg)
-{
-	return readl(dsi->base + reg);
-}
-
-static void dw_mipi_dsi_phy_write(struct dw_mipi_dsi *dsi, u8 test_code,
-				  u8 test_data)
-{
-	/*
-	 * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
-	 * is latched internally as the current test code. Test data is
-	 * programmed internally by rising edge on TESTCLK.
-	 */
-	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
-
-	dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_TESTEN | PHY_TESTDOUT(0) |
-					  PHY_TESTDIN(test_code));
-
-	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR);
-
-	dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_UNTESTEN | PHY_TESTDOUT(0) |
-					  PHY_TESTDIN(test_data));
-
-	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
-}
-
-/**
- * ns2bc - Nanoseconds to byte clock cycles
- */
-static inline unsigned int ns2bc(struct dw_mipi_dsi *dsi, int ns)
-{
-	return DIV_ROUND_UP(ns * dsi->lane_mbps / 8, 1000);
-}
-
-/**
- * ns2ui - Nanoseconds to UI time periods
- */
-static inline unsigned int ns2ui(struct dw_mipi_dsi *dsi, int ns)
-{
-	return DIV_ROUND_UP(ns * dsi->lane_mbps, 1000);
-}
-
-static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
-{
-	int ret, testdin, vco, val;
-
-	vco = (dsi->lane_mbps < 200) ? 0 : (dsi->lane_mbps + 100) / 200;
-
-	testdin = max_mbps_to_testdin(dsi->lane_mbps);
-	if (testdin < 0) {
-		DRM_DEV_ERROR(dsi->dev,
-			      "failed to get testdin for %dmbps lane clock\n",
-			      dsi->lane_mbps);
-		return testdin;
-	}
-
-	/* Start by clearing PHY state */
-	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLR);
-	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLR);
-	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLR);
-
-	ret = clk_prepare_enable(dsi->phy_cfg_clk);
-	if (ret) {
-		DRM_DEV_ERROR(dsi->dev, "Failed to enable phy_cfg_clk\n");
-		return ret;
-	}
-
-	dw_mipi_dsi_phy_write(dsi, 0x10, BYPASS_VCO_RANGE |
-					 VCO_RANGE_CON_SEL(vco) |
-					 VCO_IN_CAP_CON_LOW |
-					 REF_BIAS_CUR_SEL);
-
-	dw_mipi_dsi_phy_write(dsi, 0x11, CP_CURRENT_3MA);
-	dw_mipi_dsi_phy_write(dsi, 0x12, CP_PROGRAM_EN | LPF_PROGRAM_EN |
-					 LPF_RESISTORS_20_KOHM);
-
-	dw_mipi_dsi_phy_write(dsi, 0x44, HSFREQRANGE_SEL(testdin));
-
-	dw_mipi_dsi_phy_write(dsi, 0x17, INPUT_DIVIDER(dsi->input_div));
-	dw_mipi_dsi_phy_write(dsi, 0x18, LOOP_DIV_LOW_SEL(dsi->feedback_div) |
-					 LOW_PROGRAM_EN);
-	dw_mipi_dsi_phy_write(dsi, 0x18, LOOP_DIV_HIGH_SEL(dsi->feedback_div) |
-					 HIGH_PROGRAM_EN);
-	dw_mipi_dsi_phy_write(dsi, 0x19, PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN);
-
-	dw_mipi_dsi_phy_write(dsi, 0x22, LOW_PROGRAM_EN |
-					 BIASEXTR_SEL(BIASEXTR_127_7));
-	dw_mipi_dsi_phy_write(dsi, 0x22, HIGH_PROGRAM_EN |
-					 BANDGAP_SEL(BANDGAP_96_10));
-
-	dw_mipi_dsi_phy_write(dsi, 0x20, POWER_CONTROL | INTERNAL_REG_CURRENT |
-					 BIAS_BLOCK_ON | BANDGAP_ON);
-
-	dw_mipi_dsi_phy_write(dsi, 0x21, TER_RESISTOR_LOW | TER_CAL_DONE |
-					 SETRD_MAX | TER_RESISTORS_ON);
-	dw_mipi_dsi_phy_write(dsi, 0x21, TER_RESISTOR_HIGH | LEVEL_SHIFTERS_ON |
-					 SETRD_MAX | POWER_MANAGE |
-					 TER_RESISTORS_ON);
-
-	dw_mipi_dsi_phy_write(dsi, 0x60, TLP_PROGRAM_EN | ns2bc(dsi, 500));
-	dw_mipi_dsi_phy_write(dsi, 0x61, THS_PRE_PROGRAM_EN | ns2ui(dsi, 40));
-	dw_mipi_dsi_phy_write(dsi, 0x62, THS_ZERO_PROGRAM_EN | ns2bc(dsi, 300));
-	dw_mipi_dsi_phy_write(dsi, 0x63, THS_PRE_PROGRAM_EN | ns2ui(dsi, 100));
-	dw_mipi_dsi_phy_write(dsi, 0x64, BIT(5) | ns2bc(dsi, 100));
-	dw_mipi_dsi_phy_write(dsi, 0x65, BIT(5) | (ns2bc(dsi, 60) + 7));
-
-	dw_mipi_dsi_phy_write(dsi, 0x70, TLP_PROGRAM_EN | ns2bc(dsi, 500));
-	dw_mipi_dsi_phy_write(dsi, 0x71,
-			      THS_PRE_PROGRAM_EN | (ns2ui(dsi, 50) + 5));
-	dw_mipi_dsi_phy_write(dsi, 0x72,
-			      THS_ZERO_PROGRAM_EN | (ns2bc(dsi, 140) + 2));
-	dw_mipi_dsi_phy_write(dsi, 0x73,
-			      THS_PRE_PROGRAM_EN | (ns2ui(dsi, 60) + 8));
-	dw_mipi_dsi_phy_write(dsi, 0x74, BIT(5) | ns2bc(dsi, 100));
-
-	dsi_write(dsi, DSI_PHY_RSTZ, PHY_ENFORCEPLL | PHY_ENABLECLK |
-				     PHY_UNRSTZ | PHY_UNSHUTDOWNZ);
-
-	ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS,
-				 val, val & LOCK, 1000, PHY_STATUS_TIMEOUT_US);
-	if (ret < 0) {
-		DRM_DEV_ERROR(dsi->dev, "failed to wait for phy lock state\n");
-		goto phy_init_end;
-	}
-
-	ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS,
-				 val, val & STOP_STATE_CLK_LANE, 1000,
-				 PHY_STATUS_TIMEOUT_US);
-	if (ret < 0)
-		DRM_DEV_ERROR(dsi->dev,
-			      "failed to wait for phy clk lane stop state\n");
-
-phy_init_end:
-	clk_disable_unprepare(dsi->phy_cfg_clk);
-
-	return ret;
-}
-
-static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi,
-				    struct drm_display_mode *mode)
-{
-	unsigned int i, pre;
-	unsigned long mpclk, pllref, tmp;
-	unsigned int m = 1, n = 1, target_mbps = 1000;
-	unsigned int max_mbps = dptdin_map[ARRAY_SIZE(dptdin_map) - 1].max_mbps;
-	int bpp;
-
-	bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
-	if (bpp < 0) {
-		DRM_DEV_ERROR(dsi->dev,
-			      "failed to get bpp for pixel format %d\n",
-			      dsi->format);
-		return bpp;
-	}
-
-	mpclk = DIV_ROUND_UP(mode->clock, MSEC_PER_SEC);
-	if (mpclk) {
-		/* take 1 / 0.8, since mbps must big than bandwidth of RGB */
-		tmp = mpclk * (bpp / dsi->lanes) * 10 / 8;
-		if (tmp < max_mbps)
-			target_mbps = tmp;
-		else
-			DRM_DEV_ERROR(dsi->dev,
-				      "DPHY clock frequency is out of range\n");
-	}
-
-	pllref = DIV_ROUND_UP(clk_get_rate(dsi->pllref_clk), USEC_PER_SEC);
-	tmp = pllref;
-
-	/*
-	 * The limits on the PLL divisor are:
-	 *
-	 *	5MHz <= (pllref / n) <= 40MHz
-	 *
-	 * we walk over these values in descreasing order so that if we hit
-	 * an exact match for target_mbps it is more likely that "m" will be
-	 * even.
-	 *
-	 * TODO: ensure that "m" is even after this loop.
-	 */
-	for (i = pllref / 5; i > (pllref / 40); i--) {
-		pre = pllref / i;
-		if ((tmp > (target_mbps % pre)) && (target_mbps / pre < 512)) {
-			tmp = target_mbps % pre;
-			n = i;
-			m = target_mbps / pre;
-		}
-		if (tmp == 0)
-			break;
-	}
-
-	dsi->lane_mbps = pllref / n * m;
-	dsi->input_div = n;
-	dsi->feedback_div = m;
-
-	return 0;
-}
-
-static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
-				   struct mipi_dsi_device *device)
-{
-	struct dw_mipi_dsi *dsi = host_to_dsi(host);
-
-	if (device->lanes > dsi->pdata->max_data_lanes) {
-		DRM_DEV_ERROR(dsi->dev,
-			      "the number of data lanes(%u) is too many\n",
-			      device->lanes);
-		return -EINVAL;
-	}
-
-	dsi->lanes = device->lanes;
-	dsi->channel = device->channel;
-	dsi->format = device->format;
-	dsi->mode_flags = device->mode_flags;
-	dsi->panel = of_drm_find_panel(device->dev.of_node);
-	if (dsi->panel)
-		return drm_panel_attach(dsi->panel, &dsi->connector);
-
-	return -EINVAL;
-}
-
-static int dw_mipi_dsi_host_detach(struct mipi_dsi_host *host,
-				   struct mipi_dsi_device *device)
-{
-	struct dw_mipi_dsi *dsi = host_to_dsi(host);
-
-	drm_panel_detach(dsi->panel);
-
-	return 0;
-}
-
-static void dw_mipi_message_config(struct dw_mipi_dsi *dsi,
-				   const struct mipi_dsi_msg *msg)
-{
-	bool lpm = msg->flags & MIPI_DSI_MSG_USE_LPM;
-	u32 val = 0;
-
-	if (msg->flags & MIPI_DSI_MSG_REQ_ACK)
-		val |= EN_ACK_RQST;
-	if (lpm)
-		val |= CMD_MODE_ALL_LP;
-
-	dsi_write(dsi, DSI_LPCLK_CTRL, lpm ? 0 : PHY_TXREQUESTCLKHS);
-	dsi_write(dsi, DSI_CMD_MODE_CFG, val);
-}
-
-static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 hdr_val)
-{
-	int ret;
-	u32 val, mask;
-
-	ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
-				 val, !(val & GEN_CMD_FULL), 1000,
-				 CMD_PKT_STATUS_TIMEOUT_US);
-	if (ret < 0) {
-		DRM_DEV_ERROR(dsi->dev,
-			      "failed to get available command FIFO\n");
-		return ret;
-	}
-
-	dsi_write(dsi, DSI_GEN_HDR, hdr_val);
-
-	mask = GEN_CMD_EMPTY | GEN_PLD_W_EMPTY;
-	ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
-				 val, (val & mask) == mask,
-				 1000, CMD_PKT_STATUS_TIMEOUT_US);
-	if (ret < 0) {
-		DRM_DEV_ERROR(dsi->dev, "failed to write command FIFO\n");
-		return ret;
-	}
-
-	return 0;
-}
-
-static int dw_mipi_dsi_dcs_short_write(struct dw_mipi_dsi *dsi,
-				       const struct mipi_dsi_msg *msg)
-{
-	const u8 *tx_buf = msg->tx_buf;
-	u16 data = 0;
-	u32 val;
-
-	if (msg->tx_len > 0)
-		data |= tx_buf[0];
-	if (msg->tx_len > 1)
-		data |= tx_buf[1] << 8;
-
-	if (msg->tx_len > 2) {
-		DRM_DEV_ERROR(dsi->dev,
-			      "too long tx buf length %zu for short write\n",
-			      msg->tx_len);
-		return -EINVAL;
-	}
-
-	val = GEN_HDATA(data) | GEN_HTYPE(msg->type);
-	return dw_mipi_dsi_gen_pkt_hdr_write(dsi, val);
-}
-
-static int dw_mipi_dsi_dcs_long_write(struct dw_mipi_dsi *dsi,
-				      const struct mipi_dsi_msg *msg)
-{
-	const u8 *tx_buf = msg->tx_buf;
-	int len = msg->tx_len, pld_data_bytes = sizeof(u32), ret;
-	u32 hdr_val = GEN_HDATA(msg->tx_len) | GEN_HTYPE(msg->type);
-	u32 remainder;
-	u32 val;
-
-	if (msg->tx_len < 3) {
-		DRM_DEV_ERROR(dsi->dev,
-			      "wrong tx buf length %zu for long write\n",
-			      msg->tx_len);
-		return -EINVAL;
-	}
-
-	while (DIV_ROUND_UP(len, pld_data_bytes)) {
-		if (len < pld_data_bytes) {
-			remainder = 0;
-			memcpy(&remainder, tx_buf, len);
-			dsi_write(dsi, DSI_GEN_PLD_DATA, remainder);
-			len = 0;
-		} else {
-			memcpy(&remainder, tx_buf, pld_data_bytes);
-			dsi_write(dsi, DSI_GEN_PLD_DATA, remainder);
-			tx_buf += pld_data_bytes;
-			len -= pld_data_bytes;
-		}
-
-		ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
-					 val, !(val & GEN_PLD_W_FULL), 1000,
-					 CMD_PKT_STATUS_TIMEOUT_US);
-		if (ret < 0) {
-			DRM_DEV_ERROR(dsi->dev,
-				      "failed to get available write payload FIFO\n");
-			return ret;
-		}
-	}
-
-	return dw_mipi_dsi_gen_pkt_hdr_write(dsi, hdr_val);
-}
-
-static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host,
-					 const struct mipi_dsi_msg *msg)
-{
-	struct dw_mipi_dsi *dsi = host_to_dsi(host);
-	int ret;
-
-	dw_mipi_message_config(dsi, msg);
-
-	switch (msg->type) {
-	case MIPI_DSI_DCS_SHORT_WRITE:
-	case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
-	case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
-		ret = dw_mipi_dsi_dcs_short_write(dsi, msg);
-		break;
-	case MIPI_DSI_DCS_LONG_WRITE:
-		ret = dw_mipi_dsi_dcs_long_write(dsi, msg);
-		break;
-	default:
-		DRM_DEV_ERROR(dsi->dev, "unsupported message type 0x%02x\n",
-			      msg->type);
-		ret = -EINVAL;
-	}
-
-	return ret;
-}
-
-static const struct mipi_dsi_host_ops dw_mipi_dsi_host_ops = {
-	.attach = dw_mipi_dsi_host_attach,
-	.detach = dw_mipi_dsi_host_detach,
-	.transfer = dw_mipi_dsi_host_transfer,
-};
-
-static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi)
-{
-	u32 val;
-
-	val = ENABLE_LOW_POWER;
-
-	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
-		val |= VID_MODE_TYPE_BURST;
-	else if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
-		val |= VID_MODE_TYPE_NON_BURST_SYNC_PULSES;
-	else
-		val |= VID_MODE_TYPE_NON_BURST_SYNC_EVENTS;
-
-	dsi_write(dsi, DSI_VID_MODE_CFG, val);
-}
-
-static void dw_mipi_dsi_set_mode(struct dw_mipi_dsi *dsi,
-				 enum dw_mipi_dsi_mode mode)
-{
-	if (mode == DW_MIPI_DSI_CMD_MODE) {
-		dsi_write(dsi, DSI_PWR_UP, RESET);
-		dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE);
-		dsi_write(dsi, DSI_PWR_UP, POWERUP);
-	} else {
-		dsi_write(dsi, DSI_PWR_UP, RESET);
-		dsi_write(dsi, DSI_MODE_CFG, ENABLE_VIDEO_MODE);
-		dw_mipi_dsi_video_mode_config(dsi);
-		dsi_write(dsi, DSI_LPCLK_CTRL, PHY_TXREQUESTCLKHS);
-		dsi_write(dsi, DSI_PWR_UP, POWERUP);
-	}
-}
-
-static void dw_mipi_dsi_disable(struct dw_mipi_dsi *dsi)
-{
-	dsi_write(dsi, DSI_PWR_UP, RESET);
-	dsi_write(dsi, DSI_PHY_RSTZ, PHY_RSTZ);
-}
-
-static void dw_mipi_dsi_init(struct dw_mipi_dsi *dsi)
-{
-	/*
-	 * The maximum permitted escape clock is 20MHz and it is derived from
-	 * lanebyteclk, which is running at "lane_mbps / 8".  Thus we want:
-	 *
-	 *     (lane_mbps >> 3) / esc_clk_division < 20
-	 * which is:
-	 *     (lane_mbps >> 3) / 20 > esc_clk_division
-	 */
-	u32 esc_clk_division = (dsi->lane_mbps >> 3) / 20 + 1;
-
-	dsi_write(dsi, DSI_PWR_UP, RESET);
-	dsi_write(dsi, DSI_PHY_RSTZ, PHY_DISFORCEPLL | PHY_DISABLECLK
-		  | PHY_RSTZ | PHY_SHUTDOWNZ);
-	dsi_write(dsi, DSI_CLKMGR_CFG, TO_CLK_DIVIDSION(10) |
-		  TX_ESC_CLK_DIVIDSION(esc_clk_division));
-}
-
-static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
-				   struct drm_display_mode *mode)
-{
-	u32 val = 0, color = 0;
-
-	switch (dsi->format) {
-	case MIPI_DSI_FMT_RGB888:
-		color = DPI_COLOR_CODING_24BIT;
-		break;
-	case MIPI_DSI_FMT_RGB666:
-		color = DPI_COLOR_CODING_18BIT_2 | EN18_LOOSELY;
-		break;
-	case MIPI_DSI_FMT_RGB666_PACKED:
-		color = DPI_COLOR_CODING_18BIT_1;
-		break;
-	case MIPI_DSI_FMT_RGB565:
-		color = DPI_COLOR_CODING_16BIT_1;
-		break;
-	}
-
-	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
-		val |= VSYNC_ACTIVE_LOW;
-	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
-		val |= HSYNC_ACTIVE_LOW;
-
-	dsi_write(dsi, DSI_DPI_VCID, DPI_VID(dsi->channel));
-	dsi_write(dsi, DSI_DPI_COLOR_CODING, color);
-	dsi_write(dsi, DSI_DPI_CFG_POL, val);
-	dsi_write(dsi, DSI_DPI_LP_CMD_TIM, OUTVACT_LPCMD_TIME(4)
-		  | INVACT_LPCMD_TIME(4));
-}
-
-static void dw_mipi_dsi_packet_handler_config(struct dw_mipi_dsi *dsi)
-{
-	dsi_write(dsi, DSI_PCKHDL_CFG, EN_CRC_RX | EN_ECC_RX | EN_BTA);
-}
-
-static void dw_mipi_dsi_video_packet_config(struct dw_mipi_dsi *dsi,
-					    struct drm_display_mode *mode)
-{
-	dsi_write(dsi, DSI_VID_PKT_SIZE, VID_PKT_SIZE(mode->hdisplay));
-}
-
-static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi)
-{
-	dsi_write(dsi, DSI_TO_CNT_CFG, HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000));
-	dsi_write(dsi, DSI_BTA_TO_CNT, 0xd00);
-	dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE);
-}
-
-/* Get lane byte clock cycles. */
-static u32 dw_mipi_dsi_get_hcomponent_lbcc(struct dw_mipi_dsi *dsi,
-					   struct drm_display_mode *mode,
-					   u32 hcomponent)
-{
-	u32 frac, lbcc;
-
-	lbcc = hcomponent * dsi->lane_mbps * MSEC_PER_SEC / 8;
-
-	frac = lbcc % mode->clock;
-	lbcc = lbcc / mode->clock;
-	if (frac)
-		lbcc++;
-
-	return lbcc;
-}
-
-static void dw_mipi_dsi_line_timer_config(struct dw_mipi_dsi *dsi,
-					  struct drm_display_mode *mode)
-{
-	u32 htotal, hsa, hbp, lbcc;
-
-	htotal = mode->htotal;
-	hsa = mode->hsync_end - mode->hsync_start;
-	hbp = mode->htotal - mode->hsync_end;
-
-	lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, mode, htotal);
-	dsi_write(dsi, DSI_VID_HLINE_TIME, lbcc);
-
-	lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, mode, hsa);
-	dsi_write(dsi, DSI_VID_HSA_TIME, lbcc);
-
-	lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, mode, hbp);
-	dsi_write(dsi, DSI_VID_HBP_TIME, lbcc);
-}
-
-static void dw_mipi_dsi_vertical_timing_config(struct dw_mipi_dsi *dsi,
-					       struct drm_display_mode *mode)
-{
-	u32 vactive, vsa, vfp, vbp;
-
-	vactive = mode->vdisplay;
-	vsa = mode->vsync_end - mode->vsync_start;
-	vfp = mode->vsync_start - mode->vdisplay;
-	vbp = mode->vtotal - mode->vsync_end;
-
-	dsi_write(dsi, DSI_VID_VACTIVE_LINES, vactive);
-	dsi_write(dsi, DSI_VID_VSA_LINES, vsa);
-	dsi_write(dsi, DSI_VID_VFP_LINES, vfp);
-	dsi_write(dsi, DSI_VID_VBP_LINES, vbp);
-}
-
-static void dw_mipi_dsi_dphy_timing_config(struct dw_mipi_dsi *dsi)
-{
-	dsi_write(dsi, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME(0x40)
-		  | PHY_LP2HS_TIME(0x40) | MAX_RD_TIME(10000));
-
-	dsi_write(dsi, DSI_PHY_TMR_LPCLK_CFG, PHY_CLKHS2LP_TIME(0x40)
-		  | PHY_CLKLP2HS_TIME(0x40));
-}
-
-static void dw_mipi_dsi_dphy_interface_config(struct dw_mipi_dsi *dsi)
-{
-	dsi_write(dsi, DSI_PHY_IF_CFG, PHY_STOP_WAIT_TIME(0x20) |
-		  N_LANES(dsi->lanes));
-}
-
-static void dw_mipi_dsi_clear_err(struct dw_mipi_dsi *dsi)
-{
-	dsi_read(dsi, DSI_INT_ST0);
-	dsi_read(dsi, DSI_INT_ST1);
-	dsi_write(dsi, DSI_INT_MSK0, 0);
-	dsi_write(dsi, DSI_INT_MSK1, 0);
-}
-
-static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder)
-{
-	struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
-
-	if (dsi->dpms_mode != DRM_MODE_DPMS_ON)
-		return;
-
-	if (clk_prepare_enable(dsi->pclk)) {
-		DRM_DEV_ERROR(dsi->dev, "Failed to enable pclk\n");
-		return;
-	}
-
-	drm_panel_disable(dsi->panel);
-
-	dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_CMD_MODE);
-	drm_panel_unprepare(dsi->panel);
-
-	dw_mipi_dsi_disable(dsi);
-	pm_runtime_put(dsi->dev);
-	clk_disable_unprepare(dsi->pclk);
-	dsi->dpms_mode = DRM_MODE_DPMS_OFF;
-}
-
-static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
-{
-	struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
-	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
-	const struct dw_mipi_dsi_plat_data *pdata = dsi->pdata;
-	int mux = drm_of_encoder_active_endpoint_id(dsi->dev->of_node, encoder);
-	u32 val;
-	int ret;
-
-	ret = dw_mipi_dsi_get_lane_bps(dsi, mode);
-	if (ret < 0)
-		return;
-
-	if (dsi->dpms_mode == DRM_MODE_DPMS_ON)
-		return;
-
-	if (clk_prepare_enable(dsi->pclk)) {
-		DRM_DEV_ERROR(dsi->dev, "Failed to enable pclk\n");
-		return;
-	}
-
-	pm_runtime_get_sync(dsi->dev);
-	dw_mipi_dsi_init(dsi);
-	dw_mipi_dsi_dpi_config(dsi, mode);
-	dw_mipi_dsi_packet_handler_config(dsi);
-	dw_mipi_dsi_video_mode_config(dsi);
-	dw_mipi_dsi_video_packet_config(dsi, mode);
-	dw_mipi_dsi_command_mode_config(dsi);
-	dw_mipi_dsi_line_timer_config(dsi, mode);
-	dw_mipi_dsi_vertical_timing_config(dsi, mode);
-	dw_mipi_dsi_dphy_timing_config(dsi);
-	dw_mipi_dsi_dphy_interface_config(dsi);
-	dw_mipi_dsi_clear_err(dsi);
-
-	/*
-	 * For the RK3399, the clk of grf must be enabled before writing grf
-	 * register. And for RK3288 or other soc, this grf_clk must be NULL,
-	 * the clk_prepare_enable return true directly.
-	 */
-	ret = clk_prepare_enable(dsi->grf_clk);
-	if (ret) {
-		DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret);
-		return;
-	}
-
-	if (pdata->grf_dsi0_mode_reg)
-		regmap_write(dsi->grf_regmap, pdata->grf_dsi0_mode_reg,
-			     pdata->grf_dsi0_mode);
-
-	dw_mipi_dsi_phy_init(dsi);
-	dw_mipi_dsi_wait_for_two_frames(mode);
-
-	dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_CMD_MODE);
-	if (drm_panel_prepare(dsi->panel))
-		DRM_DEV_ERROR(dsi->dev, "failed to prepare panel\n");
-
-	dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_VID_MODE);
-	drm_panel_enable(dsi->panel);
-
-	clk_disable_unprepare(dsi->pclk);
-
-	if (mux)
-		val = pdata->dsi0_en_bit | (pdata->dsi0_en_bit << 16);
-	else
-		val = pdata->dsi0_en_bit << 16;
-
-	regmap_write(dsi->grf_regmap, pdata->grf_switch_reg, val);
-	DRM_DEV_DEBUG(dsi->dev,
-		      "vop %s output to dsi0\n", (mux) ? "LIT" : "BIG");
-	dsi->dpms_mode = DRM_MODE_DPMS_ON;
-
-	clk_disable_unprepare(dsi->grf_clk);
-}
-
-static int
-dw_mipi_dsi_encoder_atomic_check(struct drm_encoder *encoder,
-				 struct drm_crtc_state *crtc_state,
-				 struct drm_connector_state *conn_state)
-{
-	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
-	struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
-
-	switch (dsi->format) {
-	case MIPI_DSI_FMT_RGB888:
-		s->output_mode = ROCKCHIP_OUT_MODE_P888;
-		break;
-	case MIPI_DSI_FMT_RGB666:
-		s->output_mode = ROCKCHIP_OUT_MODE_P666;
-		break;
-	case MIPI_DSI_FMT_RGB565:
-		s->output_mode = ROCKCHIP_OUT_MODE_P565;
-		break;
-	default:
-		WARN_ON(1);
-		return -EINVAL;
-	}
-
-	s->output_type = DRM_MODE_CONNECTOR_DSI;
-
-	return 0;
-}
-
-static const struct drm_encoder_helper_funcs
-dw_mipi_dsi_encoder_helper_funcs = {
-	.enable = dw_mipi_dsi_encoder_enable,
-	.disable = dw_mipi_dsi_encoder_disable,
-	.atomic_check = dw_mipi_dsi_encoder_atomic_check,
-};
-
-static const struct drm_encoder_funcs dw_mipi_dsi_encoder_funcs = {
-	.destroy = drm_encoder_cleanup,
-};
-
-static int dw_mipi_dsi_connector_get_modes(struct drm_connector *connector)
-{
-	struct dw_mipi_dsi *dsi = con_to_dsi(connector);
-
-	return drm_panel_get_modes(dsi->panel);
-}
-
-static struct drm_connector_helper_funcs dw_mipi_dsi_connector_helper_funcs = {
-	.get_modes = dw_mipi_dsi_connector_get_modes,
-};
-
-static void dw_mipi_dsi_drm_connector_destroy(struct drm_connector *connector)
-{
-	drm_connector_unregister(connector);
-	drm_connector_cleanup(connector);
-}
-
-static const struct drm_connector_funcs dw_mipi_dsi_atomic_connector_funcs = {
-	.fill_modes = drm_helper_probe_single_connector_modes,
-	.destroy = dw_mipi_dsi_drm_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 dw_mipi_dsi_register(struct drm_device *drm,
-				struct dw_mipi_dsi *dsi)
-{
-	struct drm_encoder *encoder = &dsi->encoder;
-	struct drm_connector *connector = &dsi->connector;
-	struct device *dev = dsi->dev;
-	int ret;
-
-	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm,
-							     dev->of_node);
-	/*
-	 * If we failed to find the CRTC(s) which this encoder is
-	 * supposed to be connected to, it's because the CRTC has
-	 * not been registered yet.  Defer probing, and hope that
-	 * the required CRTC is added later.
-	 */
-	if (encoder->possible_crtcs == 0)
-		return -EPROBE_DEFER;
-
-	drm_encoder_helper_add(&dsi->encoder,
-			       &dw_mipi_dsi_encoder_helper_funcs);
-	ret = drm_encoder_init(drm, &dsi->encoder, &dw_mipi_dsi_encoder_funcs,
-			       DRM_MODE_ENCODER_DSI, NULL);
-	if (ret) {
-		DRM_DEV_ERROR(dev, "Failed to initialize encoder with drm\n");
-		return ret;
-	}
-
-	drm_connector_helper_add(connector,
-				 &dw_mipi_dsi_connector_helper_funcs);
-
-	drm_connector_init(drm, &dsi->connector,
-			   &dw_mipi_dsi_atomic_connector_funcs,
-			   DRM_MODE_CONNECTOR_DSI);
-
-	drm_mode_connector_attach_encoder(connector, encoder);
-
-	return 0;
-}
-
-static int rockchip_mipi_parse_dt(struct dw_mipi_dsi *dsi)
-{
-	struct device_node *np = dsi->dev->of_node;
-
-	dsi->grf_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
-	if (IS_ERR(dsi->grf_regmap)) {
-		DRM_DEV_ERROR(dsi->dev, "Unable to get rockchip,grf\n");
-		return PTR_ERR(dsi->grf_regmap);
-	}
-
-	return 0;
-}
-
-static struct dw_mipi_dsi_plat_data rk3288_mipi_dsi_drv_data = {
-	.dsi0_en_bit = RK3288_DSI0_SEL_VOP_LIT,
-	.dsi1_en_bit = RK3288_DSI1_SEL_VOP_LIT,
-	.grf_switch_reg = RK3288_GRF_SOC_CON6,
-	.max_data_lanes = 4,
-};
-
-static struct dw_mipi_dsi_plat_data rk3399_mipi_dsi_drv_data = {
-	.dsi0_en_bit = RK3399_DSI0_SEL_VOP_LIT,
-	.dsi1_en_bit = RK3399_DSI1_SEL_VOP_LIT,
-	.grf_switch_reg = RK3399_GRF_SOC_CON20,
-	.grf_dsi0_mode = RK3399_GRF_DSI_MODE,
-	.grf_dsi0_mode_reg = RK3399_GRF_SOC_CON22,
-	.flags = DW_MIPI_NEEDS_PHY_CFG_CLK | DW_MIPI_NEEDS_GRF_CLK,
-	.max_data_lanes = 4,
-};
-
-static const struct of_device_id dw_mipi_dsi_dt_ids[] = {
-	{
-	 .compatible = "rockchip,rk3288-mipi-dsi",
-	 .data = &rk3288_mipi_dsi_drv_data,
-	}, {
-	 .compatible = "rockchip,rk3399-mipi-dsi",
-	 .data = &rk3399_mipi_dsi_drv_data,
-	},
-	{ /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, dw_mipi_dsi_dt_ids);
-
-static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
-			    void *data)
-{
-	const struct of_device_id *of_id =
-			of_match_device(dw_mipi_dsi_dt_ids, dev);
-	const struct dw_mipi_dsi_plat_data *pdata = of_id->data;
-	struct platform_device *pdev = to_platform_device(dev);
-	struct reset_control *apb_rst;
-	struct drm_device *drm = data;
-	struct dw_mipi_dsi *dsi;
-	struct resource *res;
-	int ret;
-
-	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
-	if (!dsi)
-		return -ENOMEM;
-
-	dsi->dev = dev;
-	dsi->pdata = pdata;
-	dsi->dpms_mode = DRM_MODE_DPMS_OFF;
-
-	ret = rockchip_mipi_parse_dt(dsi);
-	if (ret)
-		return ret;
-
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	dsi->base = devm_ioremap_resource(dev, res);
-	if (IS_ERR(dsi->base))
-		return PTR_ERR(dsi->base);
-
-	dsi->pllref_clk = devm_clk_get(dev, "ref");
-	if (IS_ERR(dsi->pllref_clk)) {
-		ret = PTR_ERR(dsi->pllref_clk);
-		DRM_DEV_ERROR(dev,
-			      "Unable to get pll reference clock: %d\n", ret);
-		return ret;
-	}
-
-	dsi->pclk = devm_clk_get(dev, "pclk");
-	if (IS_ERR(dsi->pclk)) {
-		ret = PTR_ERR(dsi->pclk);
-		DRM_DEV_ERROR(dev, "Unable to get pclk: %d\n", ret);
-		return ret;
-	}
-
-	/*
-	 * Note that the reset was not defined in the initial device tree, so
-	 * we have to be prepared for it not being found.
-	 */
-	apb_rst = devm_reset_control_get(dev, "apb");
-	if (IS_ERR(apb_rst)) {
-		ret = PTR_ERR(apb_rst);
-		if (ret == -ENOENT) {
-			apb_rst = NULL;
-		} else {
-			DRM_DEV_ERROR(dev,
-				      "Unable to get reset control: %d\n", ret);
-			return ret;
-		}
-	}
-
-	if (apb_rst) {
-		ret = clk_prepare_enable(dsi->pclk);
-		if (ret) {
-			DRM_DEV_ERROR(dev, "Failed to enable pclk\n");
-			return ret;
-		}
-
-		reset_control_assert(apb_rst);
-		usleep_range(10, 20);
-		reset_control_deassert(apb_rst);
-
-		clk_disable_unprepare(dsi->pclk);
-	}
-
-	if (pdata->flags & DW_MIPI_NEEDS_PHY_CFG_CLK) {
-		dsi->phy_cfg_clk = devm_clk_get(dev, "phy_cfg");
-		if (IS_ERR(dsi->phy_cfg_clk)) {
-			ret = PTR_ERR(dsi->phy_cfg_clk);
-			DRM_DEV_ERROR(dev,
-				      "Unable to get phy_cfg_clk: %d\n", ret);
-			return ret;
-		}
-	}
-
-	if (pdata->flags & DW_MIPI_NEEDS_GRF_CLK) {
-		dsi->grf_clk = devm_clk_get(dev, "grf");
-		if (IS_ERR(dsi->grf_clk)) {
-			ret = PTR_ERR(dsi->grf_clk);
-			DRM_DEV_ERROR(dev, "Unable to get grf_clk: %d\n", ret);
-			return ret;
-		}
-	}
-
-	ret = clk_prepare_enable(dsi->pllref_clk);
-	if (ret) {
-		DRM_DEV_ERROR(dev, "Failed to enable pllref_clk\n");
-		return ret;
-	}
-
-	ret = dw_mipi_dsi_register(drm, dsi);
-	if (ret) {
-		DRM_DEV_ERROR(dev, "Failed to register mipi_dsi: %d\n", ret);
-		goto err_pllref;
-	}
-
-	dsi->dsi_host.ops = &dw_mipi_dsi_host_ops;
-	dsi->dsi_host.dev = dev;
-	ret = mipi_dsi_host_register(&dsi->dsi_host);
-	if (ret) {
-		DRM_DEV_ERROR(dev, "Failed to register MIPI host: %d\n", ret);
-		goto err_cleanup;
-	}
-
-	if (!dsi->panel) {
-		ret = -EPROBE_DEFER;
-		goto err_mipi_dsi_host;
-	}
-
-	dev_set_drvdata(dev, dsi);
-	pm_runtime_enable(dev);
-	return 0;
-
-err_mipi_dsi_host:
-	mipi_dsi_host_unregister(&dsi->dsi_host);
-err_cleanup:
-	dsi->connector.funcs->destroy(&dsi->connector);
-	dsi->encoder.funcs->destroy(&dsi->encoder);
-err_pllref:
-	clk_disable_unprepare(dsi->pllref_clk);
-	return ret;
-}
-
-static void dw_mipi_dsi_unbind(struct device *dev, struct device *master,
-			       void *data)
-{
-	struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
-
-	mipi_dsi_host_unregister(&dsi->dsi_host);
-	pm_runtime_disable(dev);
-
-	dsi->connector.funcs->destroy(&dsi->connector);
-	dsi->encoder.funcs->destroy(&dsi->encoder);
-
-	clk_disable_unprepare(dsi->pllref_clk);
-}
-
-static const struct component_ops dw_mipi_dsi_ops = {
-	.bind	= dw_mipi_dsi_bind,
-	.unbind	= dw_mipi_dsi_unbind,
-};
-
-static int dw_mipi_dsi_probe(struct platform_device *pdev)
-{
-	return component_add(&pdev->dev, &dw_mipi_dsi_ops);
-}
-
-static int dw_mipi_dsi_remove(struct platform_device *pdev)
-{
-	component_del(&pdev->dev, &dw_mipi_dsi_ops);
-	return 0;
-}
-
-struct platform_driver dw_mipi_dsi_driver = {
-	.probe		= dw_mipi_dsi_probe,
-	.remove		= dw_mipi_dsi_remove,
-	.driver		= {
-		.of_match_table = dw_mipi_dsi_dt_ids,
-		.name	= DRIVER_NAME,
-	},
-};
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index f814d37b1db2..623cce77ebae 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -477,7 +477,7 @@ static int __init rockchip_drm_init(void)
 	ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP);
 	ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver,
 				CONFIG_ROCKCHIP_DW_HDMI);
-	ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_driver,
+	ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver,
 				CONFIG_ROCKCHIP_DW_MIPI_DSI);
 	ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI);
 
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index 3a6ebfc26036..96bb4ca8febf 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -67,7 +67,7 @@ int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout);
 
 extern struct platform_driver cdn_dp_driver;
 extern struct platform_driver dw_hdmi_rockchip_pltfm_driver;
-extern struct platform_driver dw_mipi_dsi_driver;
+extern struct platform_driver dw_mipi_dsi_rockchip_driver;
 extern struct platform_driver inno_hdmi_driver;
 extern struct platform_driver rockchip_dp_driver;
 extern struct platform_driver rockchip_lvds_driver;
-- 
2.17.0

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

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

* [PATCH v2 6/8] drm/dsi: add helper function to find the second host in a dual-dsi setup
  2018-06-18 10:27 [PATCH v2 0/8] drm/rockchip: migrate to common dw-mipi-dsi bridge and dual-dsi Heiko Stuebner
                   ` (4 preceding siblings ...)
  2018-06-18 10:28 ` [PATCH v2 5/8] drm/rockchip: dsi: migrate to use dw-mipi-dsi bridge driver Heiko Stuebner
@ 2018-06-18 10:28 ` Heiko Stuebner
  2018-07-03 15:06   ` Andrzej Hajda
  2018-06-18 10:28 ` [PATCH v2 7/8] drm/bridge/synopsys: dsi: add dual-dsi support Heiko Stuebner
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 26+ messages in thread
From: Heiko Stuebner @ 2018-06-18 10:28 UTC (permalink / raw)
  To: dri-devel
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, philippe.cornu,
	yannick.fertre, linux-rockchip, nickey.yang, robh+dt,
	thierry.reding, laurent.pinchart, mka

From a specified output port of one dsi controller this function allows to
iterate over the list of registered dsi controllers trying to find a second
instance connected to the same display, like it is used in dual-dsi setups.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 drivers/gpu/drm/drm_mipi_dsi.c | 56 ++++++++++++++++++++++++++++++++++
 include/drm/drm_mipi_dsi.h     |  2 ++
 2 files changed, 58 insertions(+)

diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index bc73b7f5b9fc..0c3c9c7aa3b8 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -30,6 +30,7 @@
 #include <linux/device.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
+#include <linux/of_graph.h>
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
 
@@ -282,6 +283,61 @@ struct mipi_dsi_host *of_find_mipi_dsi_host_by_node(struct device_node *node)
 }
 EXPORT_SYMBOL(of_find_mipi_dsi_host_by_node);
 
+struct device_node *of_mipi_dsi_find_second_host(struct device_node *first_np,
+						 int port, int endpoint)
+{
+	struct mipi_dsi_host *first, *host;
+	struct device_node *remote, *np, *second_np = NULL;
+	int num = 0;
+
+	first = of_find_mipi_dsi_host_by_node(first_np);
+	if (!first) {
+		pr_err("no dsi-host for node %s\n", first_np->full_name);
+		return ERR_PTR(-ENODEV);
+	}
+
+	/* output-node of the known dsi-host */
+	remote = of_graph_get_remote_node(first_np, port, endpoint);
+	if (!remote) {
+		dev_err(first->dev, "no output node found\n");
+		return ERR_PTR(-ENODEV);
+	}
+
+	mutex_lock(&host_lock);
+
+	list_for_each_entry(host, &host_list, list) {
+		np = of_graph_get_remote_node(host->dev->of_node,
+					      port, endpoint);
+
+		/* found a host connected to this panel */
+		if (np == remote)
+			num++;
+
+		/* found one second host */
+		if (host->dev->of_node != first_np)
+			second_np = host->dev->of_node;
+
+		of_node_put(np);
+	}
+
+	/* of_node_get the node under host_lock */
+	if (num == 2)
+		of_node_get(second_np);
+
+	mutex_unlock(&host_lock);
+
+	of_node_put(remote);
+
+	if (num > 2) {
+		dev_err(first->dev,
+			      "too many DSI links for output: %d links\n", num);
+		return ERR_PTR(-EINVAL);
+	}
+
+	return second_np;
+}
+EXPORT_SYMBOL(of_mipi_dsi_find_second_host);
+
 int mipi_dsi_host_register(struct mipi_dsi_host *host)
 {
 	struct device_node *node;
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index 4fef19064b0f..89532ae69c91 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -107,6 +107,8 @@ struct mipi_dsi_host {
 int mipi_dsi_host_register(struct mipi_dsi_host *host);
 void mipi_dsi_host_unregister(struct mipi_dsi_host *host);
 struct mipi_dsi_host *of_find_mipi_dsi_host_by_node(struct device_node *node);
+struct device_node *of_mipi_dsi_find_second_host(struct device_node *first_np,
+						 int port, int endpoint);
 
 /* DSI mode flags */
 
-- 
2.17.0

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

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

* [PATCH v2 7/8] drm/bridge/synopsys: dsi: add dual-dsi support
  2018-06-18 10:27 [PATCH v2 0/8] drm/rockchip: migrate to common dw-mipi-dsi bridge and dual-dsi Heiko Stuebner
                   ` (5 preceding siblings ...)
  2018-06-18 10:28 ` [PATCH v2 6/8] drm/dsi: add helper function to find the second host in a dual-dsi setup Heiko Stuebner
@ 2018-06-18 10:28 ` Heiko Stuebner
  2018-07-03 11:57   ` Philippe CORNU
  2018-07-03 17:07   ` Andrzej Hajda
  2018-06-18 10:28 ` [PATCH v2 8/8] drm/rockchip: dsi: add dual mipi support Heiko Stuebner
  2018-07-03 12:05 ` [PATCH v2 0/8] drm/rockchip: migrate to common dw-mipi-dsi bridge and dual-dsi Philippe CORNU
  8 siblings, 2 replies; 26+ messages in thread
From: Heiko Stuebner @ 2018-06-18 10:28 UTC (permalink / raw)
  To: dri-devel
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, philippe.cornu,
	yannick.fertre, linux-rockchip, nickey.yang, robh+dt,
	thierry.reding, laurent.pinchart, mka

From: Nickey Yang <nickey.yang@rock-chips.com>

Allow to also drive a slave dw-mipi-dsi controller in a dual-dsi
setup. This will require additional implementation-specific
code to look up the slave instance and do specific setup.
Also will probably need code in the specific crtcs as dual-dsi
does not equal two separate dsi outputs.

To activate, the implementation-specific code should set the slave
using dw_mipi_dsi_set_slave() before calling __dw_mipi_dsi_bind().

v2:
- expect real interface number of lanes
- keep links to both master and slave

Signed-off-by: Nickey Yang <nickey.yang@rock-chips.com>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 93 +++++++++++++++++--
 include/drm/bridge/dw_mipi_dsi.h              |  1 +
 2 files changed, 86 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
index bd503f000ed5..6a345d1dde25 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
@@ -230,9 +230,20 @@ struct dw_mipi_dsi {
 	u32 format;
 	unsigned long mode_flags;
 
+	struct dw_mipi_dsi *master; /* dual-dsi master ptr */
+	struct dw_mipi_dsi *slave; /* dual-dsi slave ptr */
+
 	const struct dw_mipi_dsi_plat_data *plat_data;
 };
 
+/*
+ * Check if either a link to a master or slave is present
+ */
+static inline bool dw_mipi_is_dual_mode(struct dw_mipi_dsi *dsi)
+{
+	return dsi->slave || dsi->master;
+}
+
 /*
  * The controller should generate 2 frames before
  * preparing the peripheral.
@@ -273,18 +284,26 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
 	struct drm_bridge *bridge;
 	struct drm_panel *panel;
 	int ret;
+	int lanes = device->lanes;
 
-	if (device->lanes > dsi->plat_data->max_data_lanes) {
+	if (lanes > dsi->plat_data->max_data_lanes) {
 		dev_err(dsi->dev, "the number of data lanes(%u) is too many\n",
 			device->lanes);
 		return -EINVAL;
 	}
 
-	dsi->lanes = device->lanes;
+	dsi->lanes = lanes;
 	dsi->channel = device->channel;
 	dsi->format = device->format;
 	dsi->mode_flags = device->mode_flags;
 
+	if (dsi->slave) {
+		dsi->slave->lanes = dsi->lanes;
+		dsi->slave->channel = dsi->channel;
+		dsi->slave->format = dsi->format;
+		dsi->slave->mode_flags = dsi->mode_flags;
+	}
+
 	ret = drm_of_find_panel_or_bridge(host->dev->of_node, 1, 0,
 					  &panel, &bridge);
 	if (ret)
@@ -441,10 +460,17 @@ static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host,
 	}
 
 	dw_mipi_message_config(dsi, msg);
+	if (dsi->slave)
+		dw_mipi_message_config(dsi->slave, msg);
 
 	ret = dw_mipi_dsi_write(dsi, &packet);
 	if (ret)
 		return ret;
+	if (dsi->slave) {
+		ret = dw_mipi_dsi_write(dsi->slave, &packet);
+		if (ret)
+			return ret;
+	}
 
 	if (msg->rx_buf && msg->rx_len) {
 		ret = dw_mipi_dsi_read(dsi, msg);
@@ -583,7 +609,15 @@ static void dw_mipi_dsi_video_packet_config(struct dw_mipi_dsi *dsi,
 	 * DSI_VNPCR.NPSIZE... especially because this driver supports
 	 * non-burst video modes, see dw_mipi_dsi_video_mode_config()...
 	 */
-	dsi_write(dsi, DSI_VID_PKT_SIZE, VID_PKT_SIZE(mode->hdisplay));
+
+	int pkt_size;
+
+	if (dw_mipi_is_dual_mode(dsi))
+		pkt_size = VID_PKT_SIZE(mode->hdisplay / 2);
+	else
+		pkt_size = VID_PKT_SIZE(mode->hdisplay);
+
+	dsi_write(dsi, DSI_VID_PKT_SIZE, pkt_size);
 }
 
 static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi)
@@ -756,23 +790,39 @@ static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge)
 	dsi->panel_bridge->funcs->post_disable(dsi->panel_bridge);
 
 	dw_mipi_dsi_disable(dsi);
+	if (dsi->slave) {
+		dw_mipi_dsi_disable(dsi->slave);
+		clk_disable_unprepare(dsi->slave->pclk);
+		pm_runtime_put(dsi->slave->dev);
+	}
+
 	clk_disable_unprepare(dsi->pclk);
 	pm_runtime_put(dsi->dev);
 }
 
-static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
-					struct drm_display_mode *mode,
-					struct drm_display_mode *adjusted_mode)
+static unsigned int dw_mipi_dsi_get_lanes(struct dw_mipi_dsi *dsi)
+{
+	if (dsi->master)
+		return dsi->master->lanes + dsi->lanes;
+
+	if (dsi->slave)
+		return dsi->lanes + dsi->slave->lanes;
+
+	return dsi->lanes;
+}
+
+static void dw_mipi_dsi_mode_set(struct dw_mipi_dsi *dsi,
+				struct drm_display_mode *adjusted_mode)
 {
-	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
 	const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops;
 	void *priv_data = dsi->plat_data->priv_data;
 	int ret;
+	u32 lanes = dw_mipi_dsi_get_lanes(dsi);
 
 	clk_prepare_enable(dsi->pclk);
 
 	ret = phy_ops->get_lane_mbps(priv_data, adjusted_mode, dsi->mode_flags,
-				     dsi->lanes, dsi->format, &dsi->lane_mbps);
+				     lanes, dsi->format, &dsi->lane_mbps);
 	if (ret)
 		DRM_DEBUG_DRIVER("Phy get_lane_mbps() failed\n");
 
@@ -804,12 +854,25 @@ static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
 	dw_mipi_dsi_set_mode(dsi, 0);
 }
 
+static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
+					struct drm_display_mode *mode,
+					struct drm_display_mode *adjusted_mode)
+{
+	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
+
+	dw_mipi_dsi_mode_set(dsi, adjusted_mode);
+	if (dsi->slave)
+		dw_mipi_dsi_mode_set(dsi->slave, adjusted_mode);
+}
+
 static void dw_mipi_dsi_bridge_enable(struct drm_bridge *bridge)
 {
 	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
 
 	/* Switch to video mode for panel-bridge enable & panel enable */
 	dw_mipi_dsi_set_mode(dsi, MIPI_DSI_MODE_VIDEO);
+	if (dsi->slave)
+		dw_mipi_dsi_set_mode(dsi->slave, MIPI_DSI_MODE_VIDEO);
 }
 
 static enum drm_mode_status
@@ -949,6 +1012,20 @@ static void __dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi)
 	pm_runtime_disable(dsi->dev);
 }
 
+void dw_mipi_dsi_set_slave(struct dw_mipi_dsi *dsi, struct dw_mipi_dsi *slave)
+{
+	/* introduce controllers to each other */
+	dsi->slave = slave;
+	dsi->slave->master = dsi;
+
+	/* migrate settings for already attached displays */
+	dsi->slave->lanes = dsi->lanes;
+	dsi->slave->channel = dsi->channel;
+	dsi->slave->format = dsi->format;
+	dsi->slave->mode_flags = dsi->mode_flags;
+}
+EXPORT_SYMBOL_GPL(dw_mipi_dsi_set_slave);
+
 /*
  * Probe/remove API, used from platforms based on the DRM bridge API.
  */
diff --git a/include/drm/bridge/dw_mipi_dsi.h b/include/drm/bridge/dw_mipi_dsi.h
index 6d7f8eb5d9f2..5fd997cdf281 100644
--- a/include/drm/bridge/dw_mipi_dsi.h
+++ b/include/drm/bridge/dw_mipi_dsi.h
@@ -37,5 +37,6 @@ struct dw_mipi_dsi *dw_mipi_dsi_probe(struct platform_device *pdev,
 void dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi);
 int dw_mipi_dsi_bind(struct dw_mipi_dsi *dsi, struct drm_encoder *encoder);
 void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi);
+void dw_mipi_dsi_set_slave(struct dw_mipi_dsi *dsi, struct dw_mipi_dsi *slave);
 
 #endif /* __DW_MIPI_DSI__ */
-- 
2.17.0

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

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

* [PATCH v2 8/8] drm/rockchip: dsi: add dual mipi support
  2018-06-18 10:27 [PATCH v2 0/8] drm/rockchip: migrate to common dw-mipi-dsi bridge and dual-dsi Heiko Stuebner
                   ` (6 preceding siblings ...)
  2018-06-18 10:28 ` [PATCH v2 7/8] drm/bridge/synopsys: dsi: add dual-dsi support Heiko Stuebner
@ 2018-06-18 10:28 ` Heiko Stuebner
  2018-07-03 12:05 ` [PATCH v2 0/8] drm/rockchip: migrate to common dw-mipi-dsi bridge and dual-dsi Philippe CORNU
  8 siblings, 0 replies; 26+ messages in thread
From: Heiko Stuebner @ 2018-06-18 10:28 UTC (permalink / raw)
  To: dri-devel
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, philippe.cornu,
	yannick.fertre, linux-rockchip, nickey.yang, robh+dt,
	thierry.reding, laurent.pinchart, mka

Add the Rockchip-sepcific dual-dsi setup and hook it into the VOP as well.
As described in the general dual-dsi devicetree binding, the panel should
define two input ports and point each of them to one of the used dsi-
controllers, as well as declare one of them as clock-master.
This is used to determine the dual-dsi state and get access to both
controller instances.

Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 .../gpu/drm/rockchip/dw-mipi-dsi-rockchip.c   | 67 ++++++++++++++++++-
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h   |  1 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c   |  3 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h   |  4 ++
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c   |  1 +
 5 files changed, 75 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
index 12e4dacc7970..3382ad5a1b0d 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
@@ -218,6 +218,10 @@ struct dw_mipi_dsi_rockchip {
 	struct clk *grf_clk;
 	struct clk *phy_cfg_clk;
 
+	/* dual-channel */
+	bool is_slave;
+	struct dw_mipi_dsi_rockchip *slave;
+
 	unsigned int lane_mbps; /* per lane */
 	u16 input_div;
 	u16 feedback_div;
@@ -604,6 +608,8 @@ static void dw_mipi_dsi_encoder_mode_set(struct drm_encoder *encoder,
 	}
 
 	dw_mipi_dsi_rockchip_config(dsi, mux);
+	if (dsi->slave)
+		dw_mipi_dsi_rockchip_config(dsi->slave, mux);
 
 	clk_disable_unprepare(dsi->grf_clk);
 }
@@ -632,6 +638,8 @@ dw_mipi_dsi_encoder_atomic_check(struct drm_encoder *encoder,
 	}
 
 	s->output_type = DRM_MODE_CONNECTOR_DSI;
+	if (dsi->slave)
+		s->output_flags = ROCKCHIP_OUTPUT_DSI_DUAL;
 
 	return 0;
 }
@@ -641,6 +649,8 @@ static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder)
 	struct dw_mipi_dsi_rockchip *dsi = to_dsi(encoder);
 
 	pm_runtime_put(dsi->dev);
+	if (dsi->slave)
+		pm_runtime_put(dsi->slave->dev);
 }
 
 static const struct drm_encoder_helper_funcs
@@ -681,18 +691,70 @@ static int dw_mipi_dsi_rockchip_bind(struct device *dev,
 {
 	struct dw_mipi_dsi_rockchip *dsi = dev_get_drvdata(dev);
 	struct drm_device *drm_dev = data;
+	struct device_node *second_np;
 	struct drm_bridge *bridge;
 	struct drm_panel *panel;
+	bool master1, master2;
 	int ret;
 
 	/*
-	 * Handle probe-deferrals due to missing display.
+	 * At this point both DSIs (if in use) should have probed and found
+	 * any connected displays or bridges.
+	 * This also takes care of handling possible probe-deferrals.
 	 */
 	ret = drm_of_find_panel_or_bridge(dsi->dev->of_node, 1, 0,
 					  &panel, &bridge);
 	if (ret)
 		return ret;
 
+	second_np = of_mipi_dsi_find_second_host(dsi->dev->of_node, 1, 0);
+	if (IS_ERR(second_np))
+		return PTR_ERR(second_np);
+
+	if (second_np) {
+		struct platform_device *pdev;
+
+		master1 = of_property_read_bool(dsi->dev->of_node,
+						"clock-master");
+		master2 = of_property_read_bool(second_np, "clock-master");
+
+		if (master1 && master2) {
+			DRM_DEV_ERROR(dsi->dev, "only one clock-master allowed\n");
+			of_node_put(second_np);
+			return -EINVAL;
+		}
+
+		if (!master1 && !master2) {
+			DRM_DEV_ERROR(dsi->dev, "no clock-master defined\n");
+			of_node_put(second_np);
+			return -EINVAL;
+		}
+
+		/* we are the slave in dual-DSI */
+		if (!master1) {
+			dsi->is_slave = true;
+			of_node_put(second_np);
+			return 0;
+		}
+
+		pdev = of_find_device_by_node(second_np);
+		if (!pdev) {
+			DRM_DEV_ERROR(dev, "could not find slave controller\n");
+			return -ENODEV;
+		}
+
+		dsi->slave = platform_get_drvdata(pdev);
+		if (!dsi->slave) {
+			DRM_DEV_ERROR(dev, "could not get slaves platform-data\n");
+			return -ENODEV;
+		}
+
+		dsi->slave->is_slave = true;
+		dw_mipi_dsi_set_slave(dsi->dmd, dsi->slave->dmd);
+
+		of_node_put(second_np);
+	}
+
 	ret = rockchip_dsi_drm_create_encoder(dsi, drm_dev);
 	if (ret) {
 		DRM_DEV_ERROR(dev, "Failed to create drm encoder\n");
@@ -714,6 +776,9 @@ static void dw_mipi_dsi_rockchip_unbind(struct device *dev,
 {
 	struct dw_mipi_dsi_rockchip *dsi = dev_get_drvdata(dev);
 
+	if (dsi->is_slave)
+		return;
+
 	dw_mipi_dsi_unbind(dsi->dmd);
 }
 
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index 96bb4ca8febf..2e6d2d4d1b65 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -37,6 +37,7 @@ struct rockchip_crtc_state {
 	int output_type;
 	int output_mode;
 	int output_bpc;
+	int output_flags;
 };
 #define to_rockchip_crtc_state(s) \
 		container_of(s, struct rockchip_crtc_state, base)
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index d105e984cf09..9b2fa5e0eade 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -916,6 +916,7 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
 	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) ?
 		   BIT(VSYNC_POSITIVE) : 0;
 	VOP_REG_SET(vop, output, pin_pol, pin_pol);
+	VOP_REG_SET(vop, output, mipi_dual_channel_en, 0);
 
 	switch (s->output_type) {
 	case DRM_MODE_CONNECTOR_LVDS:
@@ -933,6 +934,8 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
 	case DRM_MODE_CONNECTOR_DSI:
 		VOP_REG_SET(vop, output, mipi_pin_pol, pin_pol);
 		VOP_REG_SET(vop, output, mipi_en, 1);
+		VOP_REG_SET(vop, output, mipi_dual_channel_en,
+			    !!(s->output_flags & ROCKCHIP_OUTPUT_DSI_DUAL));
 		break;
 	case DRM_MODE_CONNECTOR_DisplayPort:
 		pin_pol &= ~BIT(DCLK_INVERT);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index 084acdd0019a..63c815edeaac 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -60,6 +60,7 @@ struct vop_output {
 	struct vop_reg edp_en;
 	struct vop_reg hdmi_en;
 	struct vop_reg mipi_en;
+	struct vop_reg mipi_dual_channel_en;
 	struct vop_reg rgb_en;
 };
 
@@ -213,6 +214,9 @@ struct vop_data {
 /* for use special outface */
 #define ROCKCHIP_OUT_MODE_AAAA	15
 
+/* output flags */
+#define ROCKCHIP_OUTPUT_DSI_DUAL	BIT(0)
+
 enum alpha_mode {
 	ALPHA_STRAIGHT,
 	ALPHA_INVERSE,
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 08023d3ecb76..56d83f1c2599 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -421,6 +421,7 @@ static const struct vop_output rk3399_output = {
 	.hdmi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 13),
 	.edp_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 14),
 	.mipi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 15),
+	.mipi_dual_channel_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 3),
 };
 
 static const struct vop_data rk3399_vop_big = {
-- 
2.17.0

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

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

* Re: [PATCH v2 4/8] dt-bindings: display: rockchip: update DSI controller
  2018-06-18 10:28 ` [PATCH v2 4/8] dt-bindings: display: rockchip: update DSI controller Heiko Stuebner
@ 2018-06-18 10:30   ` Heiko Stuebner
  0 siblings, 0 replies; 26+ messages in thread
From: Heiko Stuebner @ 2018-06-18 10:30 UTC (permalink / raw)
  To: dri-devel
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, philippe.cornu,
	yannick.fertre, linux-rockchip, nickey.yang, robh+dt,
	thierry.reding, laurent.pinchart, mka

Am Montag, 18. Juni 2018, 12:28:02 CEST schrieb Heiko Stuebner:
> From: Nickey Yang <nickey.yang@rock-chips.com>
> 
> This patch update describe panel/port links, including
> unit addresses in documentation of device tree bindings
> for the rockchip DSI controller based on the Synopsys
> DesignWare MIPI DSI host controller.
> 
> Signed-off-by: Nickey Yang <nickey.yang@rock-chips.com>
> Reviewed-by: Brian Norris <briannorris@chromium.org>
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>

forgot to carry over the
Reviewed-by: Rob Herring <robh@kernel.org>

from v1.


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

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

* Re: [PATCH v2 1/8] drm/bridge/synopsys: dsi: move mipi_dsi_host_unregister to __dw_mipi_dsi_remove
  2018-06-18 10:27 ` [PATCH v2 1/8] drm/bridge/synopsys: dsi: move mipi_dsi_host_unregister to __dw_mipi_dsi_remove Heiko Stuebner
@ 2018-07-03 10:40   ` Andrzej Hajda
  2018-07-03 11:58   ` Philippe CORNU
  1 sibling, 0 replies; 26+ messages in thread
From: Andrzej Hajda @ 2018-07-03 10:40 UTC (permalink / raw)
  To: Heiko Stuebner, dri-devel
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, philippe.cornu,
	yannick.fertre, linux-rockchip, nickey.yang, robh+dt,
	thierry.reding, laurent.pinchart, mka

On 18.06.2018 12:27, Heiko Stuebner wrote:
> Right now the host is only unregistered when the driver is used via the
> bridge api and not via the component api, leading to the host staying
> registered in cases like probe deferral.
>
> So move the host unregister to the general remove function, so that it
> gets cleaned up in all cases.
>
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>

I have lurked into the only current user of the bridge (stm), and I am
little bit confused how does it work, but this change looks OK.

Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>

 --
Regards
Andrzej
> ---
>  drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> index fd7999642cf8..07cde255cab2 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> @@ -941,6 +941,8 @@ __dw_mipi_dsi_probe(struct platform_device *pdev,
>  
>  static void __dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi)
>  {
> +	mipi_dsi_host_unregister(&dsi->dsi_host);
> +
>  	pm_runtime_disable(dsi->dev);
>  }
>  
> @@ -957,8 +959,6 @@ EXPORT_SYMBOL_GPL(dw_mipi_dsi_probe);
>  
>  void dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi)
>  {
> -	mipi_dsi_host_unregister(&dsi->dsi_host);
> -
>  	__dw_mipi_dsi_remove(dsi);
>  }
>  EXPORT_SYMBOL_GPL(dw_mipi_dsi_remove);


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

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

* Re: [PATCH v2 7/8] drm/bridge/synopsys: dsi: add dual-dsi support
  2018-06-18 10:28 ` [PATCH v2 7/8] drm/bridge/synopsys: dsi: add dual-dsi support Heiko Stuebner
@ 2018-07-03 11:57   ` Philippe CORNU
  2018-07-03 17:07   ` Andrzej Hajda
  1 sibling, 0 replies; 26+ messages in thread
From: Philippe CORNU @ 2018-07-03 11:57 UTC (permalink / raw)
  To: Heiko Stuebner, dri-devel
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, Yannick FERTRE,
	linux-rockchip, nickey.yang, robh+dt, thierry.reding,
	laurent.pinchart, mka

Hi Heiko,
Thank you for your patch,

On 06/18/2018 12:28 PM, Heiko Stuebner wrote:
> From: Nickey Yang <nickey.yang@rock-chips.com>
> 
> Allow to also drive a slave dw-mipi-dsi controller in a dual-dsi
> setup. This will require additional implementation-specific
> code to look up the slave instance and do specific setup.
> Also will probably need code in the specific crtcs as dual-dsi
> does not equal two separate dsi outputs.
> 
> To activate, the implementation-specific code should set the slave
> using dw_mipi_dsi_set_slave() before calling __dw_mipi_dsi_bind().
> 
> v2:
> - expect real interface number of lanes
> - keep links to both master and slave
> 
> Signed-off-by: Nickey Yang <nickey.yang@rock-chips.com>
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> ---
>   drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 93 +++++++++++++++++--
>   include/drm/bridge/dw_mipi_dsi.h              |  1 +
>   2 files changed, 86 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> index bd503f000ed5..6a345d1dde25 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> @@ -230,9 +230,20 @@ struct dw_mipi_dsi {
>   	u32 format;
>   	unsigned long mode_flags;
>   
> +	struct dw_mipi_dsi *master; /* dual-dsi master ptr */
> +	struct dw_mipi_dsi *slave; /* dual-dsi slave ptr */
> +
>   	const struct dw_mipi_dsi_plat_data *plat_data;
>   };
>   
> +/*
> + * Check if either a link to a master or slave is present
> + */
> +static inline bool dw_mipi_is_dual_mode(struct dw_mipi_dsi *dsi)
> +{
> +	return dsi->slave || dsi->master;
> +}
> +
>   /*
>    * The controller should generate 2 frames before
>    * preparing the peripheral.
> @@ -273,18 +284,26 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
>   	struct drm_bridge *bridge;
>   	struct drm_panel *panel;
>   	int ret;
> +	int lanes = device->lanes;

I do not see a big interest here in adding the new var "lanes", I 
suggest to remove it or wait for more comments (not a strong opposition 
on my side:) just a preference).

>   
> -	if (device->lanes > dsi->plat_data->max_data_lanes) {
> +	if (lanes > dsi->plat_data->max_data_lanes) {
>   		dev_err(dsi->dev, "the number of data lanes(%u) is too many\n",
>   			device->lanes);

please use "lanes" has you have created it ;-) or keep it as it is if 
you decide to remove the var lanes :)


>   		return -EINVAL;
>   	}
>   
> -	dsi->lanes = device->lanes;
> +	dsi->lanes = lanes;
>   	dsi->channel = device->channel;
>   	dsi->format = device->format;
>   	dsi->mode_flags = device->mode_flags;
>   
> +	if (dsi->slave) {
> +		dsi->slave->lanes = dsi->lanes;
> +		dsi->slave->channel = dsi->channel;
> +		dsi->slave->format = dsi->format;
> +		dsi->slave->mode_flags = dsi->mode_flags;
> +	}
> +
>   	ret = drm_of_find_panel_or_bridge(host->dev->of_node, 1, 0,
>   					  &panel, &bridge);
>   	if (ret)
> @@ -441,10 +460,17 @@ static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host,
>   	}
>   
>   	dw_mipi_message_config(dsi, msg);
> +	if (dsi->slave)
> +		dw_mipi_message_config(dsi->slave, msg);
>   
>   	ret = dw_mipi_dsi_write(dsi, &packet);
>   	if (ret)
>   		return ret;
> +	if (dsi->slave) {
> +		ret = dw_mipi_dsi_write(dsi->slave, &packet);
> +		if (ret)
> +			return ret;
> +	}
>   
>   	if (msg->rx_buf && msg->rx_len) {
>   		ret = dw_mipi_dsi_read(dsi, msg);
> @@ -583,7 +609,15 @@ static void dw_mipi_dsi_video_packet_config(struct dw_mipi_dsi *dsi,
>   	 * DSI_VNPCR.NPSIZE... especially because this driver supports
>   	 * non-burst video modes, see dw_mipi_dsi_video_mode_config()...
>   	 */
> -	dsi_write(dsi, DSI_VID_PKT_SIZE, VID_PKT_SIZE(mode->hdisplay));
> +
> +	int pkt_size;

I do not see any interest in adding this pkt_size, you can directly do 
the dsi_write (below) and remove minimum 4 lines in your patch then ... 
just my opinion : )

> +
> +	if (dw_mipi_is_dual_mode(dsi))
> +		pkt_size = VID_PKT_SIZE(mode->hdisplay / 2);
> +	else
> +		pkt_size = VID_PKT_SIZE(mode->hdisplay);
> +
> +	dsi_write(dsi, DSI_VID_PKT_SIZE, pkt_size);
>   }
>   
>   static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi)
> @@ -756,23 +790,39 @@ static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge)
>   	dsi->panel_bridge->funcs->post_disable(dsi->panel_bridge);
>   
>   	dw_mipi_dsi_disable(dsi);
Maybe you should move dw_mipi_dsi_disable(dsi) call after the if 
(dsi->slave)...

> +	if (dsi->slave) {
> +		dw_mipi_dsi_disable(dsi->slave);
> +		clk_disable_unprepare(dsi->slave->pclk);
> +		pm_runtime_put(dsi->slave->dev);
> +	}
> +
>   	clk_disable_unprepare(dsi->pclk);
>   	pm_runtime_put(dsi->dev);
>   }
>   
> -static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
> -					struct drm_display_mode *mode,
> -					struct drm_display_mode *adjusted_mode)
> +static unsigned int dw_mipi_dsi_get_lanes(struct dw_mipi_dsi *dsi)
> +{
> +	if (dsi->master)
> +		return dsi->master->lanes + dsi->lanes;
> +
> +	if (dsi->slave)
> +		return dsi->lanes + dsi->slave->lanes;
maybe a short explanation for master, slave and single-dsi 3 cases could 
be nice here

> +
> +	return dsi->lanes;
> +}
> +
> +static void dw_mipi_dsi_mode_set(struct dw_mipi_dsi *dsi,
> +				struct drm_display_mode *adjusted_mode)
>   {
> -	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
>   	const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops;
>   	void *priv_data = dsi->plat_data->priv_data;
>   	int ret;
> +	u32 lanes = dw_mipi_dsi_get_lanes(dsi);
>   
>   	clk_prepare_enable(dsi->pclk);
>   
>   	ret = phy_ops->get_lane_mbps(priv_data, adjusted_mode, dsi->mode_flags,
> -				     dsi->lanes, dsi->format, &dsi->lane_mbps);
> +				     lanes, dsi->format, &dsi->lane_mbps);
>   	if (ret)
>   		DRM_DEBUG_DRIVER("Phy get_lane_mbps() failed\n");
>   
> @@ -804,12 +854,25 @@ static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
>   	dw_mipi_dsi_set_mode(dsi, 0);
>   }
>   
> +static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
> +					struct drm_display_mode *mode,
> +					struct drm_display_mode *adjusted_mode)
> +{
> +	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
> +
> +	dw_mipi_dsi_mode_set(dsi, adjusted_mode);
> +	if (dsi->slave)
> +		dw_mipi_dsi_mode_set(dsi->slave, adjusted_mode);
> +}
> +
>   static void dw_mipi_dsi_bridge_enable(struct drm_bridge *bridge)
>   {
>   	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
>   
>   	/* Switch to video mode for panel-bridge enable & panel enable */
>   	dw_mipi_dsi_set_mode(dsi, MIPI_DSI_MODE_VIDEO);
> +	if (dsi->slave)
> +		dw_mipi_dsi_set_mode(dsi->slave, MIPI_DSI_MODE_VIDEO);
>   }
>   
>   static enum drm_mode_status
> @@ -949,6 +1012,20 @@ static void __dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi)
>   	pm_runtime_disable(dsi->dev);
>   }
>   
> +void dw_mipi_dsi_set_slave(struct dw_mipi_dsi *dsi, struct dw_mipi_dsi *slave)
> +{
> +	/* introduce controllers to each other */
> +	dsi->slave = slave;
> +	dsi->slave->master = dsi;
> +
> +	/* migrate settings for already attached displays */
> +	dsi->slave->lanes = dsi->lanes;
> +	dsi->slave->channel = dsi->channel;
> +	dsi->slave->format = dsi->format;
> +	dsi->slave->mode_flags = dsi->mode_flags;

I though it has been done earlier in dw_mipi_dsi_host_attach() function?

> +}
> +EXPORT_SYMBOL_GPL(dw_mipi_dsi_set_slave);
> +
>   /*
>    * Probe/remove API, used from platforms based on the DRM bridge API.
>    */
> diff --git a/include/drm/bridge/dw_mipi_dsi.h b/include/drm/bridge/dw_mipi_dsi.h
> index 6d7f8eb5d9f2..5fd997cdf281 100644
> --- a/include/drm/bridge/dw_mipi_dsi.h
> +++ b/include/drm/bridge/dw_mipi_dsi.h
> @@ -37,5 +37,6 @@ struct dw_mipi_dsi *dw_mipi_dsi_probe(struct platform_device *pdev,
>   void dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi);
>   int dw_mipi_dsi_bind(struct dw_mipi_dsi *dsi, struct drm_encoder *encoder);
>   void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi);
> +void dw_mipi_dsi_set_slave(struct dw_mipi_dsi *dsi, struct dw_mipi_dsi *slave);


Tested-by: Philippe Cornu <philippe.cornu@st.com> (only on stm32 with 
single dsi so dual-dsi has not been tested)

I wait for more comments & (minor) updates before giving my reviewed-by 
but you are close to have it : )

many thanks,
Philippe :-)

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

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

* Re: [PATCH v2 3/8] drm/bridge/synopsys: dsi: defer probing if panel not available in bridge-attach
  2018-06-18 10:28 ` [PATCH v2 3/8] drm/bridge/synopsys: dsi: defer probing if panel not available in bridge-attach Heiko Stuebner
@ 2018-07-03 11:57   ` Philippe CORNU
  2018-07-03 12:42   ` Andrzej Hajda
  1 sibling, 0 replies; 26+ messages in thread
From: Philippe CORNU @ 2018-07-03 11:57 UTC (permalink / raw)
  To: Heiko Stuebner, dri-devel
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, Yannick FERTRE,
	linux-rockchip, nickey.yang, robh+dt, thierry.reding,
	laurent.pinchart, mka

Hi Heiko,

On 06/18/2018 12:28 PM, Heiko Stuebner wrote:
> When the panel-driver is build as a module it currently fails hard as the
> panel cannot be probed directly:
> 
> dw_mipi_dsi_bind()
>    __dw_mipi_dsi_probe()
>      creates dsi bus
>      creates panel device
>      triggers panel module load
>      panel not probed (module not loaded or panel probe slow)
>    drm_bridge_attach
>      fails with -EINVAL due to empty panel_bridge
> 
> So emit a -EPROBE_DEFER in that case to give the driver another
> chance at getting the display later.
> 
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> ---
>   drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 3 +++
>   1 file changed, 3 insertions(+)
> 
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> index bb4aeca5c0f9..bd503f000ed5 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> @@ -835,6 +835,9 @@ static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge)
>   		return -ENODEV;
>   	}
>   
> +	if (!dsi->panel_bridge)
> +		return -EPROBE_DEFER;
> +

Thank you for your patch,

Reviewed-by: Philippe Cornu <philippe.cornu@st.com>
Tested-by: Philippe Cornu <philippe.cornu@st.com>

Philippe :-)

>   	/* Set the encoder type as caller does not know it */
>   	bridge->encoder->encoder_type = DRM_MODE_ENCODER_DSI;
>   
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH v2 1/8] drm/bridge/synopsys: dsi: move mipi_dsi_host_unregister to __dw_mipi_dsi_remove
  2018-06-18 10:27 ` [PATCH v2 1/8] drm/bridge/synopsys: dsi: move mipi_dsi_host_unregister to __dw_mipi_dsi_remove Heiko Stuebner
  2018-07-03 10:40   ` Andrzej Hajda
@ 2018-07-03 11:58   ` Philippe CORNU
  1 sibling, 0 replies; 26+ messages in thread
From: Philippe CORNU @ 2018-07-03 11:58 UTC (permalink / raw)
  To: Heiko Stuebner, dri-devel
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, Yannick FERTRE,
	linux-rockchip, nickey.yang, robh+dt, thierry.reding,
	laurent.pinchart, mka

Hi Heiko,


On 06/18/2018 12:27 PM, Heiko Stuebner wrote:
> Right now the host is only unregistered when the driver is used via the
> bridge api and not via the component api, leading to the host staying
> registered in cases like probe deferral.
> 
> So move the host unregister to the general remove function, so that it
> gets cleaned up in all cases.
> 
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> ---
>   drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 4 ++--
>   1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> index fd7999642cf8..07cde255cab2 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> @@ -941,6 +941,8 @@ __dw_mipi_dsi_probe(struct platform_device *pdev,
>   
>   static void __dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi)
>   {
> +	mipi_dsi_host_unregister(&dsi->dsi_host);
> +
>   	pm_runtime_disable(dsi->dev);
>   }
>   
> @@ -957,8 +959,6 @@ EXPORT_SYMBOL_GPL(dw_mipi_dsi_probe);
>   
>   void dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi)
>   {
> -	mipi_dsi_host_unregister(&dsi->dsi_host);
> -

Thank you for your patch,

Reviewed-by: Philippe Cornu <philippe.cornu@st.com>
Tested-by: Philippe Cornu <philippe.cornu@st.com>

Philippe :-)

>   	__dw_mipi_dsi_remove(dsi);
>   }
>   EXPORT_SYMBOL_GPL(dw_mipi_dsi_remove);
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH v2 0/8] drm/rockchip: migrate to common dw-mipi-dsi bridge and dual-dsi
  2018-06-18 10:27 [PATCH v2 0/8] drm/rockchip: migrate to common dw-mipi-dsi bridge and dual-dsi Heiko Stuebner
                   ` (7 preceding siblings ...)
  2018-06-18 10:28 ` [PATCH v2 8/8] drm/rockchip: dsi: add dual mipi support Heiko Stuebner
@ 2018-07-03 12:05 ` Philippe CORNU
  8 siblings, 0 replies; 26+ messages in thread
From: Philippe CORNU @ 2018-07-03 12:05 UTC (permalink / raw)
  To: Heiko Stuebner, dri-devel
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, Yannick FERTRE,
	linux-rockchip, nickey.yang, robh+dt, thierry.reding,
	laurent.pinchart, mka

Hi Heiko,

and many thanks for this serie.
I tested "partly" it successfully on stm32 (single-dsi, no bind/unbind).

Many thanks,
Philippe :-)


On 06/18/2018 12:27 PM, Heiko Stuebner wrote:
> The Rockchip DSI driver was separate till now, not using the common
> bridge driver that was introduced a bit later. So this series migrates
> over to use that common bridge driver and then also adds support for
> dual-dsi to both the bridge and Rockchip glue code.
> 
> The bridge-migration itself is based on Nickeys earlier v8
> work, but adapted to current kernels and with a new split between probe
> and bind, so that we do not create and drop the dsi-host on each deferred
> bind attempt.
> 
> changes in v2:
> - rebase against newer drm code (dsi-bridge+rockchip changes)
> - add SPDX header to new glue driver
> - expect regular interface lanes from panel (like 4) not the double number
>    Similar to tegra
> - keep links to both master and slave
> 
> 
> The dual-dsi setup follows the port description introduced by Archit [0],
> in that the panel defines two input ports that get connected to both
> dsi-controllers instances. So on Gru-Scarlett this looks for example
> like:
> 
> &mipi_dsi {
> 	status = "okay";
> 	clock-master;
> 
> 	ports {
> 		mipi_out: port@1 {
> 			reg = <1>;
> 
> 			mipi_out_panel: endpoint {
> 				remote-endpoint = <&mipi_in_panel>;
> 			};
> 		};
> 	};
> 
> 	mipi_panel: panel@0 {
> 		/* 2 different panels are used, compatibles are in dts files */
> 		reg = <0>;
> 		backlight = <&backlight>;
> 		enable-gpios = <&gpio4 25 GPIO_ACTIVE_HIGH>;
> 		pinctrl-names = "default";
> 		pinctrl-0 = <&display_rst_l>;
> 
> 		ports {
> 			#address-cells = <1>;
> 			#size-cells = <0>;
> 
> 			port@0 {
> 				reg = <0>;
> 
> 				mipi_in_panel: endpoint {
> 					remote-endpoint = <&mipi_out_panel>;
> 				};
> 			};
> 
> 			port@1 {
> 				reg = <1>;
> 
> 				mipi1_in_panel: endpoint@1 {
> 					remote-endpoint = <&mipi1_out_panel>;
> 				};
> 			};
> 		};
> 	};
> };
> 
> &mipi_dsi1 {
> 	status = "okay";
> 
> 	ports {
> 		mipi1_out: port@1 {
> 			reg = <1>;
> 
> 			mipi1_out_panel: endpoint {
> 				remote-endpoint = <&mipi1_in_panel>;
> 			};
> 		};
> 	};
> };
> 
> 
> The driver internal setup is pretty similar to what tegra does with
> its ganged-mode [1][2]. But here a new helper function allows to traverse
> the devicetree from one controller port through the panel to find
> another dsi-controller using that same panel. This way we don't need
> a special phandle-property to link the controllers together.
> 
> For the CRTC it is still one single display to handle, only with
> an additional switch that enables the dual-dsi output.
> 
> 
> For practical purposes it is possible to just pick half the series
> (till patch 5) to get the migration to the bridge driver first,
> so that we can get rid of the dw-dsi copy in the Rockchip driver.
> 
> But of course Acks / Reviews of the dsi-bridge changes would be needed.
> 
> 
> [0] https://patchwork.kernel.org/patch/10172381/
> [1] https://lkml.org/lkml/2014/8/5/396
> [2] https://patchwork.kernel.org/patch/5075161/
> 
> Heiko Stuebner (5):
>    drm/bridge/synopsys: dsi: move mipi_dsi_host_unregister to
>      __dw_mipi_dsi_remove
>    drm/bridge/synopsys: dsi: don't call __dw_mipi_dsi_probe from
>      dw_mipi_dsi_bind
>    drm/bridge/synopsys: dsi: defer probing if panel not available in
>      bridge-attach
>    drm/dsi: add helper function to find the second host in a dual-dsi
>      setup
>    drm/rockchip: dsi: add dual mipi support
> 
> Nickey Yang (3):
>    dt-bindings: display: rockchip: update DSI controller
>    drm/rockchip: dsi: migrate to use dw-mipi-dsi bridge driver
>    drm/bridge/synopsys: dsi: add dual-dsi support
> 
>   .../display/rockchip/dw_mipi_dsi_rockchip.txt |   23 +-
>   drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c |  115 +-
>   drivers/gpu/drm/drm_mipi_dsi.c                |   56 +
>   drivers/gpu/drm/rockchip/Kconfig              |    2 +-
>   drivers/gpu/drm/rockchip/Makefile             |    2 +-
>   .../gpu/drm/rockchip/dw-mipi-dsi-rockchip.c   |  992 ++++++++++++
>   drivers/gpu/drm/rockchip/dw-mipi-dsi.c        | 1349 -----------------
>   drivers/gpu/drm/rockchip/rockchip_drm_drv.c   |    2 +-
>   drivers/gpu/drm/rockchip/rockchip_drm_drv.h   |    3 +-
>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c   |    3 +
>   drivers/gpu/drm/rockchip/rockchip_drm_vop.h   |    4 +
>   drivers/gpu/drm/rockchip/rockchip_vop_reg.c   |    1 +
>   include/drm/bridge/dw_mipi_dsi.h              |    6 +-
>   include/drm/drm_mipi_dsi.h                    |    2 +
>   14 files changed, 1179 insertions(+), 1381 deletions(-)
>   create mode 100644 drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
>   delete mode 100644 drivers/gpu/drm/rockchip/dw-mipi-dsi.c
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH v2 2/8] drm/bridge/synopsys: dsi: don't call __dw_mipi_dsi_probe from dw_mipi_dsi_bind
  2018-06-18 10:28 ` [PATCH v2 2/8] drm/bridge/synopsys: dsi: don't call __dw_mipi_dsi_probe from dw_mipi_dsi_bind Heiko Stuebner
@ 2018-07-03 12:16   ` Andrzej Hajda
  2018-07-03 12:32     ` Heiko Stübner
  2018-07-04 12:23     ` Heiko Stuebner
  0 siblings, 2 replies; 26+ messages in thread
From: Andrzej Hajda @ 2018-07-03 12:16 UTC (permalink / raw)
  To: Heiko Stuebner, dri-devel
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, philippe.cornu,
	yannick.fertre, linux-rockchip, nickey.yang, robh+dt,
	thierry.reding, laurent.pinchart, mka

On 18.06.2018 12:28, Heiko Stuebner wrote:
> __dw_mipi_dsi_probe() does all the grabbing of resources and does it using
> devm-helpers. So this is happening on each try of master bringup possibly
> slowing down things a lot.
>
> Drivers using the component framework may instead want call dw_mipi_dsi_probe
> separately in their probe function setup resources early. That way the dsi
> bus also gets created earlier and also not recreated on each bind-try, so
> that attached panels can load their modules and be probed way before the
> bridge-attach in the bind call.
>
> So drop the call to __dw_mipi_dsi_probe and modify the function to take
> a struct dw_mipi_dsi instead of the platform-device.
>
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> ---
>  drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 15 +++------------
>  include/drm/bridge/dw_mipi_dsi.h              |  5 +----
>  2 files changed, 4 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> index 07cde255cab2..bb4aeca5c0f9 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> @@ -966,31 +966,22 @@ EXPORT_SYMBOL_GPL(dw_mipi_dsi_remove);
>  /*
>   * Bind/unbind API, used from platforms based on the component framework.
>   */
> -struct dw_mipi_dsi *
> -dw_mipi_dsi_bind(struct platform_device *pdev, struct drm_encoder *encoder,
> -		 const struct dw_mipi_dsi_plat_data *plat_data)
> +int dw_mipi_dsi_bind(struct dw_mipi_dsi *dsi, struct drm_encoder *encoder)
>  {
> -	struct dw_mipi_dsi *dsi;
>  	int ret;
>  
> -	dsi = __dw_mipi_dsi_probe(pdev, plat_data);
> -	if (IS_ERR(dsi))
> -		return dsi;
> -
>  	ret = drm_bridge_attach(encoder, &dsi->bridge, NULL);
>  	if (ret) {
> -		dw_mipi_dsi_remove(dsi);
>  		DRM_ERROR("Failed to initialize bridge with drm\n");
> -		return ERR_PTR(ret);
> +		return ret;
>  	}
>  
> -	return dsi;
> +	return ret;
>  }
>  EXPORT_SYMBOL_GPL(dw_mipi_dsi_bind);
>  
>  void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi)
>  {
> -	__dw_mipi_dsi_remove(dsi);
>  }
>  EXPORT_SYMBOL_GPL(dw_mipi_dsi_unbind);

Can't we just remove these two bind/unbind functions and call
drm_bridge_attach directly?

Regards
Andrzej

>  
> diff --git a/include/drm/bridge/dw_mipi_dsi.h b/include/drm/bridge/dw_mipi_dsi.h
> index d9c6d549f971..6d7f8eb5d9f2 100644
> --- a/include/drm/bridge/dw_mipi_dsi.h
> +++ b/include/drm/bridge/dw_mipi_dsi.h
> @@ -35,10 +35,7 @@ struct dw_mipi_dsi *dw_mipi_dsi_probe(struct platform_device *pdev,
>  				      const struct dw_mipi_dsi_plat_data
>  				      *plat_data);
>  void dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi);
> -struct dw_mipi_dsi *dw_mipi_dsi_bind(struct platform_device *pdev,
> -				     struct drm_encoder *encoder,
> -				     const struct dw_mipi_dsi_plat_data
> -				     *plat_data);
> +int dw_mipi_dsi_bind(struct dw_mipi_dsi *dsi, struct drm_encoder *encoder);
>  void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi);
>  
>  #endif /* __DW_MIPI_DSI__ */


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

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

* Re: [PATCH v2 2/8] drm/bridge/synopsys: dsi: don't call __dw_mipi_dsi_probe from dw_mipi_dsi_bind
  2018-07-03 12:16   ` Andrzej Hajda
@ 2018-07-03 12:32     ` Heiko Stübner
  2018-07-04 12:23     ` Heiko Stuebner
  1 sibling, 0 replies; 26+ messages in thread
From: Heiko Stübner @ 2018-07-03 12:32 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, philippe.cornu,
	dri-devel, yannick.fertre, linux-rockchip, nickey.yang, robh+dt,
	thierry.reding, laurent.pinchart, mka

Am Dienstag, 3. Juli 2018, 14:16:28 CEST schrieb Andrzej Hajda:
> On 18.06.2018 12:28, Heiko Stuebner wrote:
> > __dw_mipi_dsi_probe() does all the grabbing of resources and does it using
> > devm-helpers. So this is happening on each try of master bringup possibly
> > slowing down things a lot.
> > 
> > Drivers using the component framework may instead want call
> > dw_mipi_dsi_probe separately in their probe function setup resources
> > early. That way the dsi bus also gets created earlier and also not
> > recreated on each bind-try, so that attached panels can load their
> > modules and be probed way before the bridge-attach in the bind call.
> > 
> > So drop the call to __dw_mipi_dsi_probe and modify the function to take
> > a struct dw_mipi_dsi instead of the platform-device.
> > 
> > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> > ---
> > 
> >  drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 15 +++------------
> >  include/drm/bridge/dw_mipi_dsi.h              |  5 +----
> >  2 files changed, 4 insertions(+), 16 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> > b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c index
> > 07cde255cab2..bb4aeca5c0f9 100644
> > --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> > +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> > @@ -966,31 +966,22 @@ EXPORT_SYMBOL_GPL(dw_mipi_dsi_remove);
> > 
> >  /*
> >  
> >   * Bind/unbind API, used from platforms based on the component framework.
> >   */
> > 
> > -struct dw_mipi_dsi *
> > -dw_mipi_dsi_bind(struct platform_device *pdev, struct drm_encoder
> > *encoder, -		 const struct dw_mipi_dsi_plat_data *plat_data)
> > +int dw_mipi_dsi_bind(struct dw_mipi_dsi *dsi, struct drm_encoder
> > *encoder)
> > 
> >  {
> > 
> > -	struct dw_mipi_dsi *dsi;
> > 
> >  	int ret;
> > 
> > -	dsi = __dw_mipi_dsi_probe(pdev, plat_data);
> > -	if (IS_ERR(dsi))
> > -		return dsi;
> > -
> > 
> >  	ret = drm_bridge_attach(encoder, &dsi->bridge, NULL);
> >  	if (ret) {
> > 
> > -		dw_mipi_dsi_remove(dsi);
> > 
> >  		DRM_ERROR("Failed to initialize bridge with drm\n");
> > 
> > -		return ERR_PTR(ret);
> > +		return ret;
> > 
> >  	}
> > 
> > -	return dsi;
> > +	return ret;
> > 
> >  }
> >  EXPORT_SYMBOL_GPL(dw_mipi_dsi_bind);
> >  
> >  void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi)
> >  {
> > 
> > -	__dw_mipi_dsi_remove(dsi);
> > 
> >  }
> >  EXPORT_SYMBOL_GPL(dw_mipi_dsi_unbind);
> 
> Can't we just remove these two bind/unbind functions and call
> drm_bridge_attach directly?

I guess my main motivation was to keep showing that the bridge-attach
(and any future bind actions) is the responsibility of the main driver,
but then again we can do it anyway you prefer.

So I see no problem to move this to the glue driver, if you prefer.


Heiko


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

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

* Re: [PATCH v2 3/8] drm/bridge/synopsys: dsi: defer probing if panel not available in bridge-attach
  2018-06-18 10:28 ` [PATCH v2 3/8] drm/bridge/synopsys: dsi: defer probing if panel not available in bridge-attach Heiko Stuebner
  2018-07-03 11:57   ` Philippe CORNU
@ 2018-07-03 12:42   ` Andrzej Hajda
  2018-07-04 10:36     ` Heiko Stuebner
  1 sibling, 1 reply; 26+ messages in thread
From: Andrzej Hajda @ 2018-07-03 12:42 UTC (permalink / raw)
  To: Heiko Stuebner, dri-devel
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, philippe.cornu,
	yannick.fertre, linux-rockchip, nickey.yang, robh+dt,
	thierry.reding, laurent.pinchart, mka

On 18.06.2018 12:28, Heiko Stuebner wrote:
> When the panel-driver is build as a module it currently fails hard as the
> panel cannot be probed directly:
>
> dw_mipi_dsi_bind()
>   __dw_mipi_dsi_probe()
>     creates dsi bus
>     creates panel device
>     triggers panel module load
>     panel not probed (module not loaded or panel probe slow)
>   drm_bridge_attach
>     fails with -EINVAL due to empty panel_bridge
>
> So emit a -EPROBE_DEFER in that case to give the driver another
> chance at getting the display later.

Thats odd (at least for me), if the resource (panel in this case) is not
present, bridge should not be attached (drm_bridge_attach should not be
called). Ie __dw_mipi_dsi_probe should rather look for the panel and
defer if not found. Resource gathering is the task of probe.
drm_bridge_attach can be called after probe and it would be difficult
for the caller to react properly for -EPROBE_DEFER error.

Regards
Andrzej

>
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> ---
>  drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 3 +++
>  1 file changed, 3 insertions(+)
>
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> index bb4aeca5c0f9..bd503f000ed5 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> @@ -835,6 +835,9 @@ static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge)
>  		return -ENODEV;
>  	}
>  
> +	if (!dsi->panel_bridge)
> +		return -EPROBE_DEFER;
> +
>  	/* Set the encoder type as caller does not know it */
>  	bridge->encoder->encoder_type = DRM_MODE_ENCODER_DSI;
>  


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

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

* Re: [PATCH v2 5/8] drm/rockchip: dsi: migrate to use dw-mipi-dsi bridge driver
  2018-06-18 10:28 ` [PATCH v2 5/8] drm/rockchip: dsi: migrate to use dw-mipi-dsi bridge driver Heiko Stuebner
@ 2018-07-03 13:06   ` Andrzej Hajda
  0 siblings, 0 replies; 26+ messages in thread
From: Andrzej Hajda @ 2018-07-03 13:06 UTC (permalink / raw)
  To: Heiko Stuebner, dri-devel
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, philippe.cornu,
	yannick.fertre, linux-rockchip, nickey.yang, robh+dt,
	thierry.reding, laurent.pinchart, mka

On 18.06.2018 12:28, Heiko Stuebner wrote:
> From: Nickey Yang <nickey.yang@rock-chips.com>
>
> Add the ROCKCHIP DSI controller driver that uses the Synopsys DesignWare
> MIPI DSI host controller bridge and remove the old separate one.
>
> changes:
>
> v2:
>    add err_pllref, remove unnecessary encoder.enable & disable
>    correct spelling mistakes
> v3:
>    call dw_mipi_dsi_unbind() in dw_mipi_dsi_rockchip_unbind()
>    fix typo, use of_device_get_match_data(),
>    change some bind() logic into probe()
>    add 'dev_set_drvdata()'
> v4:
>   return -EINVAL when can not get best_freq
>   add a clarifying comment when get vco
>   add review tag
> v5:
>   keep our power domain enabled while touching GRF
> v6:
>   change func name dw_mipi_encoder_disable to
>   dw_mipi_dsi_encoder_disable
> v7:
>   none
> v8: Heiko
>   add Archit's Review tag
>   adapt to recent changes in the original rockchip-dsi driver
>   beautify grf-handling
>   split hw-setup (resources, dsi-host) from bind into probe
> v2-new: Heiko
>   add SPDX header instead of license blurb
>   drop old versioning to not confuse people
>
>
> Signed-off-by: Nickey Yang <nickey.yang@rock-chips.com>
> Signed-off-by: Brian Norris <briannorris@chromium.org>
> Reviewed-by: Brian Norris <briannorris@chromium.org>
> Reviewed-by: Sean Paul <seanpaul@chromium.org>
> Reviewed-by: Archit Taneja <architt@codeaurora.org>
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> ---
>  drivers/gpu/drm/rockchip/Kconfig              |    2 +-
>  drivers/gpu/drm/rockchip/Makefile             |    2 +-
>  .../gpu/drm/rockchip/dw-mipi-dsi-rockchip.c   |  927 +++++++++++
>  drivers/gpu/drm/rockchip/dw-mipi-dsi.c        | 1349 -----------------
>  drivers/gpu/drm/rockchip/rockchip_drm_drv.c   |    2 +-
>  drivers/gpu/drm/rockchip/rockchip_drm_drv.h   |    2 +-
>  6 files changed, 931 insertions(+), 1353 deletions(-)
>  create mode 100644 drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
>  delete mode 100644 drivers/gpu/drm/rockchip/dw-mipi-dsi.c
>
> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
> index 0ccc76217ee4..9eb4795596d3 100644
> --- a/drivers/gpu/drm/rockchip/Kconfig
> +++ b/drivers/gpu/drm/rockchip/Kconfig
> @@ -7,7 +7,7 @@ config DRM_ROCKCHIP
>  	select VIDEOMODE_HELPERS
>  	select DRM_ANALOGIX_DP if ROCKCHIP_ANALOGIX_DP
>  	select DRM_DW_HDMI if ROCKCHIP_DW_HDMI
> -	select DRM_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI
> +	select DRM_DW_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI
>  	select SND_SOC_HDMI_CODEC if ROCKCHIP_CDN_DP && SND_SOC
>  	help
>  	  Choose this option if you have a Rockchip soc chipset.
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index a314e2109e76..0f22dad1c996 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -11,7 +11,7 @@ rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>  rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
>  rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
>  rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
> -rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
> +rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi-rockchip.o
>  rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
>  rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o
>  
> diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
> new file mode 100644
> index 000000000000..12e4dacc7970
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
> @@ -0,0 +1,927 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author:
> + *      Chris Zhong <zyw@rock-chips.com>
> + *      Nickey Yang <nickey.yang@rock-chips.com>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/iopoll.h>
> +#include <linux/math64.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/pm_runtime.h>
> +#include <drm/drmP.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/bridge/dw_mipi_dsi.h>
> +#include <video/mipi_display.h>
> +#include <linux/regmap.h>
> +#include <drm/drm_of.h>
> +#include <linux/mfd/syscon.h>

Alphabetic order.


> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_vop.h"
> +
> +#define DSI_PHY_RSTZ			0xa0
> +#define PHY_DISFORCEPLL			0
> +#define PHY_ENFORCEPLL			BIT(3)
> +#define PHY_DISABLECLK			0
> +#define PHY_ENABLECLK			BIT(2)
> +#define PHY_RSTZ			0
> +#define PHY_UNRSTZ			BIT(1)
> +#define PHY_SHUTDOWNZ			0
> +#define PHY_UNSHUTDOWNZ			BIT(0)
> +
> +#define DSI_PHY_IF_CFG			0xa4
> +#define N_LANES(n)			((((n) - 1) & 0x3) << 0)
> +#define PHY_STOP_WAIT_TIME(cycle)	(((cycle) & 0xff) << 8)
> +
> +#define DSI_PHY_STATUS			0xb0
> +#define LOCK				BIT(0)
> +#define STOP_STATE_CLK_LANE		BIT(2)
> +
> +#define DSI_PHY_TST_CTRL0		0xb4
> +#define PHY_TESTCLK			BIT(1)
> +#define PHY_UNTESTCLK			0
> +#define PHY_TESTCLR			BIT(0)
> +#define PHY_UNTESTCLR			0
> +
> +#define DSI_PHY_TST_CTRL1		0xb8
> +#define PHY_TESTEN			BIT(16)
> +#define PHY_UNTESTEN			0
> +#define PHY_TESTDOUT(n)			(((n) & 0xff) << 8)
> +#define PHY_TESTDIN(n)			(((n) & 0xff) << 0)
> +
> +#define DSI_INT_ST0			0xbc
> +#define DSI_INT_ST1			0xc0
> +#define DSI_INT_MSK0			0xc4
> +#define DSI_INT_MSK1			0xc8
> +
> +#define PHY_STATUS_TIMEOUT_US		10000
> +#define CMD_PKT_STATUS_TIMEOUT_US	20000
> +
> +#define BYPASS_VCO_RANGE	BIT(7)
> +#define VCO_RANGE_CON_SEL(val)	(((val) & 0x7) << 3)
> +#define VCO_IN_CAP_CON_DEFAULT	(0x0 << 1)
> +#define VCO_IN_CAP_CON_LOW	(0x1 << 1)
> +#define VCO_IN_CAP_CON_HIGH	(0x2 << 1)
> +#define REF_BIAS_CUR_SEL	BIT(0)
> +
> +#define CP_CURRENT_3UA	0x1
> +#define CP_CURRENT_4_5UA	0x2
> +#define CP_CURRENT_7_5UA	0x6
> +#define CP_CURRENT_6UA	0x9
> +#define CP_CURRENT_12UA	0xb
> +#define CP_CURRENT_SEL(val)	((val) & 0xf)
> +#define CP_PROGRAM_EN		BIT(7)
> +
> +#define LPF_RESISTORS_15_5KOHM	0x1
> +#define LPF_RESISTORS_13KOHM	0x2
> +#define LPF_RESISTORS_11_5KOHM	0x4
> +#define LPF_RESISTORS_10_5KOHM	0x8
> +#define LPF_RESISTORS_8KOHM	0x10
> +#define LPF_PROGRAM_EN		BIT(6)
> +#define LPF_RESISTORS_SEL(val)	((val) & 0x3f)
> +
> +#define HSFREQRANGE_SEL(val)	(((val) & 0x3f) << 1)
> +
> +#define INPUT_DIVIDER(val)	(((val) - 1) & 0x7f)
> +#define LOW_PROGRAM_EN		0
> +#define HIGH_PROGRAM_EN		BIT(7)
> +#define LOOP_DIV_LOW_SEL(val)	(((val) - 1) & 0x1f)
> +#define LOOP_DIV_HIGH_SEL(val)	((((val) - 1) >> 5) & 0xf)
> +#define PLL_LOOP_DIV_EN		BIT(5)
> +#define PLL_INPUT_DIV_EN	BIT(4)
> +
> +#define POWER_CONTROL		BIT(6)
> +#define INTERNAL_REG_CURRENT	BIT(3)
> +#define BIAS_BLOCK_ON		BIT(2)
> +#define BANDGAP_ON		BIT(0)
> +
> +#define TER_RESISTOR_HIGH	BIT(7)
> +#define	TER_RESISTOR_LOW	0
> +#define LEVEL_SHIFTERS_ON	BIT(6)
> +#define TER_CAL_DONE		BIT(5)
> +#define SETRD_MAX		(0x7 << 2)
> +#define POWER_MANAGE		BIT(1)
> +#define TER_RESISTORS_ON	BIT(0)
> +
> +#define BIASEXTR_SEL(val)	((val) & 0x7)
> +#define BANDGAP_SEL(val)	((val) & 0x7)
> +#define TLP_PROGRAM_EN		BIT(7)
> +#define THS_PRE_PROGRAM_EN	BIT(7)
> +#define THS_ZERO_PROGRAM_EN	BIT(6)
> +
> +#define PLL_BIAS_CUR_SEL_CAP_VCO_CONTROL		0x10
> +#define PLL_CP_CONTROL_PLL_LOCK_BYPASS			0x11
> +#define PLL_LPF_AND_CP_CONTROL				0x12
> +#define PLL_INPUT_DIVIDER_RATIO				0x17
> +#define PLL_LOOP_DIVIDER_RATIO				0x18
> +#define PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL	0x19
> +#define BANDGAP_AND_BIAS_CONTROL			0x20
> +#define TERMINATION_RESISTER_CONTROL			0x21
> +#define AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY		0x22
> +#define HS_RX_CONTROL_OF_LANE_0				0x44
> +#define HS_TX_CLOCK_LANE_REQUEST_STATE_TIME_CONTROL	0x60
> +#define HS_TX_CLOCK_LANE_PREPARE_STATE_TIME_CONTROL	0x61
> +#define HS_TX_CLOCK_LANE_HS_ZERO_STATE_TIME_CONTROL	0x62
> +#define HS_TX_CLOCK_LANE_TRAIL_STATE_TIME_CONTROL	0x63
> +#define HS_TX_CLOCK_LANE_EXIT_STATE_TIME_CONTROL	0x64
> +#define HS_TX_CLOCK_LANE_POST_TIME_CONTROL		0x65
> +#define HS_TX_DATA_LANE_REQUEST_STATE_TIME_CONTROL	0x70
> +#define HS_TX_DATA_LANE_PREPARE_STATE_TIME_CONTROL	0x71
> +#define HS_TX_DATA_LANE_HS_ZERO_STATE_TIME_CONTROL	0x72
> +#define HS_TX_DATA_LANE_TRAIL_STATE_TIME_CONTROL	0x73
> +#define HS_TX_DATA_LANE_EXIT_STATE_TIME_CONTROL		0x74
> +
> +#define DW_MIPI_NEEDS_PHY_CFG_CLK	BIT(0)
> +#define DW_MIPI_NEEDS_GRF_CLK		BIT(1)
> +
> +#define RK3288_GRF_SOC_CON6		0x025c
> +#define RK3288_DSI0_LCDC_SEL		BIT(6)
> +#define RK3288_DSI1_LCDC_SEL		BIT(9)
> +
> +#define RK3399_GRF_SOC_CON20		0x6250
> +#define RK3399_DSI0_LCDC_SEL		BIT(0)
> +#define RK3399_DSI1_LCDC_SEL		BIT(4)
> +
> +#define RK3399_GRF_SOC_CON22		0x6258
> +#define RK3399_DSI0_TURNREQUEST		(0xf << 12)
> +#define RK3399_DSI0_TURNDISABLE		(0xf << 8)
> +#define RK3399_DSI0_FORCETXSTOPMODE	(0xf << 4)
> +#define RK3399_DSI0_FORCERXMODE		(0xf << 0)
> +
> +#define RK3399_GRF_SOC_CON23		0x625c
> +#define RK3399_DSI1_TURNDISABLE		(0xf << 12)
> +#define RK3399_DSI1_FORCETXSTOPMODE	(0xf << 8)
> +#define RK3399_DSI1_FORCERXMODE		(0xf << 4)
> +#define RK3399_DSI1_ENABLE		(0xf << 0)
> +
> +#define RK3399_GRF_SOC_CON24		0x6260
> +#define RK3399_TXRX_MASTERSLAVEZ	BIT(7)
> +#define RK3399_TXRX_ENABLECLK		BIT(6)
> +#define RK3399_TXRX_BASEDIR		BIT(5)
> +
> +#define HIWORD_UPDATE(val, mask)	(val | (mask) << 16)
> +
> +#define to_dsi(nm)	container_of(nm, struct dw_mipi_dsi_rockchip, nm)
> +
> +enum {
> +	BANDGAP_97_07,
> +	BANDGAP_98_05,
> +	BANDGAP_99_02,
> +	BANDGAP_100_00,
> +	BANDGAP_93_17,
> +	BANDGAP_94_15,
> +	BANDGAP_95_12,
> +	BANDGAP_96_10,
> +};
> +
> +enum {
> +	BIASEXTR_87_1,
> +	BIASEXTR_91_5,
> +	BIASEXTR_95_9,
> +	BIASEXTR_100,
> +	BIASEXTR_105_94,
> +	BIASEXTR_111_88,
> +	BIASEXTR_118_8,
> +	BIASEXTR_127_7,
> +};
> +
> +struct rockchip_dw_dsi_chip_data {
> +	u32 reg;
> +
> +	u32 lcdsel_grf_reg;
> +	u32 lcdsel_big;
> +	u32 lcdsel_lit;
> +
> +	u32 enable_grf_reg;
> +	u32 enable;
> +
> +	u32 lanecfg1_grf_reg;
> +	u32 lanecfg1;
> +	u32 lanecfg2_grf_reg;
> +	u32 lanecfg2;
> +
> +	unsigned int flags;
> +	unsigned int max_data_lanes;
> +};
> +
> +struct dw_mipi_dsi_rockchip {
> +	struct device *dev;
> +	struct drm_encoder encoder;
> +	void __iomem *base;
> +
> +	struct regmap *grf_regmap;
> +	struct clk *pllref_clk;
> +	struct clk *grf_clk;
> +	struct clk *phy_cfg_clk;
> +
> +	unsigned int lane_mbps; /* per lane */
> +	u16 input_div;
> +	u16 feedback_div;
> +	u32 format;
> +
> +	struct dw_mipi_dsi *dmd;
> +	const struct rockchip_dw_dsi_chip_data *cdata;
> +	struct dw_mipi_dsi_plat_data pdata;
> +};
> +
> +struct dphy_pll_parameter_map {
> +	unsigned int max_mbps;
> +	u8 hsfreqrange;
> +	u8 icpctrl;
> +	u8 lpfctrl;
> +};
> +
> +/* The table is based on 27MHz DPHY pll reference clock. */
> +static const struct dphy_pll_parameter_map dppa_map[] = {
> +	{  89, 0x00, CP_CURRENT_3UA, LPF_RESISTORS_13KOHM },
> +	{  99, 0x10, CP_CURRENT_3UA, LPF_RESISTORS_13KOHM },
> +	{ 109, 0x20, CP_CURRENT_3UA, LPF_RESISTORS_13KOHM },
> +	{ 129, 0x01, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM },
> +	{ 139, 0x11, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM },
> +	{ 149, 0x21, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM },
> +	{ 169, 0x02, CP_CURRENT_6UA, LPF_RESISTORS_13KOHM },
> +	{ 179, 0x12, CP_CURRENT_6UA, LPF_RESISTORS_13KOHM },
> +	{ 199, 0x22, CP_CURRENT_6UA, LPF_RESISTORS_13KOHM },
> +	{ 219, 0x03, CP_CURRENT_4_5UA, LPF_RESISTORS_13KOHM },
> +	{ 239, 0x13, CP_CURRENT_4_5UA, LPF_RESISTORS_13KOHM },
> +	{ 249, 0x23, CP_CURRENT_4_5UA, LPF_RESISTORS_13KOHM },
> +	{ 269, 0x04, CP_CURRENT_6UA, LPF_RESISTORS_11_5KOHM },
> +	{ 299, 0x14, CP_CURRENT_6UA, LPF_RESISTORS_11_5KOHM },
> +	{ 329, 0x05, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM },
> +	{ 359, 0x15, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM },
> +	{ 399, 0x25, CP_CURRENT_3UA, LPF_RESISTORS_15_5KOHM },
> +	{ 449, 0x06, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
> +	{ 499, 0x16, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
> +	{ 549, 0x07, CP_CURRENT_7_5UA, LPF_RESISTORS_10_5KOHM },
> +	{ 599, 0x17, CP_CURRENT_7_5UA, LPF_RESISTORS_10_5KOHM },
> +	{ 649, 0x08, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
> +	{ 699, 0x18, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
> +	{ 749, 0x09, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
> +	{ 799, 0x19, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
> +	{ 849, 0x29, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
> +	{ 899, 0x39, CP_CURRENT_7_5UA, LPF_RESISTORS_11_5KOHM },
> +	{ 949, 0x0a, CP_CURRENT_12UA, LPF_RESISTORS_8KOHM },
> +	{ 999, 0x1a, CP_CURRENT_12UA, LPF_RESISTORS_8KOHM },
> +	{1049, 0x2a, CP_CURRENT_12UA, LPF_RESISTORS_8KOHM },
> +	{1099, 0x3a, CP_CURRENT_12UA, LPF_RESISTORS_8KOHM },
> +	{1149, 0x0b, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM },
> +	{1199, 0x1b, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM },
> +	{1249, 0x2b, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM },
> +	{1299, 0x3b, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM },
> +	{1349, 0x0c, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM },
> +	{1399, 0x1c, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM },
> +	{1449, 0x2c, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM },
> +	{1500, 0x3c, CP_CURRENT_12UA, LPF_RESISTORS_10_5KOHM }
> +};
> +
> +static int max_mbps_to_parameter(unsigned int max_mbps)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(dppa_map); i++)
> +		if (dppa_map[i].max_mbps >= max_mbps)
> +			return i;
> +
> +	return -EINVAL;
> +}
> +
> +static inline void dsi_write(struct dw_mipi_dsi_rockchip *dsi, u32 reg, u32 val)
> +{
> +	writel(val, dsi->base + reg);
> +}
> +
> +static inline u32 dsi_read(struct dw_mipi_dsi_rockchip *dsi, u32 reg)
> +{
> +	return readl(dsi->base + reg);
> +}
> +
> +static inline void dsi_set(struct dw_mipi_dsi_rockchip *dsi, u32 reg, u32 mask)
> +{
> +	dsi_write(dsi, reg, dsi_read(dsi, reg) | mask);
> +}
> +
> +static inline void dsi_update_bits(struct dw_mipi_dsi_rockchip *dsi, u32 reg,
> +				   u32 mask, u32 val)
> +{
> +	dsi_write(dsi, reg, (dsi_read(dsi, reg) & ~mask) | val);
> +}
> +
> +static void dw_mipi_dsi_phy_write(struct dw_mipi_dsi_rockchip *dsi,
> +				  u8 test_code,
> +				  u8 test_data)
> +{
> +	/*
> +	 * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
> +	 * is latched internally as the current test code. Test data is
> +	 * programmed internally by rising edge on TESTCLK.
> +	 */
> +	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
> +
> +	dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_TESTEN | PHY_TESTDOUT(0) |
> +					  PHY_TESTDIN(test_code));
> +
> +	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR);
> +
> +	dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_UNTESTEN | PHY_TESTDOUT(0) |
> +					  PHY_TESTDIN(test_data));
> +
> +	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
> +}
> +
> +/**
> + * ns2bc - Nanoseconds to byte clock cycles
> + */
> +static inline unsigned int ns2bc(struct dw_mipi_dsi_rockchip *dsi, int ns)
> +{
> +	return DIV_ROUND_UP(ns * dsi->lane_mbps / 8, 1000);
> +}
> +
> +/**
> + * ns2ui - Nanoseconds to UI time periods
> + */
> +static inline unsigned int ns2ui(struct dw_mipi_dsi_rockchip *dsi, int ns)
> +{
> +	return DIV_ROUND_UP(ns * dsi->lane_mbps, 1000);
> +}
> +
> +static int dw_mipi_dsi_phy_init(void *priv_data)
> +{
> +	struct dw_mipi_dsi_rockchip *dsi = priv_data;
> +	int ret, i, vco;
> +
> +	/*
> +	 * Get vco from frequency(lane_mbps)
> +	 * vco	frequency table
> +	 * 000 - between   80 and  200 MHz
> +	 * 001 - between  200 and  300 MHz
> +	 * 010 - between  300 and  500 MHz
> +	 * 011 - between  500 and  700 MHz
> +	 * 100 - between  700 and  900 MHz
> +	 * 101 - between  900 and 1100 MHz
> +	 * 110 - between 1100 and 1300 MHz
> +	 * 111 - between 1300 and 1500 MHz
> +	 */
> +	vco = (dsi->lane_mbps < 200) ? 0 : (dsi->lane_mbps + 100) / 200;
> +
> +	i = max_mbps_to_parameter(dsi->lane_mbps);
> +	if (i < 0) {
> +		DRM_DEV_ERROR(dsi->dev,
> +			      "failed to get parameter for %dmbps clock\n",
> +			      dsi->lane_mbps);
> +		return i;
> +	}
> +
> +	ret = clk_prepare_enable(dsi->phy_cfg_clk);
> +	if (ret) {
> +		DRM_DEV_ERROR(dsi->dev, "Failed to enable phy_cfg_clk\n");
> +		return ret;
> +	}
> +
> +	dw_mipi_dsi_phy_write(dsi, PLL_BIAS_CUR_SEL_CAP_VCO_CONTROL,
> +			      BYPASS_VCO_RANGE |
> +			      VCO_RANGE_CON_SEL(vco) |
> +			      VCO_IN_CAP_CON_LOW |
> +			      REF_BIAS_CUR_SEL);
> +
> +	dw_mipi_dsi_phy_write(dsi, PLL_CP_CONTROL_PLL_LOCK_BYPASS,
> +			      CP_CURRENT_SEL(dppa_map[i].icpctrl));
> +	dw_mipi_dsi_phy_write(dsi, PLL_LPF_AND_CP_CONTROL,
> +			      CP_PROGRAM_EN | LPF_PROGRAM_EN |
> +			      LPF_RESISTORS_SEL(dppa_map[i].lpfctrl));
> +
> +	dw_mipi_dsi_phy_write(dsi, HS_RX_CONTROL_OF_LANE_0,
> +			      HSFREQRANGE_SEL(dppa_map[i].hsfreqrange));
> +
> +	dw_mipi_dsi_phy_write(dsi, PLL_INPUT_DIVIDER_RATIO,
> +			      INPUT_DIVIDER(dsi->input_div));
> +	dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO,
> +			      LOOP_DIV_LOW_SEL(dsi->feedback_div) |
> +			      LOW_PROGRAM_EN);
> +	/*
> +	 * We need set PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL immediately
> +	 * to make the configured LSB effective according to IP simulation
> +	 * and lab test results.
> +	 * Only in this way can we get correct mipi phy pll frequency.
> +	 */
> +	dw_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL,
> +			      PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN);
> +	dw_mipi_dsi_phy_write(dsi, PLL_LOOP_DIVIDER_RATIO,
> +			      LOOP_DIV_HIGH_SEL(dsi->feedback_div) |
> +			      HIGH_PROGRAM_EN);
> +	dw_mipi_dsi_phy_write(dsi, PLL_INPUT_AND_LOOP_DIVIDER_RATIOS_CONTROL,
> +			      PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN);
> +
> +	dw_mipi_dsi_phy_write(dsi, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY,
> +			      LOW_PROGRAM_EN | BIASEXTR_SEL(BIASEXTR_127_7));
> +	dw_mipi_dsi_phy_write(dsi, AFE_BIAS_BANDGAP_ANALOG_PROGRAMMABILITY,
> +			      HIGH_PROGRAM_EN | BANDGAP_SEL(BANDGAP_96_10));
> +
> +	dw_mipi_dsi_phy_write(dsi, BANDGAP_AND_BIAS_CONTROL,
> +			      POWER_CONTROL | INTERNAL_REG_CURRENT |
> +			      BIAS_BLOCK_ON | BANDGAP_ON);
> +
> +	dw_mipi_dsi_phy_write(dsi, TERMINATION_RESISTER_CONTROL,
> +			      TER_RESISTOR_LOW | TER_CAL_DONE |
> +			      SETRD_MAX | TER_RESISTORS_ON);
> +	dw_mipi_dsi_phy_write(dsi, TERMINATION_RESISTER_CONTROL,
> +			      TER_RESISTOR_HIGH | LEVEL_SHIFTERS_ON |
> +			      SETRD_MAX | POWER_MANAGE |
> +			      TER_RESISTORS_ON);
> +
> +	dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_REQUEST_STATE_TIME_CONTROL,
> +			      TLP_PROGRAM_EN | ns2bc(dsi, 500));
> +	dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_PREPARE_STATE_TIME_CONTROL,
> +			      THS_PRE_PROGRAM_EN | ns2ui(dsi, 40));
> +	dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_HS_ZERO_STATE_TIME_CONTROL,
> +			      THS_ZERO_PROGRAM_EN | ns2bc(dsi, 300));
> +	dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_TRAIL_STATE_TIME_CONTROL,
> +			      THS_PRE_PROGRAM_EN | ns2ui(dsi, 100));
> +	dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_EXIT_STATE_TIME_CONTROL,
> +			      BIT(5) | ns2bc(dsi, 100));
> +	dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_POST_TIME_CONTROL,
> +			      BIT(5) | (ns2bc(dsi, 60) + 7));
> +
> +	dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_REQUEST_STATE_TIME_CONTROL,
> +			      TLP_PROGRAM_EN | ns2bc(dsi, 500));
> +	dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_PREPARE_STATE_TIME_CONTROL,
> +			      THS_PRE_PROGRAM_EN | (ns2ui(dsi, 50) + 20));
> +	dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_HS_ZERO_STATE_TIME_CONTROL,
> +			      THS_ZERO_PROGRAM_EN | (ns2bc(dsi, 140) + 2));
> +	dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_TRAIL_STATE_TIME_CONTROL,
> +			      THS_PRE_PROGRAM_EN | (ns2ui(dsi, 60) + 8));
> +	dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_EXIT_STATE_TIME_CONTROL,
> +			      BIT(5) | ns2bc(dsi, 100));
> +
> +	clk_disable_unprepare(dsi->phy_cfg_clk);
> +
> +	return ret;
> +}
> +
> +static int
> +dw_mipi_dsi_get_lane_mbps(void *priv_data, struct drm_display_mode *mode,
> +			  unsigned long mode_flags, u32 lanes, u32 format,
> +			  unsigned int *lane_mbps)
> +{
> +	struct dw_mipi_dsi_rockchip *dsi = priv_data;
> +	int bpp;
> +	unsigned long mpclk, tmp;
> +	unsigned int target_mbps = 1000;
> +	unsigned int max_mbps = dppa_map[ARRAY_SIZE(dppa_map) - 1].max_mbps;
> +	unsigned long best_freq = 0;
> +	unsigned long fvco_min, fvco_max, fin, fout;
> +	unsigned int min_prediv, max_prediv;
> +	unsigned int _prediv, uninitialized_var(best_prediv);
> +	unsigned long _fbdiv, uninitialized_var(best_fbdiv);
> +	unsigned long min_delta = ULONG_MAX;
> +
> +	dsi->format = format;
> +	bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
> +	if (bpp < 0) {
> +		DRM_DEV_ERROR(dsi->dev,
> +			      "failed to get bpp for pixel format %d\n",
> +			      dsi->format);
> +		return bpp;
> +	}
> +
> +	mpclk = DIV_ROUND_UP(mode->clock, MSEC_PER_SEC);
> +	if (mpclk) {
> +		/* take 1 / 0.8, since mbps must big than bandwidth of RGB */
> +		tmp = mpclk * (bpp / lanes) * 10 / 8;
> +		if (tmp < max_mbps)
> +			target_mbps = tmp;
> +		else
> +			DRM_DEV_ERROR(dsi->dev,
> +				      "DPHY clock frequency is out of range\n");
> +	}
> +
> +	fin = clk_get_rate(dsi->pllref_clk);
> +	fout = target_mbps * USEC_PER_SEC;
> +
> +	/* constraint: 5Mhz <= Fref / N <= 40MHz */
> +	min_prediv = DIV_ROUND_UP(fin, 40 * USEC_PER_SEC);
> +	max_prediv = fin / (5 * USEC_PER_SEC);
> +
> +	/* constraint: 80MHz <= Fvco <= 1500Mhz */
> +	fvco_min = 80 * USEC_PER_SEC;
> +	fvco_max = 1500 * USEC_PER_SEC;
> +
> +	for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) {
> +		u64 tmp;
> +		u32 delta;
> +		/* Fvco = Fref * M / N */
> +		tmp = (u64)fout * _prediv;
> +		do_div(tmp, fin);
> +		_fbdiv = tmp;
> +		/*
> +		 * Due to the use of a "by 2 pre-scaler," the range of the
> +		 * feedback multiplication value M is limited to even division
> +		 * numbers, and m must be greater than 6, not bigger than 512.
> +		 */
> +		if (_fbdiv < 6 || _fbdiv > 512)
> +			continue;
> +
> +		_fbdiv += _fbdiv % 2;
> +
> +		tmp = (u64)_fbdiv * fin;
> +		do_div(tmp, _prediv);
> +		if (tmp < fvco_min || tmp > fvco_max)
> +			continue;
> +
> +		delta = abs(fout - tmp);
> +		if (delta < min_delta) {
> +			best_prediv = _prediv;
> +			best_fbdiv = _fbdiv;
> +			min_delta = delta;
> +			best_freq = tmp;
> +		}
> +	}
> +
> +	if (best_freq) {
> +		dsi->lane_mbps = DIV_ROUND_UP(best_freq, USEC_PER_SEC);
> +		*lane_mbps = dsi->lane_mbps;
> +		dsi->input_div = best_prediv;
> +		dsi->feedback_div = best_fbdiv;
> +	} else {
> +		DRM_DEV_ERROR(dsi->dev, "Can not find best_freq for DPHY\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct dw_mipi_dsi_phy_ops dw_mipi_dsi_rockchip_phy_ops = {
> +	.init = dw_mipi_dsi_phy_init,
> +	.get_lane_mbps = dw_mipi_dsi_get_lane_mbps,
> +};
> +
> +static void dw_mipi_dsi_rockchip_config(struct dw_mipi_dsi_rockchip *dsi,
> +					int mux)
> +{
> +	pm_runtime_get_sync(dsi->dev);
> +
> +	if (dsi->cdata->lcdsel_grf_reg)
> +		regmap_write(dsi->grf_regmap, dsi->cdata->lcdsel_grf_reg,
> +			mux ? dsi->cdata->lcdsel_lit : dsi->cdata->lcdsel_big);
> +
> +	if (dsi->cdata->lanecfg1_grf_reg)
> +		regmap_write(dsi->grf_regmap, dsi->cdata->lanecfg1_grf_reg,
> +					      dsi->cdata->lanecfg1);
> +
> +	if (dsi->cdata->lanecfg2_grf_reg)
> +		regmap_write(dsi->grf_regmap, dsi->cdata->lanecfg2_grf_reg,
> +					      dsi->cdata->lanecfg2);
> +
> +	if (dsi->cdata->enable_grf_reg)
> +		regmap_write(dsi->grf_regmap, dsi->cdata->enable_grf_reg,
> +					      dsi->cdata->enable);
> +}
> +
> +static void dw_mipi_dsi_encoder_mode_set(struct drm_encoder *encoder,
> +					 struct drm_display_mode *mode,
> +					 struct drm_display_mode *adjusted)
> +{
> +	struct dw_mipi_dsi_rockchip *dsi = to_dsi(encoder);
> +	int ret, mux;
> +
> +	mux = drm_of_encoder_active_endpoint_id(dsi->dev->of_node,
> +						&dsi->encoder);
> +	if (mux < 0)
> +		return;
> +
> +	/*
> +	 * For the RK3399, the clk of grf must be enabled before writing grf
> +	 * register. And for RK3288 or other soc, this grf_clk must be NULL,
> +	 * the clk_prepare_enable return true directly.
> +	 */
> +	ret = clk_prepare_enable(dsi->grf_clk);
> +	if (ret) {
> +		DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret);
> +		return;
> +	}
> +
> +	dw_mipi_dsi_rockchip_config(dsi, mux);
> +
> +	clk_disable_unprepare(dsi->grf_clk);
> +}

Description of .mode_set callback states clearly hw touching should be
done in .enable callback. Is there a reason to do it here?


> +
> +static int
> +dw_mipi_dsi_encoder_atomic_check(struct drm_encoder *encoder,
> +				 struct drm_crtc_state *crtc_state,
> +				 struct drm_connector_state *conn_state)
> +{
> +	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
> +	struct dw_mipi_dsi_rockchip *dsi = to_dsi(encoder);
> +
> +	switch (dsi->format) {
> +	case MIPI_DSI_FMT_RGB888:
> +		s->output_mode = ROCKCHIP_OUT_MODE_P888;
> +		break;
> +	case MIPI_DSI_FMT_RGB666:
> +		s->output_mode = ROCKCHIP_OUT_MODE_P666;
> +		break;
> +	case MIPI_DSI_FMT_RGB565:
> +		s->output_mode = ROCKCHIP_OUT_MODE_P565;
> +		break;
> +	default:
> +		WARN_ON(1);
> +		return -EINVAL;
> +	}
> +
> +	s->output_type = DRM_MODE_CONNECTOR_DSI;
> +
> +	return 0;
> +}
> +
> +static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder)
> +{
> +	struct dw_mipi_dsi_rockchip *dsi = to_dsi(encoder);
> +
> +	pm_runtime_put(dsi->dev);
> +}
> +
> +static const struct drm_encoder_helper_funcs
> +dw_mipi_dsi_encoder_helper_funcs = {
> +	.mode_set = dw_mipi_dsi_encoder_mode_set,
> +	.atomic_check = dw_mipi_dsi_encoder_atomic_check,
> +	.disable = dw_mipi_dsi_encoder_disable,
> +};
> +
> +static const struct drm_encoder_funcs dw_mipi_dsi_encoder_funcs = {
> +	.destroy = drm_encoder_cleanup,
> +};
> +
> +static int rockchip_dsi_drm_create_encoder(struct dw_mipi_dsi_rockchip *dsi,
> +					   struct drm_device *drm_dev)
> +{
> +	struct drm_encoder *encoder = &dsi->encoder;
> +	int ret;
> +
> +	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
> +							     dsi->dev->of_node);
> +
> +	ret = drm_encoder_init(drm_dev, encoder, &dw_mipi_dsi_encoder_funcs,
> +			       DRM_MODE_ENCODER_DSI, NULL);
> +	if (ret) {
> +		DRM_ERROR("Failed to initialize encoder with drm\n");
> +		return ret;
> +	}
> +
> +	drm_encoder_helper_add(encoder, &dw_mipi_dsi_encoder_helper_funcs);
> +
> +	return 0;
> +}
> +
> +static int dw_mipi_dsi_rockchip_bind(struct device *dev,
> +				     struct device *master,
> +				     void *data)
> +{
> +	struct dw_mipi_dsi_rockchip *dsi = dev_get_drvdata(dev);
> +	struct drm_device *drm_dev = data;
> +	struct drm_bridge *bridge;
> +	struct drm_panel *panel;
> +	int ret;
> +
> +	/*
> +	 * Handle probe-deferrals due to missing display.
> +	 */
> +	ret = drm_of_find_panel_or_bridge(dsi->dev->of_node, 1, 0,
> +					  &panel, &bridge);
> +	if (ret)
> +		return ret;

Do we really need it? At least if you consider my comments to previous
patches.

> +
> +	ret = rockchip_dsi_drm_create_encoder(dsi, drm_dev);
> +	if (ret) {
> +		DRM_DEV_ERROR(dev, "Failed to create drm encoder\n");
> +		return ret;
> +	}
> +
> +	ret = dw_mipi_dsi_bind(dsi->dmd, &dsi->encoder);
> +	if (ret) {
> +		DRM_DEV_ERROR(dev, "Failed to bind: %d\n", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void dw_mipi_dsi_rockchip_unbind(struct device *dev,
> +					struct device *master,
> +					void *data)
> +{
> +	struct dw_mipi_dsi_rockchip *dsi = dev_get_drvdata(dev);
> +
> +	dw_mipi_dsi_unbind(dsi->dmd);
> +}
> +
> +static const struct component_ops dw_mipi_dsi_rockchip_ops = {
> +	.bind	= dw_mipi_dsi_rockchip_bind,
> +	.unbind	= dw_mipi_dsi_rockchip_unbind,
> +};
> +
> +static int dw_mipi_dsi_rockchip_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	struct dw_mipi_dsi_rockchip *dsi;
> +	struct resource *res;
> +	const struct rockchip_dw_dsi_chip_data *cdata =
> +				of_device_get_match_data(dev);
> +	int ret, i;
> +
> +	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
> +	if (!dsi)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	dsi->base = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(dsi->base)) {
> +		DRM_DEV_ERROR(dev, "Unable to get dsi registers\n");
> +		return PTR_ERR(dsi->base);
> +	}
> +
> +	i = 0;
> +	while (cdata[i].reg) {
> +		if (cdata[i].reg == res->start) {
> +			dsi->cdata = &cdata[i];
> +			break;
> +		}
> +
> +		i++;
> +	}
> +
> +	if (!dsi->cdata) {
> +		dev_err(dev, "no dsi-config for %s node\n", np->name);
> +		return -EINVAL;
> +	}
> +
> +	dsi->pllref_clk = devm_clk_get(dev, "ref");
> +	if (IS_ERR(dsi->pllref_clk)) {
> +		ret = PTR_ERR(dsi->pllref_clk);
> +		DRM_DEV_ERROR(dev,
> +			      "Unable to get pll reference clock: %d\n", ret);
> +		return ret;
> +	}
> +
> +	if (dsi->cdata->flags & DW_MIPI_NEEDS_PHY_CFG_CLK) {
> +		dsi->phy_cfg_clk = devm_clk_get(dev, "phy_cfg");
> +		if (IS_ERR(dsi->phy_cfg_clk)) {
> +			ret = PTR_ERR(dsi->phy_cfg_clk);
> +			DRM_DEV_ERROR(dev,
> +				      "Unable to get phy_cfg_clk: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	if (dsi->cdata->flags & DW_MIPI_NEEDS_GRF_CLK) {
> +		dsi->grf_clk = devm_clk_get(dev, "grf");
> +		if (IS_ERR(dsi->grf_clk)) {
> +			ret = PTR_ERR(dsi->grf_clk);
> +			DRM_DEV_ERROR(dev, "Unable to get grf_clk: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	dsi->grf_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
> +	if (IS_ERR(dsi->grf_regmap)) {
> +		DRM_DEV_ERROR(dsi->dev, "Unable to get rockchip,grf\n");
> +		return PTR_ERR(dsi->grf_regmap);
> +	}
> +
> +	dsi->dev = dev;
> +	dsi->pdata.base = dsi->base;
> +	dsi->pdata.max_data_lanes = dsi->cdata->max_data_lanes;
> +	dsi->pdata.phy_ops = &dw_mipi_dsi_rockchip_phy_ops;
> +	dsi->pdata.priv_data = dsi;
> +	platform_set_drvdata(pdev, dsi);
> +
> +	ret = clk_prepare_enable(dsi->pllref_clk);

Can't you enable the clock later? Resources usually should be enabled as
late as possible.

Regards
Andrzej

> +	if (ret) {
> +		DRM_DEV_ERROR(dev, "Failed to enable pllref_clk: %d\n", ret);
> +		return ret;
> +	}
> +
> +	dsi->dmd = dw_mipi_dsi_probe(pdev, &dsi->pdata);
> +	if (IS_ERR(dsi->dmd)) {
> +		ret = PTR_ERR(dsi->dmd);
> +		if (ret != -EPROBE_DEFER)
> +			DRM_DEV_ERROR(dev,
> +				      "Failed to probe dw_mipi_dsi: %d\n", ret);
> +		goto err_clkdisable;
> +	}
> +
> +	ret = component_add(&pdev->dev, &dw_mipi_dsi_rockchip_ops);
> +	if (ret) {
> +		DRM_DEV_ERROR(dev, "Failed to register component: %d\n", ret);
> +		goto err_dsiprobe;
> +	}
> +
> +	return 0;
> +
> +err_dsiprobe:
> +	dw_mipi_dsi_remove(dsi->dmd);
> +err_clkdisable:
> +	clk_disable_unprepare(dsi->pllref_clk);
> +	return ret;
> +}
> +
> +static int dw_mipi_dsi_rockchip_remove(struct platform_device *pdev)
> +{
> +	struct dw_mipi_dsi_rockchip *dsi = platform_get_drvdata(pdev);
> +
> +	component_del(&pdev->dev, &dw_mipi_dsi_rockchip_ops);
> +	dw_mipi_dsi_remove(dsi->dmd);
> +	clk_disable_unprepare(dsi->pllref_clk);
> +
> +	return 0;
> +}
> +
> +static const struct rockchip_dw_dsi_chip_data rk3288_chip_data[] = {
> +	{
> +		.reg = 0xff960000,
> +		.lcdsel_grf_reg = RK3288_GRF_SOC_CON6,
> +		.lcdsel_big = HIWORD_UPDATE(0, RK3288_DSI0_LCDC_SEL),
> +		.lcdsel_lit = HIWORD_UPDATE(RK3288_DSI0_LCDC_SEL, RK3288_DSI0_LCDC_SEL),
> +
> +		.max_data_lanes = 4,
> +	},
> +	{
> +		.reg = 0xff964000,
> +		.lcdsel_grf_reg = RK3288_GRF_SOC_CON6,
> +		.lcdsel_big = HIWORD_UPDATE(0, RK3288_DSI1_LCDC_SEL),
> +		.lcdsel_lit = HIWORD_UPDATE(RK3288_DSI1_LCDC_SEL, RK3288_DSI1_LCDC_SEL),
> +
> +		.max_data_lanes = 4,
> +	},
> +	{ /* sentinel */ }
> +};
> +
> +static const struct rockchip_dw_dsi_chip_data rk3399_chip_data[] = {
> +	{
> +		.reg = 0xff960000,
> +		.lcdsel_grf_reg = RK3399_GRF_SOC_CON20,
> +		.lcdsel_big = HIWORD_UPDATE(0, RK3399_DSI0_LCDC_SEL),
> +		.lcdsel_lit = HIWORD_UPDATE(RK3399_DSI0_LCDC_SEL,
> +					    RK3399_DSI0_LCDC_SEL),
> +
> +		.lanecfg1_grf_reg = RK3399_GRF_SOC_CON22,
> +		.lanecfg1 = HIWORD_UPDATE(0, RK3399_DSI0_TURNREQUEST |
> +					     RK3399_DSI0_TURNDISABLE |
> +					     RK3399_DSI0_FORCETXSTOPMODE |
> +					     RK3399_DSI0_FORCERXMODE),
> +
> +		.flags = DW_MIPI_NEEDS_PHY_CFG_CLK | DW_MIPI_NEEDS_GRF_CLK,
> +		.max_data_lanes = 4,
> +	},
> +	{
> +		.reg = 0xff968000,
> +		.lcdsel_grf_reg = RK3399_GRF_SOC_CON20,
> +		.lcdsel_big = HIWORD_UPDATE(0, RK3399_DSI1_LCDC_SEL),
> +		.lcdsel_lit = HIWORD_UPDATE(RK3399_DSI1_LCDC_SEL,
> +					    RK3399_DSI1_LCDC_SEL),
> +
> +		.lanecfg1_grf_reg = RK3399_GRF_SOC_CON23,
> +		.lanecfg1 = HIWORD_UPDATE(0, RK3399_DSI1_TURNDISABLE |
> +					     RK3399_DSI1_FORCETXSTOPMODE |
> +					     RK3399_DSI1_FORCERXMODE |
> +					     RK3399_DSI1_ENABLE),
> +
> +		.lanecfg2_grf_reg = RK3399_GRF_SOC_CON24,
> +		.lanecfg2 = HIWORD_UPDATE(RK3399_TXRX_MASTERSLAVEZ |
> +					  RK3399_TXRX_ENABLECLK,
> +					  RK3399_TXRX_MASTERSLAVEZ |
> +					  RK3399_TXRX_ENABLECLK |
> +					  RK3399_TXRX_BASEDIR),
> +
> +		.enable_grf_reg = RK3399_GRF_SOC_CON23,
> +		.enable = HIWORD_UPDATE(RK3399_DSI1_ENABLE, RK3399_DSI1_ENABLE),
> +
> +		.flags = DW_MIPI_NEEDS_PHY_CFG_CLK | DW_MIPI_NEEDS_GRF_CLK,
> +		.max_data_lanes = 4,
> +	},
> +	{ /* sentinel */ }
> +};
> +
> +static const struct of_device_id dw_mipi_dsi_rockchip_dt_ids[] = {
> +	{
> +	 .compatible = "rockchip,rk3288-mipi-dsi",
> +	 .data = &rk3288_chip_data,
> +	}, {
> +	 .compatible = "rockchip,rk3399-mipi-dsi",
> +	 .data = &rk3399_chip_data,
> +	},
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, dw_mipi_dsi_rockchip_dt_ids);
> +
> +struct platform_driver dw_mipi_dsi_rockchip_driver = {
> +	.probe		= dw_mipi_dsi_rockchip_probe,
> +	.remove		= dw_mipi_dsi_rockchip_remove,
> +	.driver		= {
> +		.of_match_table = dw_mipi_dsi_rockchip_dt_ids,
> +		.name	= "dw-mipi-dsi-rockchip",
> +	},
> +};
> diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
> deleted file mode 100644
> index d53d5a09547f..000000000000
> --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
> +++ /dev/null
> @@ -1,1349 +0,0 @@
> -/*
> - * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; either version 2 of the License, or
> - * (at your option) any later version.
> - */
> -#include <linux/clk.h>
> -#include <linux/component.h>
> -#include <linux/iopoll.h>
> -#include <linux/math64.h>
> -#include <linux/module.h>
> -#include <linux/of_device.h>
> -#include <linux/pm_runtime.h>
> -#include <linux/regmap.h>
> -#include <linux/reset.h>
> -#include <linux/mfd/syscon.h>
> -#include <drm/drm_atomic_helper.h>
> -#include <drm/drm_crtc.h>
> -#include <drm/drm_crtc_helper.h>
> -#include <drm/drm_mipi_dsi.h>
> -#include <drm/drm_of.h>
> -#include <drm/drm_panel.h>
> -#include <drm/drmP.h>
> -#include <video/mipi_display.h>
> -
> -#include "rockchip_drm_drv.h"
> -#include "rockchip_drm_vop.h"
> -
> -#define DRIVER_NAME    "dw-mipi-dsi"
> -
> -#define RK3288_GRF_SOC_CON6		0x025c
> -#define RK3288_DSI0_SEL_VOP_LIT		BIT(6)
> -#define RK3288_DSI1_SEL_VOP_LIT		BIT(9)
> -
> -#define RK3399_GRF_SOC_CON20		0x6250
> -#define RK3399_DSI0_SEL_VOP_LIT		BIT(0)
> -#define RK3399_DSI1_SEL_VOP_LIT		BIT(4)
> -
> -/* disable turnrequest, turndisable, forcetxstopmode, forcerxmode */
> -#define RK3399_GRF_SOC_CON22		0x6258
> -#define RK3399_GRF_DSI_MODE		0xffff0000
> -
> -#define DSI_VERSION			0x00
> -#define DSI_PWR_UP			0x04
> -#define RESET				0
> -#define POWERUP				BIT(0)
> -
> -#define DSI_CLKMGR_CFG			0x08
> -#define TO_CLK_DIVIDSION(div)		(((div) & 0xff) << 8)
> -#define TX_ESC_CLK_DIVIDSION(div)	(((div) & 0xff) << 0)
> -
> -#define DSI_DPI_VCID			0x0c
> -#define DPI_VID(vid)			(((vid) & 0x3) << 0)
> -
> -#define DSI_DPI_COLOR_CODING		0x10
> -#define EN18_LOOSELY			BIT(8)
> -#define DPI_COLOR_CODING_16BIT_1	0x0
> -#define DPI_COLOR_CODING_16BIT_2	0x1
> -#define DPI_COLOR_CODING_16BIT_3	0x2
> -#define DPI_COLOR_CODING_18BIT_1	0x3
> -#define DPI_COLOR_CODING_18BIT_2	0x4
> -#define DPI_COLOR_CODING_24BIT		0x5
> -
> -#define DSI_DPI_CFG_POL			0x14
> -#define COLORM_ACTIVE_LOW		BIT(4)
> -#define SHUTD_ACTIVE_LOW		BIT(3)
> -#define HSYNC_ACTIVE_LOW		BIT(2)
> -#define VSYNC_ACTIVE_LOW		BIT(1)
> -#define DATAEN_ACTIVE_LOW		BIT(0)
> -
> -#define DSI_DPI_LP_CMD_TIM		0x18
> -#define OUTVACT_LPCMD_TIME(p)		(((p) & 0xff) << 16)
> -#define INVACT_LPCMD_TIME(p)		((p) & 0xff)
> -
> -#define DSI_DBI_CFG			0x20
> -#define DSI_DBI_CMDSIZE			0x28
> -
> -#define DSI_PCKHDL_CFG			0x2c
> -#define EN_CRC_RX			BIT(4)
> -#define EN_ECC_RX			BIT(3)
> -#define EN_BTA				BIT(2)
> -#define EN_EOTP_RX			BIT(1)
> -#define EN_EOTP_TX			BIT(0)
> -
> -#define DSI_MODE_CFG			0x34
> -#define ENABLE_VIDEO_MODE		0
> -#define ENABLE_CMD_MODE			BIT(0)
> -
> -#define DSI_VID_MODE_CFG		0x38
> -#define FRAME_BTA_ACK			BIT(14)
> -#define ENABLE_LOW_POWER		(0x3f << 8)
> -#define ENABLE_LOW_POWER_MASK		(0x3f << 8)
> -#define VID_MODE_TYPE_NON_BURST_SYNC_PULSES	0x0
> -#define VID_MODE_TYPE_NON_BURST_SYNC_EVENTS	0x1
> -#define VID_MODE_TYPE_BURST			0x2
> -#define VID_MODE_TYPE_MASK			0x3
> -
> -#define DSI_VID_PKT_SIZE		0x3c
> -#define VID_PKT_SIZE(p)			(((p) & 0x3fff) << 0)
> -#define VID_PKT_MAX_SIZE		0x3fff
> -
> -#define DSI_VID_HSA_TIME		0x48
> -#define DSI_VID_HBP_TIME		0x4c
> -#define DSI_VID_HLINE_TIME		0x50
> -#define DSI_VID_VSA_LINES		0x54
> -#define DSI_VID_VBP_LINES		0x58
> -#define DSI_VID_VFP_LINES		0x5c
> -#define DSI_VID_VACTIVE_LINES		0x60
> -#define DSI_CMD_MODE_CFG		0x68
> -#define MAX_RD_PKT_SIZE_LP		BIT(24)
> -#define DCS_LW_TX_LP			BIT(19)
> -#define DCS_SR_0P_TX_LP			BIT(18)
> -#define DCS_SW_1P_TX_LP			BIT(17)
> -#define DCS_SW_0P_TX_LP			BIT(16)
> -#define GEN_LW_TX_LP			BIT(14)
> -#define GEN_SR_2P_TX_LP			BIT(13)
> -#define GEN_SR_1P_TX_LP			BIT(12)
> -#define GEN_SR_0P_TX_LP			BIT(11)
> -#define GEN_SW_2P_TX_LP			BIT(10)
> -#define GEN_SW_1P_TX_LP			BIT(9)
> -#define GEN_SW_0P_TX_LP			BIT(8)
> -#define EN_ACK_RQST			BIT(1)
> -#define EN_TEAR_FX			BIT(0)
> -
> -#define CMD_MODE_ALL_LP			(MAX_RD_PKT_SIZE_LP | \
> -					 DCS_LW_TX_LP | \
> -					 DCS_SR_0P_TX_LP | \
> -					 DCS_SW_1P_TX_LP | \
> -					 DCS_SW_0P_TX_LP | \
> -					 GEN_LW_TX_LP | \
> -					 GEN_SR_2P_TX_LP | \
> -					 GEN_SR_1P_TX_LP | \
> -					 GEN_SR_0P_TX_LP | \
> -					 GEN_SW_2P_TX_LP | \
> -					 GEN_SW_1P_TX_LP | \
> -					 GEN_SW_0P_TX_LP)
> -
> -#define DSI_GEN_HDR			0x6c
> -#define GEN_HDATA(data)			(((data) & 0xffff) << 8)
> -#define GEN_HDATA_MASK			(0xffff << 8)
> -#define GEN_HTYPE(type)			(((type) & 0xff) << 0)
> -#define GEN_HTYPE_MASK			0xff
> -
> -#define DSI_GEN_PLD_DATA		0x70
> -
> -#define DSI_CMD_PKT_STATUS		0x74
> -#define GEN_CMD_EMPTY			BIT(0)
> -#define GEN_CMD_FULL			BIT(1)
> -#define GEN_PLD_W_EMPTY			BIT(2)
> -#define GEN_PLD_W_FULL			BIT(3)
> -#define GEN_PLD_R_EMPTY			BIT(4)
> -#define GEN_PLD_R_FULL			BIT(5)
> -#define GEN_RD_CMD_BUSY			BIT(6)
> -
> -#define DSI_TO_CNT_CFG			0x78
> -#define HSTX_TO_CNT(p)			(((p) & 0xffff) << 16)
> -#define LPRX_TO_CNT(p)			((p) & 0xffff)
> -
> -#define DSI_BTA_TO_CNT			0x8c
> -#define DSI_LPCLK_CTRL			0x94
> -#define AUTO_CLKLANE_CTRL		BIT(1)
> -#define PHY_TXREQUESTCLKHS		BIT(0)
> -
> -#define DSI_PHY_TMR_LPCLK_CFG		0x98
> -#define PHY_CLKHS2LP_TIME(lbcc)		(((lbcc) & 0x3ff) << 16)
> -#define PHY_CLKLP2HS_TIME(lbcc)		((lbcc) & 0x3ff)
> -
> -#define DSI_PHY_TMR_CFG			0x9c
> -#define PHY_HS2LP_TIME(lbcc)		(((lbcc) & 0xff) << 24)
> -#define PHY_LP2HS_TIME(lbcc)		(((lbcc) & 0xff) << 16)
> -#define MAX_RD_TIME(lbcc)		((lbcc) & 0x7fff)
> -
> -#define DSI_PHY_RSTZ			0xa0
> -#define PHY_DISFORCEPLL			0
> -#define PHY_ENFORCEPLL			BIT(3)
> -#define PHY_DISABLECLK			0
> -#define PHY_ENABLECLK			BIT(2)
> -#define PHY_RSTZ			0
> -#define PHY_UNRSTZ			BIT(1)
> -#define PHY_SHUTDOWNZ			0
> -#define PHY_UNSHUTDOWNZ			BIT(0)
> -
> -#define DSI_PHY_IF_CFG			0xa4
> -#define N_LANES(n)			((((n) - 1) & 0x3) << 0)
> -#define PHY_STOP_WAIT_TIME(cycle)	(((cycle) & 0xff) << 8)
> -
> -#define DSI_PHY_STATUS			0xb0
> -#define LOCK				BIT(0)
> -#define STOP_STATE_CLK_LANE		BIT(2)
> -
> -#define DSI_PHY_TST_CTRL0		0xb4
> -#define PHY_TESTCLK			BIT(1)
> -#define PHY_UNTESTCLK			0
> -#define PHY_TESTCLR			BIT(0)
> -#define PHY_UNTESTCLR			0
> -
> -#define DSI_PHY_TST_CTRL1		0xb8
> -#define PHY_TESTEN			BIT(16)
> -#define PHY_UNTESTEN			0
> -#define PHY_TESTDOUT(n)			(((n) & 0xff) << 8)
> -#define PHY_TESTDIN(n)			(((n) & 0xff) << 0)
> -
> -#define DSI_INT_ST0			0xbc
> -#define DSI_INT_ST1			0xc0
> -#define DSI_INT_MSK0			0xc4
> -#define DSI_INT_MSK1			0xc8
> -
> -#define PHY_STATUS_TIMEOUT_US		10000
> -#define CMD_PKT_STATUS_TIMEOUT_US	20000
> -
> -#define BYPASS_VCO_RANGE	BIT(7)
> -#define VCO_RANGE_CON_SEL(val)	(((val) & 0x7) << 3)
> -#define VCO_IN_CAP_CON_DEFAULT	(0x0 << 1)
> -#define VCO_IN_CAP_CON_LOW	(0x1 << 1)
> -#define VCO_IN_CAP_CON_HIGH	(0x2 << 1)
> -#define REF_BIAS_CUR_SEL	BIT(0)
> -
> -#define CP_CURRENT_3MA		BIT(3)
> -#define CP_PROGRAM_EN		BIT(7)
> -#define LPF_PROGRAM_EN		BIT(6)
> -#define LPF_RESISTORS_20_KOHM	0
> -
> -#define HSFREQRANGE_SEL(val)	(((val) & 0x3f) << 1)
> -
> -#define INPUT_DIVIDER(val)	(((val) - 1) & 0x7f)
> -#define LOW_PROGRAM_EN		0
> -#define HIGH_PROGRAM_EN		BIT(7)
> -#define LOOP_DIV_LOW_SEL(val)	(((val) - 1) & 0x1f)
> -#define LOOP_DIV_HIGH_SEL(val)	((((val) - 1) >> 5) & 0x1f)
> -#define PLL_LOOP_DIV_EN		BIT(5)
> -#define PLL_INPUT_DIV_EN	BIT(4)
> -
> -#define POWER_CONTROL		BIT(6)
> -#define INTERNAL_REG_CURRENT	BIT(3)
> -#define BIAS_BLOCK_ON		BIT(2)
> -#define BANDGAP_ON		BIT(0)
> -
> -#define TER_RESISTOR_HIGH	BIT(7)
> -#define	TER_RESISTOR_LOW	0
> -#define LEVEL_SHIFTERS_ON	BIT(6)
> -#define TER_CAL_DONE		BIT(5)
> -#define SETRD_MAX		(0x7 << 2)
> -#define POWER_MANAGE		BIT(1)
> -#define TER_RESISTORS_ON	BIT(0)
> -
> -#define BIASEXTR_SEL(val)	((val) & 0x7)
> -#define BANDGAP_SEL(val)	((val) & 0x7)
> -#define TLP_PROGRAM_EN		BIT(7)
> -#define THS_PRE_PROGRAM_EN	BIT(7)
> -#define THS_ZERO_PROGRAM_EN	BIT(6)
> -
> -#define DW_MIPI_NEEDS_PHY_CFG_CLK	BIT(0)
> -#define DW_MIPI_NEEDS_GRF_CLK		BIT(1)
> -
> -enum {
> -	BANDGAP_97_07,
> -	BANDGAP_98_05,
> -	BANDGAP_99_02,
> -	BANDGAP_100_00,
> -	BANDGAP_93_17,
> -	BANDGAP_94_15,
> -	BANDGAP_95_12,
> -	BANDGAP_96_10,
> -};
> -
> -enum {
> -	BIASEXTR_87_1,
> -	BIASEXTR_91_5,
> -	BIASEXTR_95_9,
> -	BIASEXTR_100,
> -	BIASEXTR_105_94,
> -	BIASEXTR_111_88,
> -	BIASEXTR_118_8,
> -	BIASEXTR_127_7,
> -};
> -
> -struct dw_mipi_dsi_plat_data {
> -	u32 dsi0_en_bit;
> -	u32 dsi1_en_bit;
> -	u32 grf_switch_reg;
> -	u32 grf_dsi0_mode;
> -	u32 grf_dsi0_mode_reg;
> -	unsigned int flags;
> -	unsigned int max_data_lanes;
> -};
> -
> -struct dw_mipi_dsi {
> -	struct drm_encoder encoder;
> -	struct drm_connector connector;
> -	struct mipi_dsi_host dsi_host;
> -	struct drm_panel *panel;
> -	struct device *dev;
> -	struct regmap *grf_regmap;
> -	void __iomem *base;
> -
> -	struct clk *grf_clk;
> -	struct clk *pllref_clk;
> -	struct clk *pclk;
> -	struct clk *phy_cfg_clk;
> -
> -	int dpms_mode;
> -	unsigned int lane_mbps; /* per lane */
> -	u32 channel;
> -	u32 lanes;
> -	u32 format;
> -	u16 input_div;
> -	u16 feedback_div;
> -	unsigned long mode_flags;
> -
> -	const struct dw_mipi_dsi_plat_data *pdata;
> -};
> -
> -enum dw_mipi_dsi_mode {
> -	DW_MIPI_DSI_CMD_MODE,
> -	DW_MIPI_DSI_VID_MODE,
> -};
> -
> -struct dphy_pll_testdin_map {
> -	unsigned int max_mbps;
> -	u8 testdin;
> -};
> -
> -/* The table is based on 27MHz DPHY pll reference clock. */
> -static const struct dphy_pll_testdin_map dptdin_map[] = {
> -	{  90, 0x00}, { 100, 0x10}, { 110, 0x20}, { 130, 0x01},
> -	{ 140, 0x11}, { 150, 0x21}, { 170, 0x02}, { 180, 0x12},
> -	{ 200, 0x22}, { 220, 0x03}, { 240, 0x13}, { 250, 0x23},
> -	{ 270, 0x04}, { 300, 0x14}, { 330, 0x05}, { 360, 0x15},
> -	{ 400, 0x25}, { 450, 0x06}, { 500, 0x16}, { 550, 0x07},
> -	{ 600, 0x17}, { 650, 0x08}, { 700, 0x18}, { 750, 0x09},
> -	{ 800, 0x19}, { 850, 0x29}, { 900, 0x39}, { 950, 0x0a},
> -	{1000, 0x1a}, {1050, 0x2a}, {1100, 0x3a}, {1150, 0x0b},
> -	{1200, 0x1b}, {1250, 0x2b}, {1300, 0x3b}, {1350, 0x0c},
> -	{1400, 0x1c}, {1450, 0x2c}, {1500, 0x3c}
> -};
> -
> -static int max_mbps_to_testdin(unsigned int max_mbps)
> -{
> -	int i;
> -
> -	for (i = 0; i < ARRAY_SIZE(dptdin_map); i++)
> -		if (dptdin_map[i].max_mbps > max_mbps)
> -			return dptdin_map[i].testdin;
> -
> -	return -EINVAL;
> -}
> -
> -/*
> - * The controller should generate 2 frames before
> - * preparing the peripheral.
> - */
> -static void dw_mipi_dsi_wait_for_two_frames(struct drm_display_mode *mode)
> -{
> -	int refresh, two_frames;
> -
> -	refresh = drm_mode_vrefresh(mode);
> -	two_frames = DIV_ROUND_UP(MSEC_PER_SEC, refresh) * 2;
> -	msleep(two_frames);
> -}
> -
> -static inline struct dw_mipi_dsi *host_to_dsi(struct mipi_dsi_host *host)
> -{
> -	return container_of(host, struct dw_mipi_dsi, dsi_host);
> -}
> -
> -static inline struct dw_mipi_dsi *con_to_dsi(struct drm_connector *con)
> -{
> -	return container_of(con, struct dw_mipi_dsi, connector);
> -}
> -
> -static inline struct dw_mipi_dsi *encoder_to_dsi(struct drm_encoder *encoder)
> -{
> -	return container_of(encoder, struct dw_mipi_dsi, encoder);
> -}
> -
> -static inline void dsi_write(struct dw_mipi_dsi *dsi, u32 reg, u32 val)
> -{
> -	writel(val, dsi->base + reg);
> -}
> -
> -static inline u32 dsi_read(struct dw_mipi_dsi *dsi, u32 reg)
> -{
> -	return readl(dsi->base + reg);
> -}
> -
> -static void dw_mipi_dsi_phy_write(struct dw_mipi_dsi *dsi, u8 test_code,
> -				  u8 test_data)
> -{
> -	/*
> -	 * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
> -	 * is latched internally as the current test code. Test data is
> -	 * programmed internally by rising edge on TESTCLK.
> -	 */
> -	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
> -
> -	dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_TESTEN | PHY_TESTDOUT(0) |
> -					  PHY_TESTDIN(test_code));
> -
> -	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLK | PHY_UNTESTCLR);
> -
> -	dsi_write(dsi, DSI_PHY_TST_CTRL1, PHY_UNTESTEN | PHY_TESTDOUT(0) |
> -					  PHY_TESTDIN(test_data));
> -
> -	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLK | PHY_UNTESTCLR);
> -}
> -
> -/**
> - * ns2bc - Nanoseconds to byte clock cycles
> - */
> -static inline unsigned int ns2bc(struct dw_mipi_dsi *dsi, int ns)
> -{
> -	return DIV_ROUND_UP(ns * dsi->lane_mbps / 8, 1000);
> -}
> -
> -/**
> - * ns2ui - Nanoseconds to UI time periods
> - */
> -static inline unsigned int ns2ui(struct dw_mipi_dsi *dsi, int ns)
> -{
> -	return DIV_ROUND_UP(ns * dsi->lane_mbps, 1000);
> -}
> -
> -static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
> -{
> -	int ret, testdin, vco, val;
> -
> -	vco = (dsi->lane_mbps < 200) ? 0 : (dsi->lane_mbps + 100) / 200;
> -
> -	testdin = max_mbps_to_testdin(dsi->lane_mbps);
> -	if (testdin < 0) {
> -		DRM_DEV_ERROR(dsi->dev,
> -			      "failed to get testdin for %dmbps lane clock\n",
> -			      dsi->lane_mbps);
> -		return testdin;
> -	}
> -
> -	/* Start by clearing PHY state */
> -	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLR);
> -	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_TESTCLR);
> -	dsi_write(dsi, DSI_PHY_TST_CTRL0, PHY_UNTESTCLR);
> -
> -	ret = clk_prepare_enable(dsi->phy_cfg_clk);
> -	if (ret) {
> -		DRM_DEV_ERROR(dsi->dev, "Failed to enable phy_cfg_clk\n");
> -		return ret;
> -	}
> -
> -	dw_mipi_dsi_phy_write(dsi, 0x10, BYPASS_VCO_RANGE |
> -					 VCO_RANGE_CON_SEL(vco) |
> -					 VCO_IN_CAP_CON_LOW |
> -					 REF_BIAS_CUR_SEL);
> -
> -	dw_mipi_dsi_phy_write(dsi, 0x11, CP_CURRENT_3MA);
> -	dw_mipi_dsi_phy_write(dsi, 0x12, CP_PROGRAM_EN | LPF_PROGRAM_EN |
> -					 LPF_RESISTORS_20_KOHM);
> -
> -	dw_mipi_dsi_phy_write(dsi, 0x44, HSFREQRANGE_SEL(testdin));
> -
> -	dw_mipi_dsi_phy_write(dsi, 0x17, INPUT_DIVIDER(dsi->input_div));
> -	dw_mipi_dsi_phy_write(dsi, 0x18, LOOP_DIV_LOW_SEL(dsi->feedback_div) |
> -					 LOW_PROGRAM_EN);
> -	dw_mipi_dsi_phy_write(dsi, 0x18, LOOP_DIV_HIGH_SEL(dsi->feedback_div) |
> -					 HIGH_PROGRAM_EN);
> -	dw_mipi_dsi_phy_write(dsi, 0x19, PLL_LOOP_DIV_EN | PLL_INPUT_DIV_EN);
> -
> -	dw_mipi_dsi_phy_write(dsi, 0x22, LOW_PROGRAM_EN |
> -					 BIASEXTR_SEL(BIASEXTR_127_7));
> -	dw_mipi_dsi_phy_write(dsi, 0x22, HIGH_PROGRAM_EN |
> -					 BANDGAP_SEL(BANDGAP_96_10));
> -
> -	dw_mipi_dsi_phy_write(dsi, 0x20, POWER_CONTROL | INTERNAL_REG_CURRENT |
> -					 BIAS_BLOCK_ON | BANDGAP_ON);
> -
> -	dw_mipi_dsi_phy_write(dsi, 0x21, TER_RESISTOR_LOW | TER_CAL_DONE |
> -					 SETRD_MAX | TER_RESISTORS_ON);
> -	dw_mipi_dsi_phy_write(dsi, 0x21, TER_RESISTOR_HIGH | LEVEL_SHIFTERS_ON |
> -					 SETRD_MAX | POWER_MANAGE |
> -					 TER_RESISTORS_ON);
> -
> -	dw_mipi_dsi_phy_write(dsi, 0x60, TLP_PROGRAM_EN | ns2bc(dsi, 500));
> -	dw_mipi_dsi_phy_write(dsi, 0x61, THS_PRE_PROGRAM_EN | ns2ui(dsi, 40));
> -	dw_mipi_dsi_phy_write(dsi, 0x62, THS_ZERO_PROGRAM_EN | ns2bc(dsi, 300));
> -	dw_mipi_dsi_phy_write(dsi, 0x63, THS_PRE_PROGRAM_EN | ns2ui(dsi, 100));
> -	dw_mipi_dsi_phy_write(dsi, 0x64, BIT(5) | ns2bc(dsi, 100));
> -	dw_mipi_dsi_phy_write(dsi, 0x65, BIT(5) | (ns2bc(dsi, 60) + 7));
> -
> -	dw_mipi_dsi_phy_write(dsi, 0x70, TLP_PROGRAM_EN | ns2bc(dsi, 500));
> -	dw_mipi_dsi_phy_write(dsi, 0x71,
> -			      THS_PRE_PROGRAM_EN | (ns2ui(dsi, 50) + 5));
> -	dw_mipi_dsi_phy_write(dsi, 0x72,
> -			      THS_ZERO_PROGRAM_EN | (ns2bc(dsi, 140) + 2));
> -	dw_mipi_dsi_phy_write(dsi, 0x73,
> -			      THS_PRE_PROGRAM_EN | (ns2ui(dsi, 60) + 8));
> -	dw_mipi_dsi_phy_write(dsi, 0x74, BIT(5) | ns2bc(dsi, 100));
> -
> -	dsi_write(dsi, DSI_PHY_RSTZ, PHY_ENFORCEPLL | PHY_ENABLECLK |
> -				     PHY_UNRSTZ | PHY_UNSHUTDOWNZ);
> -
> -	ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS,
> -				 val, val & LOCK, 1000, PHY_STATUS_TIMEOUT_US);
> -	if (ret < 0) {
> -		DRM_DEV_ERROR(dsi->dev, "failed to wait for phy lock state\n");
> -		goto phy_init_end;
> -	}
> -
> -	ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS,
> -				 val, val & STOP_STATE_CLK_LANE, 1000,
> -				 PHY_STATUS_TIMEOUT_US);
> -	if (ret < 0)
> -		DRM_DEV_ERROR(dsi->dev,
> -			      "failed to wait for phy clk lane stop state\n");
> -
> -phy_init_end:
> -	clk_disable_unprepare(dsi->phy_cfg_clk);
> -
> -	return ret;
> -}
> -
> -static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi,
> -				    struct drm_display_mode *mode)
> -{
> -	unsigned int i, pre;
> -	unsigned long mpclk, pllref, tmp;
> -	unsigned int m = 1, n = 1, target_mbps = 1000;
> -	unsigned int max_mbps = dptdin_map[ARRAY_SIZE(dptdin_map) - 1].max_mbps;
> -	int bpp;
> -
> -	bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
> -	if (bpp < 0) {
> -		DRM_DEV_ERROR(dsi->dev,
> -			      "failed to get bpp for pixel format %d\n",
> -			      dsi->format);
> -		return bpp;
> -	}
> -
> -	mpclk = DIV_ROUND_UP(mode->clock, MSEC_PER_SEC);
> -	if (mpclk) {
> -		/* take 1 / 0.8, since mbps must big than bandwidth of RGB */
> -		tmp = mpclk * (bpp / dsi->lanes) * 10 / 8;
> -		if (tmp < max_mbps)
> -			target_mbps = tmp;
> -		else
> -			DRM_DEV_ERROR(dsi->dev,
> -				      "DPHY clock frequency is out of range\n");
> -	}
> -
> -	pllref = DIV_ROUND_UP(clk_get_rate(dsi->pllref_clk), USEC_PER_SEC);
> -	tmp = pllref;
> -
> -	/*
> -	 * The limits on the PLL divisor are:
> -	 *
> -	 *	5MHz <= (pllref / n) <= 40MHz
> -	 *
> -	 * we walk over these values in descreasing order so that if we hit
> -	 * an exact match for target_mbps it is more likely that "m" will be
> -	 * even.
> -	 *
> -	 * TODO: ensure that "m" is even after this loop.
> -	 */
> -	for (i = pllref / 5; i > (pllref / 40); i--) {
> -		pre = pllref / i;
> -		if ((tmp > (target_mbps % pre)) && (target_mbps / pre < 512)) {
> -			tmp = target_mbps % pre;
> -			n = i;
> -			m = target_mbps / pre;
> -		}
> -		if (tmp == 0)
> -			break;
> -	}
> -
> -	dsi->lane_mbps = pllref / n * m;
> -	dsi->input_div = n;
> -	dsi->feedback_div = m;
> -
> -	return 0;
> -}
> -
> -static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
> -				   struct mipi_dsi_device *device)
> -{
> -	struct dw_mipi_dsi *dsi = host_to_dsi(host);
> -
> -	if (device->lanes > dsi->pdata->max_data_lanes) {
> -		DRM_DEV_ERROR(dsi->dev,
> -			      "the number of data lanes(%u) is too many\n",
> -			      device->lanes);
> -		return -EINVAL;
> -	}
> -
> -	dsi->lanes = device->lanes;
> -	dsi->channel = device->channel;
> -	dsi->format = device->format;
> -	dsi->mode_flags = device->mode_flags;
> -	dsi->panel = of_drm_find_panel(device->dev.of_node);
> -	if (dsi->panel)
> -		return drm_panel_attach(dsi->panel, &dsi->connector);
> -
> -	return -EINVAL;
> -}
> -
> -static int dw_mipi_dsi_host_detach(struct mipi_dsi_host *host,
> -				   struct mipi_dsi_device *device)
> -{
> -	struct dw_mipi_dsi *dsi = host_to_dsi(host);
> -
> -	drm_panel_detach(dsi->panel);
> -
> -	return 0;
> -}
> -
> -static void dw_mipi_message_config(struct dw_mipi_dsi *dsi,
> -				   const struct mipi_dsi_msg *msg)
> -{
> -	bool lpm = msg->flags & MIPI_DSI_MSG_USE_LPM;
> -	u32 val = 0;
> -
> -	if (msg->flags & MIPI_DSI_MSG_REQ_ACK)
> -		val |= EN_ACK_RQST;
> -	if (lpm)
> -		val |= CMD_MODE_ALL_LP;
> -
> -	dsi_write(dsi, DSI_LPCLK_CTRL, lpm ? 0 : PHY_TXREQUESTCLKHS);
> -	dsi_write(dsi, DSI_CMD_MODE_CFG, val);
> -}
> -
> -static int dw_mipi_dsi_gen_pkt_hdr_write(struct dw_mipi_dsi *dsi, u32 hdr_val)
> -{
> -	int ret;
> -	u32 val, mask;
> -
> -	ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
> -				 val, !(val & GEN_CMD_FULL), 1000,
> -				 CMD_PKT_STATUS_TIMEOUT_US);
> -	if (ret < 0) {
> -		DRM_DEV_ERROR(dsi->dev,
> -			      "failed to get available command FIFO\n");
> -		return ret;
> -	}
> -
> -	dsi_write(dsi, DSI_GEN_HDR, hdr_val);
> -
> -	mask = GEN_CMD_EMPTY | GEN_PLD_W_EMPTY;
> -	ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
> -				 val, (val & mask) == mask,
> -				 1000, CMD_PKT_STATUS_TIMEOUT_US);
> -	if (ret < 0) {
> -		DRM_DEV_ERROR(dsi->dev, "failed to write command FIFO\n");
> -		return ret;
> -	}
> -
> -	return 0;
> -}
> -
> -static int dw_mipi_dsi_dcs_short_write(struct dw_mipi_dsi *dsi,
> -				       const struct mipi_dsi_msg *msg)
> -{
> -	const u8 *tx_buf = msg->tx_buf;
> -	u16 data = 0;
> -	u32 val;
> -
> -	if (msg->tx_len > 0)
> -		data |= tx_buf[0];
> -	if (msg->tx_len > 1)
> -		data |= tx_buf[1] << 8;
> -
> -	if (msg->tx_len > 2) {
> -		DRM_DEV_ERROR(dsi->dev,
> -			      "too long tx buf length %zu for short write\n",
> -			      msg->tx_len);
> -		return -EINVAL;
> -	}
> -
> -	val = GEN_HDATA(data) | GEN_HTYPE(msg->type);
> -	return dw_mipi_dsi_gen_pkt_hdr_write(dsi, val);
> -}
> -
> -static int dw_mipi_dsi_dcs_long_write(struct dw_mipi_dsi *dsi,
> -				      const struct mipi_dsi_msg *msg)
> -{
> -	const u8 *tx_buf = msg->tx_buf;
> -	int len = msg->tx_len, pld_data_bytes = sizeof(u32), ret;
> -	u32 hdr_val = GEN_HDATA(msg->tx_len) | GEN_HTYPE(msg->type);
> -	u32 remainder;
> -	u32 val;
> -
> -	if (msg->tx_len < 3) {
> -		DRM_DEV_ERROR(dsi->dev,
> -			      "wrong tx buf length %zu for long write\n",
> -			      msg->tx_len);
> -		return -EINVAL;
> -	}
> -
> -	while (DIV_ROUND_UP(len, pld_data_bytes)) {
> -		if (len < pld_data_bytes) {
> -			remainder = 0;
> -			memcpy(&remainder, tx_buf, len);
> -			dsi_write(dsi, DSI_GEN_PLD_DATA, remainder);
> -			len = 0;
> -		} else {
> -			memcpy(&remainder, tx_buf, pld_data_bytes);
> -			dsi_write(dsi, DSI_GEN_PLD_DATA, remainder);
> -			tx_buf += pld_data_bytes;
> -			len -= pld_data_bytes;
> -		}
> -
> -		ret = readl_poll_timeout(dsi->base + DSI_CMD_PKT_STATUS,
> -					 val, !(val & GEN_PLD_W_FULL), 1000,
> -					 CMD_PKT_STATUS_TIMEOUT_US);
> -		if (ret < 0) {
> -			DRM_DEV_ERROR(dsi->dev,
> -				      "failed to get available write payload FIFO\n");
> -			return ret;
> -		}
> -	}
> -
> -	return dw_mipi_dsi_gen_pkt_hdr_write(dsi, hdr_val);
> -}
> -
> -static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host,
> -					 const struct mipi_dsi_msg *msg)
> -{
> -	struct dw_mipi_dsi *dsi = host_to_dsi(host);
> -	int ret;
> -
> -	dw_mipi_message_config(dsi, msg);
> -
> -	switch (msg->type) {
> -	case MIPI_DSI_DCS_SHORT_WRITE:
> -	case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
> -	case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
> -		ret = dw_mipi_dsi_dcs_short_write(dsi, msg);
> -		break;
> -	case MIPI_DSI_DCS_LONG_WRITE:
> -		ret = dw_mipi_dsi_dcs_long_write(dsi, msg);
> -		break;
> -	default:
> -		DRM_DEV_ERROR(dsi->dev, "unsupported message type 0x%02x\n",
> -			      msg->type);
> -		ret = -EINVAL;
> -	}
> -
> -	return ret;
> -}
> -
> -static const struct mipi_dsi_host_ops dw_mipi_dsi_host_ops = {
> -	.attach = dw_mipi_dsi_host_attach,
> -	.detach = dw_mipi_dsi_host_detach,
> -	.transfer = dw_mipi_dsi_host_transfer,
> -};
> -
> -static void dw_mipi_dsi_video_mode_config(struct dw_mipi_dsi *dsi)
> -{
> -	u32 val;
> -
> -	val = ENABLE_LOW_POWER;
> -
> -	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
> -		val |= VID_MODE_TYPE_BURST;
> -	else if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
> -		val |= VID_MODE_TYPE_NON_BURST_SYNC_PULSES;
> -	else
> -		val |= VID_MODE_TYPE_NON_BURST_SYNC_EVENTS;
> -
> -	dsi_write(dsi, DSI_VID_MODE_CFG, val);
> -}
> -
> -static void dw_mipi_dsi_set_mode(struct dw_mipi_dsi *dsi,
> -				 enum dw_mipi_dsi_mode mode)
> -{
> -	if (mode == DW_MIPI_DSI_CMD_MODE) {
> -		dsi_write(dsi, DSI_PWR_UP, RESET);
> -		dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE);
> -		dsi_write(dsi, DSI_PWR_UP, POWERUP);
> -	} else {
> -		dsi_write(dsi, DSI_PWR_UP, RESET);
> -		dsi_write(dsi, DSI_MODE_CFG, ENABLE_VIDEO_MODE);
> -		dw_mipi_dsi_video_mode_config(dsi);
> -		dsi_write(dsi, DSI_LPCLK_CTRL, PHY_TXREQUESTCLKHS);
> -		dsi_write(dsi, DSI_PWR_UP, POWERUP);
> -	}
> -}
> -
> -static void dw_mipi_dsi_disable(struct dw_mipi_dsi *dsi)
> -{
> -	dsi_write(dsi, DSI_PWR_UP, RESET);
> -	dsi_write(dsi, DSI_PHY_RSTZ, PHY_RSTZ);
> -}
> -
> -static void dw_mipi_dsi_init(struct dw_mipi_dsi *dsi)
> -{
> -	/*
> -	 * The maximum permitted escape clock is 20MHz and it is derived from
> -	 * lanebyteclk, which is running at "lane_mbps / 8".  Thus we want:
> -	 *
> -	 *     (lane_mbps >> 3) / esc_clk_division < 20
> -	 * which is:
> -	 *     (lane_mbps >> 3) / 20 > esc_clk_division
> -	 */
> -	u32 esc_clk_division = (dsi->lane_mbps >> 3) / 20 + 1;
> -
> -	dsi_write(dsi, DSI_PWR_UP, RESET);
> -	dsi_write(dsi, DSI_PHY_RSTZ, PHY_DISFORCEPLL | PHY_DISABLECLK
> -		  | PHY_RSTZ | PHY_SHUTDOWNZ);
> -	dsi_write(dsi, DSI_CLKMGR_CFG, TO_CLK_DIVIDSION(10) |
> -		  TX_ESC_CLK_DIVIDSION(esc_clk_division));
> -}
> -
> -static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
> -				   struct drm_display_mode *mode)
> -{
> -	u32 val = 0, color = 0;
> -
> -	switch (dsi->format) {
> -	case MIPI_DSI_FMT_RGB888:
> -		color = DPI_COLOR_CODING_24BIT;
> -		break;
> -	case MIPI_DSI_FMT_RGB666:
> -		color = DPI_COLOR_CODING_18BIT_2 | EN18_LOOSELY;
> -		break;
> -	case MIPI_DSI_FMT_RGB666_PACKED:
> -		color = DPI_COLOR_CODING_18BIT_1;
> -		break;
> -	case MIPI_DSI_FMT_RGB565:
> -		color = DPI_COLOR_CODING_16BIT_1;
> -		break;
> -	}
> -
> -	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
> -		val |= VSYNC_ACTIVE_LOW;
> -	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
> -		val |= HSYNC_ACTIVE_LOW;
> -
> -	dsi_write(dsi, DSI_DPI_VCID, DPI_VID(dsi->channel));
> -	dsi_write(dsi, DSI_DPI_COLOR_CODING, color);
> -	dsi_write(dsi, DSI_DPI_CFG_POL, val);
> -	dsi_write(dsi, DSI_DPI_LP_CMD_TIM, OUTVACT_LPCMD_TIME(4)
> -		  | INVACT_LPCMD_TIME(4));
> -}
> -
> -static void dw_mipi_dsi_packet_handler_config(struct dw_mipi_dsi *dsi)
> -{
> -	dsi_write(dsi, DSI_PCKHDL_CFG, EN_CRC_RX | EN_ECC_RX | EN_BTA);
> -}
> -
> -static void dw_mipi_dsi_video_packet_config(struct dw_mipi_dsi *dsi,
> -					    struct drm_display_mode *mode)
> -{
> -	dsi_write(dsi, DSI_VID_PKT_SIZE, VID_PKT_SIZE(mode->hdisplay));
> -}
> -
> -static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi)
> -{
> -	dsi_write(dsi, DSI_TO_CNT_CFG, HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000));
> -	dsi_write(dsi, DSI_BTA_TO_CNT, 0xd00);
> -	dsi_write(dsi, DSI_MODE_CFG, ENABLE_CMD_MODE);
> -}
> -
> -/* Get lane byte clock cycles. */
> -static u32 dw_mipi_dsi_get_hcomponent_lbcc(struct dw_mipi_dsi *dsi,
> -					   struct drm_display_mode *mode,
> -					   u32 hcomponent)
> -{
> -	u32 frac, lbcc;
> -
> -	lbcc = hcomponent * dsi->lane_mbps * MSEC_PER_SEC / 8;
> -
> -	frac = lbcc % mode->clock;
> -	lbcc = lbcc / mode->clock;
> -	if (frac)
> -		lbcc++;
> -
> -	return lbcc;
> -}
> -
> -static void dw_mipi_dsi_line_timer_config(struct dw_mipi_dsi *dsi,
> -					  struct drm_display_mode *mode)
> -{
> -	u32 htotal, hsa, hbp, lbcc;
> -
> -	htotal = mode->htotal;
> -	hsa = mode->hsync_end - mode->hsync_start;
> -	hbp = mode->htotal - mode->hsync_end;
> -
> -	lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, mode, htotal);
> -	dsi_write(dsi, DSI_VID_HLINE_TIME, lbcc);
> -
> -	lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, mode, hsa);
> -	dsi_write(dsi, DSI_VID_HSA_TIME, lbcc);
> -
> -	lbcc = dw_mipi_dsi_get_hcomponent_lbcc(dsi, mode, hbp);
> -	dsi_write(dsi, DSI_VID_HBP_TIME, lbcc);
> -}
> -
> -static void dw_mipi_dsi_vertical_timing_config(struct dw_mipi_dsi *dsi,
> -					       struct drm_display_mode *mode)
> -{
> -	u32 vactive, vsa, vfp, vbp;
> -
> -	vactive = mode->vdisplay;
> -	vsa = mode->vsync_end - mode->vsync_start;
> -	vfp = mode->vsync_start - mode->vdisplay;
> -	vbp = mode->vtotal - mode->vsync_end;
> -
> -	dsi_write(dsi, DSI_VID_VACTIVE_LINES, vactive);
> -	dsi_write(dsi, DSI_VID_VSA_LINES, vsa);
> -	dsi_write(dsi, DSI_VID_VFP_LINES, vfp);
> -	dsi_write(dsi, DSI_VID_VBP_LINES, vbp);
> -}
> -
> -static void dw_mipi_dsi_dphy_timing_config(struct dw_mipi_dsi *dsi)
> -{
> -	dsi_write(dsi, DSI_PHY_TMR_CFG, PHY_HS2LP_TIME(0x40)
> -		  | PHY_LP2HS_TIME(0x40) | MAX_RD_TIME(10000));
> -
> -	dsi_write(dsi, DSI_PHY_TMR_LPCLK_CFG, PHY_CLKHS2LP_TIME(0x40)
> -		  | PHY_CLKLP2HS_TIME(0x40));
> -}
> -
> -static void dw_mipi_dsi_dphy_interface_config(struct dw_mipi_dsi *dsi)
> -{
> -	dsi_write(dsi, DSI_PHY_IF_CFG, PHY_STOP_WAIT_TIME(0x20) |
> -		  N_LANES(dsi->lanes));
> -}
> -
> -static void dw_mipi_dsi_clear_err(struct dw_mipi_dsi *dsi)
> -{
> -	dsi_read(dsi, DSI_INT_ST0);
> -	dsi_read(dsi, DSI_INT_ST1);
> -	dsi_write(dsi, DSI_INT_MSK0, 0);
> -	dsi_write(dsi, DSI_INT_MSK1, 0);
> -}
> -
> -static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder)
> -{
> -	struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
> -
> -	if (dsi->dpms_mode != DRM_MODE_DPMS_ON)
> -		return;
> -
> -	if (clk_prepare_enable(dsi->pclk)) {
> -		DRM_DEV_ERROR(dsi->dev, "Failed to enable pclk\n");
> -		return;
> -	}
> -
> -	drm_panel_disable(dsi->panel);
> -
> -	dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_CMD_MODE);
> -	drm_panel_unprepare(dsi->panel);
> -
> -	dw_mipi_dsi_disable(dsi);
> -	pm_runtime_put(dsi->dev);
> -	clk_disable_unprepare(dsi->pclk);
> -	dsi->dpms_mode = DRM_MODE_DPMS_OFF;
> -}
> -
> -static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
> -{
> -	struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
> -	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
> -	const struct dw_mipi_dsi_plat_data *pdata = dsi->pdata;
> -	int mux = drm_of_encoder_active_endpoint_id(dsi->dev->of_node, encoder);
> -	u32 val;
> -	int ret;
> -
> -	ret = dw_mipi_dsi_get_lane_bps(dsi, mode);
> -	if (ret < 0)
> -		return;
> -
> -	if (dsi->dpms_mode == DRM_MODE_DPMS_ON)
> -		return;
> -
> -	if (clk_prepare_enable(dsi->pclk)) {
> -		DRM_DEV_ERROR(dsi->dev, "Failed to enable pclk\n");
> -		return;
> -	}
> -
> -	pm_runtime_get_sync(dsi->dev);
> -	dw_mipi_dsi_init(dsi);
> -	dw_mipi_dsi_dpi_config(dsi, mode);
> -	dw_mipi_dsi_packet_handler_config(dsi);
> -	dw_mipi_dsi_video_mode_config(dsi);
> -	dw_mipi_dsi_video_packet_config(dsi, mode);
> -	dw_mipi_dsi_command_mode_config(dsi);
> -	dw_mipi_dsi_line_timer_config(dsi, mode);
> -	dw_mipi_dsi_vertical_timing_config(dsi, mode);
> -	dw_mipi_dsi_dphy_timing_config(dsi);
> -	dw_mipi_dsi_dphy_interface_config(dsi);
> -	dw_mipi_dsi_clear_err(dsi);
> -
> -	/*
> -	 * For the RK3399, the clk of grf must be enabled before writing grf
> -	 * register. And for RK3288 or other soc, this grf_clk must be NULL,
> -	 * the clk_prepare_enable return true directly.
> -	 */
> -	ret = clk_prepare_enable(dsi->grf_clk);
> -	if (ret) {
> -		DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret);
> -		return;
> -	}
> -
> -	if (pdata->grf_dsi0_mode_reg)
> -		regmap_write(dsi->grf_regmap, pdata->grf_dsi0_mode_reg,
> -			     pdata->grf_dsi0_mode);
> -
> -	dw_mipi_dsi_phy_init(dsi);
> -	dw_mipi_dsi_wait_for_two_frames(mode);
> -
> -	dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_CMD_MODE);
> -	if (drm_panel_prepare(dsi->panel))
> -		DRM_DEV_ERROR(dsi->dev, "failed to prepare panel\n");
> -
> -	dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_VID_MODE);
> -	drm_panel_enable(dsi->panel);
> -
> -	clk_disable_unprepare(dsi->pclk);
> -
> -	if (mux)
> -		val = pdata->dsi0_en_bit | (pdata->dsi0_en_bit << 16);
> -	else
> -		val = pdata->dsi0_en_bit << 16;
> -
> -	regmap_write(dsi->grf_regmap, pdata->grf_switch_reg, val);
> -	DRM_DEV_DEBUG(dsi->dev,
> -		      "vop %s output to dsi0\n", (mux) ? "LIT" : "BIG");
> -	dsi->dpms_mode = DRM_MODE_DPMS_ON;
> -
> -	clk_disable_unprepare(dsi->grf_clk);
> -}
> -
> -static int
> -dw_mipi_dsi_encoder_atomic_check(struct drm_encoder *encoder,
> -				 struct drm_crtc_state *crtc_state,
> -				 struct drm_connector_state *conn_state)
> -{
> -	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
> -	struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
> -
> -	switch (dsi->format) {
> -	case MIPI_DSI_FMT_RGB888:
> -		s->output_mode = ROCKCHIP_OUT_MODE_P888;
> -		break;
> -	case MIPI_DSI_FMT_RGB666:
> -		s->output_mode = ROCKCHIP_OUT_MODE_P666;
> -		break;
> -	case MIPI_DSI_FMT_RGB565:
> -		s->output_mode = ROCKCHIP_OUT_MODE_P565;
> -		break;
> -	default:
> -		WARN_ON(1);
> -		return -EINVAL;
> -	}
> -
> -	s->output_type = DRM_MODE_CONNECTOR_DSI;
> -
> -	return 0;
> -}
> -
> -static const struct drm_encoder_helper_funcs
> -dw_mipi_dsi_encoder_helper_funcs = {
> -	.enable = dw_mipi_dsi_encoder_enable,
> -	.disable = dw_mipi_dsi_encoder_disable,
> -	.atomic_check = dw_mipi_dsi_encoder_atomic_check,
> -};
> -
> -static const struct drm_encoder_funcs dw_mipi_dsi_encoder_funcs = {
> -	.destroy = drm_encoder_cleanup,
> -};
> -
> -static int dw_mipi_dsi_connector_get_modes(struct drm_connector *connector)
> -{
> -	struct dw_mipi_dsi *dsi = con_to_dsi(connector);
> -
> -	return drm_panel_get_modes(dsi->panel);
> -}
> -
> -static struct drm_connector_helper_funcs dw_mipi_dsi_connector_helper_funcs = {
> -	.get_modes = dw_mipi_dsi_connector_get_modes,
> -};
> -
> -static void dw_mipi_dsi_drm_connector_destroy(struct drm_connector *connector)
> -{
> -	drm_connector_unregister(connector);
> -	drm_connector_cleanup(connector);
> -}
> -
> -static const struct drm_connector_funcs dw_mipi_dsi_atomic_connector_funcs = {
> -	.fill_modes = drm_helper_probe_single_connector_modes,
> -	.destroy = dw_mipi_dsi_drm_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 dw_mipi_dsi_register(struct drm_device *drm,
> -				struct dw_mipi_dsi *dsi)
> -{
> -	struct drm_encoder *encoder = &dsi->encoder;
> -	struct drm_connector *connector = &dsi->connector;
> -	struct device *dev = dsi->dev;
> -	int ret;
> -
> -	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm,
> -							     dev->of_node);
> -	/*
> -	 * If we failed to find the CRTC(s) which this encoder is
> -	 * supposed to be connected to, it's because the CRTC has
> -	 * not been registered yet.  Defer probing, and hope that
> -	 * the required CRTC is added later.
> -	 */
> -	if (encoder->possible_crtcs == 0)
> -		return -EPROBE_DEFER;
> -
> -	drm_encoder_helper_add(&dsi->encoder,
> -			       &dw_mipi_dsi_encoder_helper_funcs);
> -	ret = drm_encoder_init(drm, &dsi->encoder, &dw_mipi_dsi_encoder_funcs,
> -			       DRM_MODE_ENCODER_DSI, NULL);
> -	if (ret) {
> -		DRM_DEV_ERROR(dev, "Failed to initialize encoder with drm\n");
> -		return ret;
> -	}
> -
> -	drm_connector_helper_add(connector,
> -				 &dw_mipi_dsi_connector_helper_funcs);
> -
> -	drm_connector_init(drm, &dsi->connector,
> -			   &dw_mipi_dsi_atomic_connector_funcs,
> -			   DRM_MODE_CONNECTOR_DSI);
> -
> -	drm_mode_connector_attach_encoder(connector, encoder);
> -
> -	return 0;
> -}
> -
> -static int rockchip_mipi_parse_dt(struct dw_mipi_dsi *dsi)
> -{
> -	struct device_node *np = dsi->dev->of_node;
> -
> -	dsi->grf_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
> -	if (IS_ERR(dsi->grf_regmap)) {
> -		DRM_DEV_ERROR(dsi->dev, "Unable to get rockchip,grf\n");
> -		return PTR_ERR(dsi->grf_regmap);
> -	}
> -
> -	return 0;
> -}
> -
> -static struct dw_mipi_dsi_plat_data rk3288_mipi_dsi_drv_data = {
> -	.dsi0_en_bit = RK3288_DSI0_SEL_VOP_LIT,
> -	.dsi1_en_bit = RK3288_DSI1_SEL_VOP_LIT,
> -	.grf_switch_reg = RK3288_GRF_SOC_CON6,
> -	.max_data_lanes = 4,
> -};
> -
> -static struct dw_mipi_dsi_plat_data rk3399_mipi_dsi_drv_data = {
> -	.dsi0_en_bit = RK3399_DSI0_SEL_VOP_LIT,
> -	.dsi1_en_bit = RK3399_DSI1_SEL_VOP_LIT,
> -	.grf_switch_reg = RK3399_GRF_SOC_CON20,
> -	.grf_dsi0_mode = RK3399_GRF_DSI_MODE,
> -	.grf_dsi0_mode_reg = RK3399_GRF_SOC_CON22,
> -	.flags = DW_MIPI_NEEDS_PHY_CFG_CLK | DW_MIPI_NEEDS_GRF_CLK,
> -	.max_data_lanes = 4,
> -};
> -
> -static const struct of_device_id dw_mipi_dsi_dt_ids[] = {
> -	{
> -	 .compatible = "rockchip,rk3288-mipi-dsi",
> -	 .data = &rk3288_mipi_dsi_drv_data,
> -	}, {
> -	 .compatible = "rockchip,rk3399-mipi-dsi",
> -	 .data = &rk3399_mipi_dsi_drv_data,
> -	},
> -	{ /* sentinel */ }
> -};
> -MODULE_DEVICE_TABLE(of, dw_mipi_dsi_dt_ids);
> -
> -static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
> -			    void *data)
> -{
> -	const struct of_device_id *of_id =
> -			of_match_device(dw_mipi_dsi_dt_ids, dev);
> -	const struct dw_mipi_dsi_plat_data *pdata = of_id->data;
> -	struct platform_device *pdev = to_platform_device(dev);
> -	struct reset_control *apb_rst;
> -	struct drm_device *drm = data;
> -	struct dw_mipi_dsi *dsi;
> -	struct resource *res;
> -	int ret;
> -
> -	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
> -	if (!dsi)
> -		return -ENOMEM;
> -
> -	dsi->dev = dev;
> -	dsi->pdata = pdata;
> -	dsi->dpms_mode = DRM_MODE_DPMS_OFF;
> -
> -	ret = rockchip_mipi_parse_dt(dsi);
> -	if (ret)
> -		return ret;
> -
> -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> -	dsi->base = devm_ioremap_resource(dev, res);
> -	if (IS_ERR(dsi->base))
> -		return PTR_ERR(dsi->base);
> -
> -	dsi->pllref_clk = devm_clk_get(dev, "ref");
> -	if (IS_ERR(dsi->pllref_clk)) {
> -		ret = PTR_ERR(dsi->pllref_clk);
> -		DRM_DEV_ERROR(dev,
> -			      "Unable to get pll reference clock: %d\n", ret);
> -		return ret;
> -	}
> -
> -	dsi->pclk = devm_clk_get(dev, "pclk");
> -	if (IS_ERR(dsi->pclk)) {
> -		ret = PTR_ERR(dsi->pclk);
> -		DRM_DEV_ERROR(dev, "Unable to get pclk: %d\n", ret);
> -		return ret;
> -	}
> -
> -	/*
> -	 * Note that the reset was not defined in the initial device tree, so
> -	 * we have to be prepared for it not being found.
> -	 */
> -	apb_rst = devm_reset_control_get(dev, "apb");
> -	if (IS_ERR(apb_rst)) {
> -		ret = PTR_ERR(apb_rst);
> -		if (ret == -ENOENT) {
> -			apb_rst = NULL;
> -		} else {
> -			DRM_DEV_ERROR(dev,
> -				      "Unable to get reset control: %d\n", ret);
> -			return ret;
> -		}
> -	}
> -
> -	if (apb_rst) {
> -		ret = clk_prepare_enable(dsi->pclk);
> -		if (ret) {
> -			DRM_DEV_ERROR(dev, "Failed to enable pclk\n");
> -			return ret;
> -		}
> -
> -		reset_control_assert(apb_rst);
> -		usleep_range(10, 20);
> -		reset_control_deassert(apb_rst);
> -
> -		clk_disable_unprepare(dsi->pclk);
> -	}
> -
> -	if (pdata->flags & DW_MIPI_NEEDS_PHY_CFG_CLK) {
> -		dsi->phy_cfg_clk = devm_clk_get(dev, "phy_cfg");
> -		if (IS_ERR(dsi->phy_cfg_clk)) {
> -			ret = PTR_ERR(dsi->phy_cfg_clk);
> -			DRM_DEV_ERROR(dev,
> -				      "Unable to get phy_cfg_clk: %d\n", ret);
> -			return ret;
> -		}
> -	}
> -
> -	if (pdata->flags & DW_MIPI_NEEDS_GRF_CLK) {
> -		dsi->grf_clk = devm_clk_get(dev, "grf");
> -		if (IS_ERR(dsi->grf_clk)) {
> -			ret = PTR_ERR(dsi->grf_clk);
> -			DRM_DEV_ERROR(dev, "Unable to get grf_clk: %d\n", ret);
> -			return ret;
> -		}
> -	}
> -
> -	ret = clk_prepare_enable(dsi->pllref_clk);
> -	if (ret) {
> -		DRM_DEV_ERROR(dev, "Failed to enable pllref_clk\n");
> -		return ret;
> -	}
> -
> -	ret = dw_mipi_dsi_register(drm, dsi);
> -	if (ret) {
> -		DRM_DEV_ERROR(dev, "Failed to register mipi_dsi: %d\n", ret);
> -		goto err_pllref;
> -	}
> -
> -	dsi->dsi_host.ops = &dw_mipi_dsi_host_ops;
> -	dsi->dsi_host.dev = dev;
> -	ret = mipi_dsi_host_register(&dsi->dsi_host);
> -	if (ret) {
> -		DRM_DEV_ERROR(dev, "Failed to register MIPI host: %d\n", ret);
> -		goto err_cleanup;
> -	}
> -
> -	if (!dsi->panel) {
> -		ret = -EPROBE_DEFER;
> -		goto err_mipi_dsi_host;
> -	}
> -
> -	dev_set_drvdata(dev, dsi);
> -	pm_runtime_enable(dev);
> -	return 0;
> -
> -err_mipi_dsi_host:
> -	mipi_dsi_host_unregister(&dsi->dsi_host);
> -err_cleanup:
> -	dsi->connector.funcs->destroy(&dsi->connector);
> -	dsi->encoder.funcs->destroy(&dsi->encoder);
> -err_pllref:
> -	clk_disable_unprepare(dsi->pllref_clk);
> -	return ret;
> -}
> -
> -static void dw_mipi_dsi_unbind(struct device *dev, struct device *master,
> -			       void *data)
> -{
> -	struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
> -
> -	mipi_dsi_host_unregister(&dsi->dsi_host);
> -	pm_runtime_disable(dev);
> -
> -	dsi->connector.funcs->destroy(&dsi->connector);
> -	dsi->encoder.funcs->destroy(&dsi->encoder);
> -
> -	clk_disable_unprepare(dsi->pllref_clk);
> -}
> -
> -static const struct component_ops dw_mipi_dsi_ops = {
> -	.bind	= dw_mipi_dsi_bind,
> -	.unbind	= dw_mipi_dsi_unbind,
> -};
> -
> -static int dw_mipi_dsi_probe(struct platform_device *pdev)
> -{
> -	return component_add(&pdev->dev, &dw_mipi_dsi_ops);
> -}
> -
> -static int dw_mipi_dsi_remove(struct platform_device *pdev)
> -{
> -	component_del(&pdev->dev, &dw_mipi_dsi_ops);
> -	return 0;
> -}
> -
> -struct platform_driver dw_mipi_dsi_driver = {
> -	.probe		= dw_mipi_dsi_probe,
> -	.remove		= dw_mipi_dsi_remove,
> -	.driver		= {
> -		.of_match_table = dw_mipi_dsi_dt_ids,
> -		.name	= DRIVER_NAME,
> -	},
> -};
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> index f814d37b1db2..623cce77ebae 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> @@ -477,7 +477,7 @@ static int __init rockchip_drm_init(void)
>  	ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP);
>  	ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver,
>  				CONFIG_ROCKCHIP_DW_HDMI);
> -	ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_driver,
> +	ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver,
>  				CONFIG_ROCKCHIP_DW_MIPI_DSI);
>  	ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI);
>  
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> index 3a6ebfc26036..96bb4ca8febf 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> @@ -67,7 +67,7 @@ int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout);
>  
>  extern struct platform_driver cdn_dp_driver;
>  extern struct platform_driver dw_hdmi_rockchip_pltfm_driver;
> -extern struct platform_driver dw_mipi_dsi_driver;
> +extern struct platform_driver dw_mipi_dsi_rockchip_driver;
>  extern struct platform_driver inno_hdmi_driver;
>  extern struct platform_driver rockchip_dp_driver;
>  extern struct platform_driver rockchip_lvds_driver;


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

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

* Re: [PATCH v2 6/8] drm/dsi: add helper function to find the second host in a dual-dsi setup
  2018-06-18 10:28 ` [PATCH v2 6/8] drm/dsi: add helper function to find the second host in a dual-dsi setup Heiko Stuebner
@ 2018-07-03 15:06   ` Andrzej Hajda
  2018-07-04 10:47     ` Heiko Stuebner
  0 siblings, 1 reply; 26+ messages in thread
From: Andrzej Hajda @ 2018-07-03 15:06 UTC (permalink / raw)
  To: Heiko Stuebner, dri-devel
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, philippe.cornu,
	yannick.fertre, linux-rockchip, nickey.yang, robh+dt,
	thierry.reding, laurent.pinchart, mka

On 18.06.2018 12:28, Heiko Stuebner wrote:
> >From a specified output port of one dsi controller this function allows to
> iterate over the list of registered dsi controllers trying to find a second
> instance connected to the same display, like it is used in dual-dsi setups.
>
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>

The code looks hacky to me - it iterates over external nodes, it assumes
all graph links are dedicated exclusively to dsi, it assumes that there
is no additional node between dsi host and the panel.
Anyway let's skip it for a moment, and look at the following patches.

Regards
Andrzej

> ---
>  drivers/gpu/drm/drm_mipi_dsi.c | 56 ++++++++++++++++++++++++++++++++++
>  include/drm/drm_mipi_dsi.h     |  2 ++
>  2 files changed, 58 insertions(+)
>
> diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
> index bc73b7f5b9fc..0c3c9c7aa3b8 100644
> --- a/drivers/gpu/drm/drm_mipi_dsi.c
> +++ b/drivers/gpu/drm/drm_mipi_dsi.c
> @@ -30,6 +30,7 @@
>  #include <linux/device.h>
>  #include <linux/module.h>
>  #include <linux/of_device.h>
> +#include <linux/of_graph.h>
>  #include <linux/pm_runtime.h>
>  #include <linux/slab.h>
>  
> @@ -282,6 +283,61 @@ struct mipi_dsi_host *of_find_mipi_dsi_host_by_node(struct device_node *node)
>  }
>  EXPORT_SYMBOL(of_find_mipi_dsi_host_by_node);
>  
> +struct device_node *of_mipi_dsi_find_second_host(struct device_node *first_np,
> +						 int port, int endpoint)
> +{
> +	struct mipi_dsi_host *first, *host;
> +	struct device_node *remote, *np, *second_np = NULL;
> +	int num = 0;
> +
> +	first = of_find_mipi_dsi_host_by_node(first_np);
> +	if (!first) {
> +		pr_err("no dsi-host for node %s\n", first_np->full_name);
> +		return ERR_PTR(-ENODEV);
> +	}
> +
> +	/* output-node of the known dsi-host */
> +	remote = of_graph_get_remote_node(first_np, port, endpoint);
> +	if (!remote) {
> +		dev_err(first->dev, "no output node found\n");
> +		return ERR_PTR(-ENODEV);
> +	}
> +
> +	mutex_lock(&host_lock);
> +
> +	list_for_each_entry(host, &host_list, list) {
> +		np = of_graph_get_remote_node(host->dev->of_node,
> +					      port, endpoint);
> +
> +		/* found a host connected to this panel */
> +		if (np == remote)
> +			num++;
> +
> +		/* found one second host */
> +		if (host->dev->of_node != first_np)
> +			second_np = host->dev->of_node;
> +
> +		of_node_put(np);
> +	}
> +
> +	/* of_node_get the node under host_lock */
> +	if (num == 2)
> +		of_node_get(second_np);
> +
> +	mutex_unlock(&host_lock);
> +
> +	of_node_put(remote);
> +
> +	if (num > 2) {
> +		dev_err(first->dev,
> +			      "too many DSI links for output: %d links\n", num);
> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	return second_np;
> +}
> +EXPORT_SYMBOL(of_mipi_dsi_find_second_host);
> +
>  int mipi_dsi_host_register(struct mipi_dsi_host *host)
>  {
>  	struct device_node *node;
> diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
> index 4fef19064b0f..89532ae69c91 100644
> --- a/include/drm/drm_mipi_dsi.h
> +++ b/include/drm/drm_mipi_dsi.h
> @@ -107,6 +107,8 @@ struct mipi_dsi_host {
>  int mipi_dsi_host_register(struct mipi_dsi_host *host);
>  void mipi_dsi_host_unregister(struct mipi_dsi_host *host);
>  struct mipi_dsi_host *of_find_mipi_dsi_host_by_node(struct device_node *node);
> +struct device_node *of_mipi_dsi_find_second_host(struct device_node *first_np,
> +						 int port, int endpoint);
>  
>  /* DSI mode flags */
>  


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

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

* Re: [PATCH v2 7/8] drm/bridge/synopsys: dsi: add dual-dsi support
  2018-06-18 10:28 ` [PATCH v2 7/8] drm/bridge/synopsys: dsi: add dual-dsi support Heiko Stuebner
  2018-07-03 11:57   ` Philippe CORNU
@ 2018-07-03 17:07   ` Andrzej Hajda
  2018-07-09 13:45     ` Heiko Stuebner
  1 sibling, 1 reply; 26+ messages in thread
From: Andrzej Hajda @ 2018-07-03 17:07 UTC (permalink / raw)
  To: Heiko Stuebner, dri-devel
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, philippe.cornu,
	yannick.fertre, linux-rockchip, nickey.yang, robh+dt,
	thierry.reding, laurent.pinchart, mka

On 18.06.2018 12:28, Heiko Stuebner wrote:
> From: Nickey Yang <nickey.yang@rock-chips.com>
>
> Allow to also drive a slave dw-mipi-dsi controller in a dual-dsi
> setup. This will require additional implementation-specific
> code to look up the slave instance and do specific setup.
> Also will probably need code in the specific crtcs as dual-dsi
> does not equal two separate dsi outputs.
>
> To activate, the implementation-specific code should set the slave
> using dw_mipi_dsi_set_slave() before calling __dw_mipi_dsi_bind().
>
> v2:
> - expect real interface number of lanes
> - keep links to both master and slave

I did not see the whole driver/pipeline, but it seems the point of this
patch is to perform the same work on the slave as on the master in case
of dual mode. I think DSI should not be a place for it,
DSI masters usually are stupid devices from display stack PoV, they just
convert video streams, in dual mode also. In this case the panel and/or
crtc adds complications so they should be responsible for handling it.
Panel should:
- register its both mipi interfaces with proper mode_flags (maybe some
dual-mode indication flags should be added if necessary),
- register drm_panel for both interfaces (it requires change in
drm_panel api), and provide video mode timings.
- in case it needs perform transfers perform it to master/slave/both
interfaces according to its needs,

I am not sure about DRM pipeline, it should model, maybe it could be
done this way:
CRTC -->ENCODER0(dsi master) --> CONNECTOR0 (panel interface 0)
      |---> ENCODER1(dsi slave) --> CONNECTOR1 (panel interface 1)

But I am not sure if it is not reserved only for mirroring.

For me more tempting solution is to create meta-encoder-connector let's
call it dual-encoder (maybe it could be even generic), which is visible
to userspace as single pipeline and encapsulates both dsi bridges/panel
inputs. So its every callback will be translated usually to sequence of
callbacks to 1st and 2nd dsi, or in case of get_modes it should return
mode which represent sum of modes in both panels.
Maybe it looks more complicated, but it can be more universal - you can
use it with different bridges/panels even two single-panels if necessary.

Of course I do not see the whole picture, or I can be just wrong, or
just freaking purist :). If there are arguments against my vision please
provide them.
I am also not strongly against your solution, I just want to show
alternatives, which could be better/more generic.

Regards
Andrzej

>
> Signed-off-by: Nickey Yang <nickey.yang@rock-chips.com>
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> ---
>  drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 93 +++++++++++++++++--
>  include/drm/bridge/dw_mipi_dsi.h              |  1 +
>  2 files changed, 86 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> index bd503f000ed5..6a345d1dde25 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> @@ -230,9 +230,20 @@ struct dw_mipi_dsi {
>  	u32 format;
>  	unsigned long mode_flags;
>  
> +	struct dw_mipi_dsi *master; /* dual-dsi master ptr */
> +	struct dw_mipi_dsi *slave; /* dual-dsi slave ptr */
> +
>  	const struct dw_mipi_dsi_plat_data *plat_data;
>  };
>  
> +/*
> + * Check if either a link to a master or slave is present
> + */
> +static inline bool dw_mipi_is_dual_mode(struct dw_mipi_dsi *dsi)
> +{
> +	return dsi->slave || dsi->master;
> +}
> +
>  /*
>   * The controller should generate 2 frames before
>   * preparing the peripheral.
> @@ -273,18 +284,26 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
>  	struct drm_bridge *bridge;
>  	struct drm_panel *panel;
>  	int ret;
> +	int lanes = device->lanes;
>  
> -	if (device->lanes > dsi->plat_data->max_data_lanes) {
> +	if (lanes > dsi->plat_data->max_data_lanes) {
>  		dev_err(dsi->dev, "the number of data lanes(%u) is too many\n",
>  			device->lanes);
>  		return -EINVAL;
>  	}
>  
> -	dsi->lanes = device->lanes;
> +	dsi->lanes = lanes;
>  	dsi->channel = device->channel;
>  	dsi->format = device->format;
>  	dsi->mode_flags = device->mode_flags;
>  
> +	if (dsi->slave) {
> +		dsi->slave->lanes = dsi->lanes;
> +		dsi->slave->channel = dsi->channel;
> +		dsi->slave->format = dsi->format;
> +		dsi->slave->mode_flags = dsi->mode_flags;
> +	}
> +
>  	ret = drm_of_find_panel_or_bridge(host->dev->of_node, 1, 0,
>  					  &panel, &bridge);
>  	if (ret)
> @@ -441,10 +460,17 @@ static ssize_t dw_mipi_dsi_host_transfer(struct mipi_dsi_host *host,
>  	}
>  
>  	dw_mipi_message_config(dsi, msg);
> +	if (dsi->slave)
> +		dw_mipi_message_config(dsi->slave, msg);
>  
>  	ret = dw_mipi_dsi_write(dsi, &packet);
>  	if (ret)
>  		return ret;
> +	if (dsi->slave) {
> +		ret = dw_mipi_dsi_write(dsi->slave, &packet);
> +		if (ret)
> +			return ret;
> +	}
>  
>  	if (msg->rx_buf && msg->rx_len) {
>  		ret = dw_mipi_dsi_read(dsi, msg);
> @@ -583,7 +609,15 @@ static void dw_mipi_dsi_video_packet_config(struct dw_mipi_dsi *dsi,
>  	 * DSI_VNPCR.NPSIZE... especially because this driver supports
>  	 * non-burst video modes, see dw_mipi_dsi_video_mode_config()...
>  	 */
> -	dsi_write(dsi, DSI_VID_PKT_SIZE, VID_PKT_SIZE(mode->hdisplay));
> +
> +	int pkt_size;
> +
> +	if (dw_mipi_is_dual_mode(dsi))
> +		pkt_size = VID_PKT_SIZE(mode->hdisplay / 2);
> +	else
> +		pkt_size = VID_PKT_SIZE(mode->hdisplay);
> +
> +	dsi_write(dsi, DSI_VID_PKT_SIZE, pkt_size);
>  }
>  
>  static void dw_mipi_dsi_command_mode_config(struct dw_mipi_dsi *dsi)
> @@ -756,23 +790,39 @@ static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge)
>  	dsi->panel_bridge->funcs->post_disable(dsi->panel_bridge);
>  
>  	dw_mipi_dsi_disable(dsi);
> +	if (dsi->slave) {
> +		dw_mipi_dsi_disable(dsi->slave);
> +		clk_disable_unprepare(dsi->slave->pclk);
> +		pm_runtime_put(dsi->slave->dev);
> +	}
> +
>  	clk_disable_unprepare(dsi->pclk);
>  	pm_runtime_put(dsi->dev);
>  }
>  
> -static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
> -					struct drm_display_mode *mode,
> -					struct drm_display_mode *adjusted_mode)
> +static unsigned int dw_mipi_dsi_get_lanes(struct dw_mipi_dsi *dsi)
> +{
> +	if (dsi->master)
> +		return dsi->master->lanes + dsi->lanes;
> +
> +	if (dsi->slave)
> +		return dsi->lanes + dsi->slave->lanes;
> +
> +	return dsi->lanes;
> +}
> +
> +static void dw_mipi_dsi_mode_set(struct dw_mipi_dsi *dsi,
> +				struct drm_display_mode *adjusted_mode)
>  {
> -	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
>  	const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops;
>  	void *priv_data = dsi->plat_data->priv_data;
>  	int ret;
> +	u32 lanes = dw_mipi_dsi_get_lanes(dsi);
>  
>  	clk_prepare_enable(dsi->pclk);
>  
>  	ret = phy_ops->get_lane_mbps(priv_data, adjusted_mode, dsi->mode_flags,
> -				     dsi->lanes, dsi->format, &dsi->lane_mbps);
> +				     lanes, dsi->format, &dsi->lane_mbps);
>  	if (ret)
>  		DRM_DEBUG_DRIVER("Phy get_lane_mbps() failed\n");
>  
> @@ -804,12 +854,25 @@ static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
>  	dw_mipi_dsi_set_mode(dsi, 0);
>  }
>  
> +static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
> +					struct drm_display_mode *mode,
> +					struct drm_display_mode *adjusted_mode)
> +{
> +	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
> +
> +	dw_mipi_dsi_mode_set(dsi, adjusted_mode);
> +	if (dsi->slave)
> +		dw_mipi_dsi_mode_set(dsi->slave, adjusted_mode);
> +}
> +
>  static void dw_mipi_dsi_bridge_enable(struct drm_bridge *bridge)
>  {
>  	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
>  
>  	/* Switch to video mode for panel-bridge enable & panel enable */
>  	dw_mipi_dsi_set_mode(dsi, MIPI_DSI_MODE_VIDEO);
> +	if (dsi->slave)
> +		dw_mipi_dsi_set_mode(dsi->slave, MIPI_DSI_MODE_VIDEO);
>  }
>  
>  static enum drm_mode_status
> @@ -949,6 +1012,20 @@ static void __dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi)
>  	pm_runtime_disable(dsi->dev);
>  }
>  
> +void dw_mipi_dsi_set_slave(struct dw_mipi_dsi *dsi, struct dw_mipi_dsi *slave)
> +{
> +	/* introduce controllers to each other */
> +	dsi->slave = slave;
> +	dsi->slave->master = dsi;
> +
> +	/* migrate settings for already attached displays */
> +	dsi->slave->lanes = dsi->lanes;
> +	dsi->slave->channel = dsi->channel;
> +	dsi->slave->format = dsi->format;
> +	dsi->slave->mode_flags = dsi->mode_flags;
> +}
> +EXPORT_SYMBOL_GPL(dw_mipi_dsi_set_slave);
> +
>  /*
>   * Probe/remove API, used from platforms based on the DRM bridge API.
>   */
> diff --git a/include/drm/bridge/dw_mipi_dsi.h b/include/drm/bridge/dw_mipi_dsi.h
> index 6d7f8eb5d9f2..5fd997cdf281 100644
> --- a/include/drm/bridge/dw_mipi_dsi.h
> +++ b/include/drm/bridge/dw_mipi_dsi.h
> @@ -37,5 +37,6 @@ struct dw_mipi_dsi *dw_mipi_dsi_probe(struct platform_device *pdev,
>  void dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi);
>  int dw_mipi_dsi_bind(struct dw_mipi_dsi *dsi, struct drm_encoder *encoder);
>  void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi);
> +void dw_mipi_dsi_set_slave(struct dw_mipi_dsi *dsi, struct dw_mipi_dsi *slave);
>  
>  #endif /* __DW_MIPI_DSI__ */


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

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

* Re: [PATCH v2 3/8] drm/bridge/synopsys: dsi: defer probing if panel not available in bridge-attach
  2018-07-03 12:42   ` Andrzej Hajda
@ 2018-07-04 10:36     ` Heiko Stuebner
  0 siblings, 0 replies; 26+ messages in thread
From: Heiko Stuebner @ 2018-07-04 10:36 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, philippe.cornu,
	dri-devel, yannick.fertre, linux-rockchip, nickey.yang, robh+dt,
	thierry.reding, laurent.pinchart, mka

Am Dienstag, 3. Juli 2018, 14:42:49 CEST schrieb Andrzej Hajda:
> On 18.06.2018 12:28, Heiko Stuebner wrote:
> > When the panel-driver is build as a module it currently fails hard as the
> > panel cannot be probed directly:
> >
> > dw_mipi_dsi_bind()
> >   __dw_mipi_dsi_probe()
> >     creates dsi bus
> >     creates panel device
> >     triggers panel module load
> >     panel not probed (module not loaded or panel probe slow)
> >   drm_bridge_attach
> >     fails with -EINVAL due to empty panel_bridge
> >
> > So emit a -EPROBE_DEFER in that case to give the driver another
> > chance at getting the display later.
> 
> Thats odd (at least for me), if the resource (panel in this case) is not
> present, bridge should not be attached (drm_bridge_attach should not be
> called). Ie __dw_mipi_dsi_probe should rather look for the panel and
> defer if not found. Resource gathering is the task of probe.
> drm_bridge_attach can be called after probe and it would be difficult
> for the caller to react properly for -EPROBE_DEFER error.

I think one of the problems comes from the fact that the panel only
gets probed (and even the panel-device created) after the dsi host bus
gets created in __dw_mipi_dsi_probe.


In hindsight, I think this is more of a concurrency issue between the
panel probing and calling dsi-attach and the dsi-controller looking for
the panel vs. panel-bridge.

So when I'm using drm_of_find_panel_or_bridge() as indicator of the
panel being, it probably makes things racy. I'll try to find a better
solution for the issue.


Heiko


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

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

* Re: [PATCH v2 6/8] drm/dsi: add helper function to find the second host in a dual-dsi setup
  2018-07-03 15:06   ` Andrzej Hajda
@ 2018-07-04 10:47     ` Heiko Stuebner
  0 siblings, 0 replies; 26+ messages in thread
From: Heiko Stuebner @ 2018-07-04 10:47 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, philippe.cornu,
	dri-devel, yannick.fertre, linux-rockchip, nickey.yang, robh+dt,
	thierry.reding, laurent.pinchart, mka

Am Dienstag, 3. Juli 2018, 17:06:48 CEST schrieb Andrzej Hajda:
> On 18.06.2018 12:28, Heiko Stuebner wrote:
> > >From a specified output port of one dsi controller this function allows to
> > iterate over the list of registered dsi controllers trying to find a second
> > instance connected to the same display, like it is used in dual-dsi setups.
> >
> > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> 
> The code looks hacky to me - it iterates over external nodes, it assumes
> all graph links are dedicated exclusively to dsi

The loop iterates over all registered dsi controllers and checks the same
port/endpoint combination as on the primary host.

One assumption is that the dsi controllers are identical, but that is
also an argument

As the splitting of the output to two DSI-controllers is a special
feature of the socs CRTC, I'd think the chances are very slim that
there will be two completely different DSI controllers used for
something like this.

> it assumes that there
> is no additional node between dsi host and the panel.

Yep.


Heiko


> > ---
> >  drivers/gpu/drm/drm_mipi_dsi.c | 56 ++++++++++++++++++++++++++++++++++
> >  include/drm/drm_mipi_dsi.h     |  2 ++
> >  2 files changed, 58 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
> > index bc73b7f5b9fc..0c3c9c7aa3b8 100644
> > --- a/drivers/gpu/drm/drm_mipi_dsi.c
> > +++ b/drivers/gpu/drm/drm_mipi_dsi.c
> > @@ -30,6 +30,7 @@
> >  #include <linux/device.h>
> >  #include <linux/module.h>
> >  #include <linux/of_device.h>
> > +#include <linux/of_graph.h>
> >  #include <linux/pm_runtime.h>
> >  #include <linux/slab.h>
> >  
> > @@ -282,6 +283,61 @@ struct mipi_dsi_host *of_find_mipi_dsi_host_by_node(struct device_node *node)
> >  }
> >  EXPORT_SYMBOL(of_find_mipi_dsi_host_by_node);
> >  
> > +struct device_node *of_mipi_dsi_find_second_host(struct device_node *first_np,
> > +						 int port, int endpoint)
> > +{
> > +	struct mipi_dsi_host *first, *host;
> > +	struct device_node *remote, *np, *second_np = NULL;
> > +	int num = 0;
> > +
> > +	first = of_find_mipi_dsi_host_by_node(first_np);
> > +	if (!first) {
> > +		pr_err("no dsi-host for node %s\n", first_np->full_name);
> > +		return ERR_PTR(-ENODEV);
> > +	}
> > +
> > +	/* output-node of the known dsi-host */
> > +	remote = of_graph_get_remote_node(first_np, port, endpoint);
> > +	if (!remote) {
> > +		dev_err(first->dev, "no output node found\n");
> > +		return ERR_PTR(-ENODEV);
> > +	}
> > +
> > +	mutex_lock(&host_lock);
> > +
> > +	list_for_each_entry(host, &host_list, list) {
> > +		np = of_graph_get_remote_node(host->dev->of_node,
> > +					      port, endpoint);
> > +
> > +		/* found a host connected to this panel */
> > +		if (np == remote)
> > +			num++;
> > +
> > +		/* found one second host */
> > +		if (host->dev->of_node != first_np)
> > +			second_np = host->dev->of_node;
> > +
> > +		of_node_put(np);
> > +	}
> > +
> > +	/* of_node_get the node under host_lock */
> > +	if (num == 2)
> > +		of_node_get(second_np);
> > +
> > +	mutex_unlock(&host_lock);
> > +
> > +	of_node_put(remote);
> > +
> > +	if (num > 2) {
> > +		dev_err(first->dev,
> > +			      "too many DSI links for output: %d links\n", num);
> > +		return ERR_PTR(-EINVAL);
> > +	}
> > +
> > +	return second_np;
> > +}
> > +EXPORT_SYMBOL(of_mipi_dsi_find_second_host);
> > +
> >  int mipi_dsi_host_register(struct mipi_dsi_host *host)
> >  {
> >  	struct device_node *node;
> > diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
> > index 4fef19064b0f..89532ae69c91 100644
> > --- a/include/drm/drm_mipi_dsi.h
> > +++ b/include/drm/drm_mipi_dsi.h
> > @@ -107,6 +107,8 @@ struct mipi_dsi_host {
> >  int mipi_dsi_host_register(struct mipi_dsi_host *host);
> >  void mipi_dsi_host_unregister(struct mipi_dsi_host *host);
> >  struct mipi_dsi_host *of_find_mipi_dsi_host_by_node(struct device_node *node);
> > +struct device_node *of_mipi_dsi_find_second_host(struct device_node *first_np,
> > +						 int port, int endpoint);
> >  
> >  /* DSI mode flags */
> >  
> 
> 
> 




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

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

* Re: [PATCH v2 2/8] drm/bridge/synopsys: dsi: don't call __dw_mipi_dsi_probe from dw_mipi_dsi_bind
  2018-07-03 12:16   ` Andrzej Hajda
  2018-07-03 12:32     ` Heiko Stübner
@ 2018-07-04 12:23     ` Heiko Stuebner
  1 sibling, 0 replies; 26+ messages in thread
From: Heiko Stuebner @ 2018-07-04 12:23 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, philippe.cornu,
	dri-devel, yannick.fertre, linux-rockchip, nickey.yang, robh+dt,
	thierry.reding, laurent.pinchart, mka

Am Dienstag, 3. Juli 2018, 14:16:28 CEST schrieb Andrzej Hajda:
> On 18.06.2018 12:28, Heiko Stuebner wrote:
> > __dw_mipi_dsi_probe() does all the grabbing of resources and does it using
> > devm-helpers. So this is happening on each try of master bringup possibly
> > slowing down things a lot.
> >
> > Drivers using the component framework may instead want call dw_mipi_dsi_probe
> > separately in their probe function setup resources early. That way the dsi
> > bus also gets created earlier and also not recreated on each bind-try, so
> > that attached panels can load their modules and be probed way before the
> > bridge-attach in the bind call.
> >
> > So drop the call to __dw_mipi_dsi_probe and modify the function to take
> > a struct dw_mipi_dsi instead of the platform-device.
> >
> > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> > ---
> >  drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c | 15 +++------------
> >  include/drm/bridge/dw_mipi_dsi.h              |  5 +----
> >  2 files changed, 4 insertions(+), 16 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> > index 07cde255cab2..bb4aeca5c0f9 100644
> > --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> > +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> > @@ -966,31 +966,22 @@ EXPORT_SYMBOL_GPL(dw_mipi_dsi_remove);
> >  /*
> >   * Bind/unbind API, used from platforms based on the component framework.
> >   */
> > -struct dw_mipi_dsi *
> > -dw_mipi_dsi_bind(struct platform_device *pdev, struct drm_encoder *encoder,
> > -		 const struct dw_mipi_dsi_plat_data *plat_data)
> > +int dw_mipi_dsi_bind(struct dw_mipi_dsi *dsi, struct drm_encoder *encoder)
> >  {
> > -	struct dw_mipi_dsi *dsi;
> >  	int ret;
> >  
> > -	dsi = __dw_mipi_dsi_probe(pdev, plat_data);
> > -	if (IS_ERR(dsi))
> > -		return dsi;
> > -
> >  	ret = drm_bridge_attach(encoder, &dsi->bridge, NULL);
> >  	if (ret) {
> > -		dw_mipi_dsi_remove(dsi);
> >  		DRM_ERROR("Failed to initialize bridge with drm\n");
> > -		return ERR_PTR(ret);
> > +		return ret;
> >  	}
> >  
> > -	return dsi;
> > +	return ret;
> >  }
> >  EXPORT_SYMBOL_GPL(dw_mipi_dsi_bind);
> >  
> >  void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi)
> >  {
> > -	__dw_mipi_dsi_remove(dsi);
> >  }
> >  EXPORT_SYMBOL_GPL(dw_mipi_dsi_unbind);
> 
> Can't we just remove these two bind/unbind functions and call
> drm_bridge_attach directly?

And I just realized, we can't.

struct dw_mipi_dsi is local to the common bridge driver, which also makes
sense to not expose internal stuff. So drm_bridge attach will need to
be called from the common bridge driver.


Heiko


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

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

* Re: [PATCH v2 7/8] drm/bridge/synopsys: dsi: add dual-dsi support
  2018-07-03 17:07   ` Andrzej Hajda
@ 2018-07-09 13:45     ` Heiko Stuebner
  2018-07-09 16:02       ` Andrzej Hajda
  0 siblings, 1 reply; 26+ messages in thread
From: Heiko Stuebner @ 2018-07-09 13:45 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: mark.rutland, devicetree, briannorris, hoegsberg, philippe.cornu,
	dri-devel, yannick.fertre, linux-rockchip, nickey.yang, robh+dt,
	thierry.reding, laurent.pinchart, mka

Am Dienstag, 3. Juli 2018, 19:07:02 CEST schrieb Andrzej Hajda:
> On 18.06.2018 12:28, Heiko Stuebner wrote:
> > From: Nickey Yang <nickey.yang@rock-chips.com>
> >
> > Allow to also drive a slave dw-mipi-dsi controller in a dual-dsi
> > setup. This will require additional implementation-specific
> > code to look up the slave instance and do specific setup.
> > Also will probably need code in the specific crtcs as dual-dsi
> > does not equal two separate dsi outputs.
> >
> > To activate, the implementation-specific code should set the slave
> > using dw_mipi_dsi_set_slave() before calling __dw_mipi_dsi_bind().
> >
> > v2:
> > - expect real interface number of lanes
> > - keep links to both master and slave
> 
> I did not see the whole driver/pipeline, but it seems the point of this
> patch is to perform the same work on the slave as on the master in case
> of dual mode. I think DSI should not be a place for it,
> DSI masters usually are stupid devices from display stack PoV, they just
> convert video streams, in dual mode also. In this case the panel and/or
> crtc adds complications so they should be responsible for handling it.
> Panel should:
> - register its both mipi interfaces with proper mode_flags (maybe some
> dual-mode indication flags should be added if necessary),
> - register drm_panel for both interfaces (it requires change in
> drm_panel api), and provide video mode timings.
> - in case it needs perform transfers perform it to master/slave/both
> interfaces according to its needs,
> 
> I am not sure about DRM pipeline, it should model, maybe it could be
> done this way:
> CRTC -->ENCODER0(dsi master) --> CONNECTOR0 (panel interface 0)
>       |---> ENCODER1(dsi slave) --> CONNECTOR1 (panel interface 1)
> 
> But I am not sure if it is not reserved only for mirroring.
> 
> For me more tempting solution is to create meta-encoder-connector let's
> call it dual-encoder (maybe it could be even generic), which is visible
> to userspace as single pipeline and encapsulates both dsi bridges/panel
> inputs. So its every callback will be translated usually to sequence of
> callbacks to 1st and 2nd dsi, or in case of get_modes it should return
> mode which represent sum of modes in both panels.
> Maybe it looks more complicated, but it can be more universal - you can
> use it with different bridges/panels even two single-panels if necessary.
> 
> Of course I do not see the whole picture, or I can be just wrong, or
> just freaking purist :). If there are arguments against my vision please
> provide them.
> I am also not strongly against your solution, I just want to show
> alternatives, which could be better/more generic.

I cannot really claim to know the correct way to go forward, as these
drm-internals are still very much unknown land for me, but current
thought points I had on this:

- strawman argument ;-) : this is the same way tegra handles dual-dsi
  in its ganged mode ... also with regards to panel handling
  With Thierry helming both panels and the tegra dual-dsi change

- while the panel may expose two DSI data interfaces, there is still
  only one power-sequence and also only one init-command sequence.
  So I don't think you can really handle a dual-dsi panels as two fully
  separate panels, creating instead the need for both panel-instances
  to work together

- Right now we have two data-points on dual-something voodoo
  (tegra+rockchip)
  So for me it sounds hard to design something generic that survives
  the first non-dsi use-case.

  The devicetree binding (see cover-letter) should be pretty stable, as
  a panel like this will need to be bound to one of the two dsi controllers
  so nothing hinders code changes later on if the need for a more generic
  solution comes up.


Heiko


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

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

* Re: [PATCH v2 7/8] drm/bridge/synopsys: dsi: add dual-dsi support
  2018-07-09 13:45     ` Heiko Stuebner
@ 2018-07-09 16:02       ` Andrzej Hajda
  0 siblings, 0 replies; 26+ messages in thread
From: Andrzej Hajda @ 2018-07-09 16:02 UTC (permalink / raw)
  To: Heiko Stuebner
  Cc: mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA,
	architt-sgV2jX0FEOL9JmXXK+q4OQ,
	briannorris-F7+t8E8rja9g9hUCZPvPmw,
	hoegsberg-Re5JQEeQqe8AvxtiuMwx3w, philippe.cornu-qxv4g6HH51o,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	yannick.fertre-qxv4g6HH51o,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	nickey.yang-TNX95d0MmH7DzftRWevZcw,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	thierry.reding-Re5JQEeQqe8AvxtiuMwx3w,
	laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
	seanpaul-F7+t8E8rja9g9hUCZPvPmw, mka-F7+t8E8rja9g9hUCZPvPmw

On 09.07.2018 15:45, Heiko Stuebner wrote:
> Am Dienstag, 3. Juli 2018, 19:07:02 CEST schrieb Andrzej Hajda:
>> On 18.06.2018 12:28, Heiko Stuebner wrote:
>>> From: Nickey Yang <nickey.yang-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>>>
>>> Allow to also drive a slave dw-mipi-dsi controller in a dual-dsi
>>> setup. This will require additional implementation-specific
>>> code to look up the slave instance and do specific setup.
>>> Also will probably need code in the specific crtcs as dual-dsi
>>> does not equal two separate dsi outputs.
>>>
>>> To activate, the implementation-specific code should set the slave
>>> using dw_mipi_dsi_set_slave() before calling __dw_mipi_dsi_bind().
>>>
>>> v2:
>>> - expect real interface number of lanes
>>> - keep links to both master and slave
>> I did not see the whole driver/pipeline, but it seems the point of this
>> patch is to perform the same work on the slave as on the master in case
>> of dual mode. I think DSI should not be a place for it,
>> DSI masters usually are stupid devices from display stack PoV, they just
>> convert video streams, in dual mode also. In this case the panel and/or
>> crtc adds complications so they should be responsible for handling it.
>> Panel should:
>> - register its both mipi interfaces with proper mode_flags (maybe some
>> dual-mode indication flags should be added if necessary),
>> - register drm_panel for both interfaces (it requires change in
>> drm_panel api), and provide video mode timings.
>> - in case it needs perform transfers perform it to master/slave/both
>> interfaces according to its needs,
>>
>> I am not sure about DRM pipeline, it should model, maybe it could be
>> done this way:
>> CRTC -->ENCODER0(dsi master) --> CONNECTOR0 (panel interface 0)
>>       |---> ENCODER1(dsi slave) --> CONNECTOR1 (panel interface 1)
>>
>> But I am not sure if it is not reserved only for mirroring.
>>
>> For me more tempting solution is to create meta-encoder-connector let's
>> call it dual-encoder (maybe it could be even generic), which is visible
>> to userspace as single pipeline and encapsulates both dsi bridges/panel
>> inputs. So its every callback will be translated usually to sequence of
>> callbacks to 1st and 2nd dsi, or in case of get_modes it should return
>> mode which represent sum of modes in both panels.
>> Maybe it looks more complicated, but it can be more universal - you can
>> use it with different bridges/panels even two single-panels if necessary.
>>
>> Of course I do not see the whole picture, or I can be just wrong, or
>> just freaking purist :). If there are arguments against my vision please
>> provide them.
>> I am also not strongly against your solution, I just want to show
>> alternatives, which could be better/more generic.
> I cannot really claim to know the correct way to go forward, as these
> drm-internals are still very much unknown land for me, but current
> thought points I had on this:
>
> - strawman argument ;-) : this is the same way tegra handles dual-dsi
>   in its ganged mode ... also with regards to panel handling
>   With Thierry helming both panels and the tegra dual-dsi change

Yes, and I remember our disagreement on the subject :)

>
> - while the panel may expose two DSI data interfaces, there is still
>   only one power-sequence and also only one init-command sequence.

As in case of almost all devices exposing two, or more interfaces.

>   So I don't think you can really handle a dual-dsi panels as two fully
>   separate panels, creating instead the need for both panel-instances
>   to work together

The question is if they can work separately? Ie is it possible to use
only one interface, and have picture only on left/top side, even/odd
lines? Or to have connected interfaces to two independent display
controllers (of course via some dsi bridges)? Btw I have not seen
bindings/driver for the panel, what is the panel name?
The answer for above questions would be helpful.
If the only way of work is with both dsi interfaces, exposing one
drm_panel maybe could be OK.

The point is if the panel has two interfaces it should be his
responsibility for handling it, not the poor DSI host, which is just for
converting native video signal (RGB I guess) to DSI. The other
responsible for the mess is CRTC which actually should negotiate with
panel how the data is split between both channels (left/right,
top/bottom, odd/even, ....). DSI-host here serves only as data
transport, in fact it does not need to know if it works in dual mode or
not (from HW point of view) - dual mode is just something which only
CRTC and panel should be really aware.

See what will happen to your solution if in next version of hardware vendor:
1. replace dual-panel with two panels glued together.
2. replace DSI bridges with different ones.
3. replace only one DSI briges - ie two different bridges working in
dual mode.

>
> - Right now we have two data-points on dual-something voodoo
>   (tegra+rockchip)
>   So for me it sounds hard to design something generic that survives
>   the first non-dsi use-case.

As I said I do not insist on the solution, but I want to show you it can
be dead-end. On the other side maybe dual dsi solutions will die
eventually soon, so there is no point in polishing it :)

Regards
Andrzej

>
>   The devicetree binding (see cover-letter) should be pretty stable, as
>   a panel like this will need to be bound to one of the two dsi controllers
>   so nothing hinders code changes later on if the need for a more generic
>   solution comes up.
>
>
> Heiko
>
>
>
>
>

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

end of thread, other threads:[~2018-07-09 16:02 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-06-18 10:27 [PATCH v2 0/8] drm/rockchip: migrate to common dw-mipi-dsi bridge and dual-dsi Heiko Stuebner
2018-06-18 10:27 ` [PATCH v2 1/8] drm/bridge/synopsys: dsi: move mipi_dsi_host_unregister to __dw_mipi_dsi_remove Heiko Stuebner
2018-07-03 10:40   ` Andrzej Hajda
2018-07-03 11:58   ` Philippe CORNU
2018-06-18 10:28 ` [PATCH v2 2/8] drm/bridge/synopsys: dsi: don't call __dw_mipi_dsi_probe from dw_mipi_dsi_bind Heiko Stuebner
2018-07-03 12:16   ` Andrzej Hajda
2018-07-03 12:32     ` Heiko Stübner
2018-07-04 12:23     ` Heiko Stuebner
2018-06-18 10:28 ` [PATCH v2 3/8] drm/bridge/synopsys: dsi: defer probing if panel not available in bridge-attach Heiko Stuebner
2018-07-03 11:57   ` Philippe CORNU
2018-07-03 12:42   ` Andrzej Hajda
2018-07-04 10:36     ` Heiko Stuebner
2018-06-18 10:28 ` [PATCH v2 4/8] dt-bindings: display: rockchip: update DSI controller Heiko Stuebner
2018-06-18 10:30   ` Heiko Stuebner
2018-06-18 10:28 ` [PATCH v2 5/8] drm/rockchip: dsi: migrate to use dw-mipi-dsi bridge driver Heiko Stuebner
2018-07-03 13:06   ` Andrzej Hajda
2018-06-18 10:28 ` [PATCH v2 6/8] drm/dsi: add helper function to find the second host in a dual-dsi setup Heiko Stuebner
2018-07-03 15:06   ` Andrzej Hajda
2018-07-04 10:47     ` Heiko Stuebner
2018-06-18 10:28 ` [PATCH v2 7/8] drm/bridge/synopsys: dsi: add dual-dsi support Heiko Stuebner
2018-07-03 11:57   ` Philippe CORNU
2018-07-03 17:07   ` Andrzej Hajda
2018-07-09 13:45     ` Heiko Stuebner
2018-07-09 16:02       ` Andrzej Hajda
2018-06-18 10:28 ` [PATCH v2 8/8] drm/rockchip: dsi: add dual mipi support Heiko Stuebner
2018-07-03 12:05 ` [PATCH v2 0/8] drm/rockchip: migrate to common dw-mipi-dsi bridge and dual-dsi Philippe CORNU

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.