public inbox for u-boot@lists.denx.de
 help / color / mirror / Atom feed
* [PATCH v3 00/11] Tegra DC improvements
@ 2023-03-27  8:11 Svyatoslav Ryhel
  2023-03-27  8:11 ` [PATCH v3 01/11] tegra: lcd: video: integrate display driver for t30 Svyatoslav Ryhel
                   ` (11 more replies)
  0 siblings, 12 replies; 16+ messages in thread
From: Svyatoslav Ryhel @ 2023-03-27  8:11 UTC (permalink / raw)
  To: Tom Warren, Anatolij Gustschin, Simon Glass, Svyatoslav Ryhel,
	Marcel Ziswiler, Thierry Reding, Maxim Schwalm, Dmitry Osipenko,
	Nicolas Chauvet
  Cc: u-boot

This patch set is dedicated to improvement of video support
on T20 and T30 devices. It contains:

- DC driver improvements (T30 support was added into existing T20
DC driver, it was moved into own folder, added support of reading
clocks from dts, improved work with panel ops and implemented
native 180 degree panel rotation support)

- DSI driver bring up (driver is based on mainline Linux one with
minor adjustments, only T30 tested)

- Simple panel driver tweaks (added get_display_timing ops and
implemented simple MIPI DSI panels support)

Patches were successfully tested on Paz00 board with unmodified state
and on TF101 (Ventana board T20) with old binding and with updated
binding. All work without any regressions.

---

Changes from v2:
- resend after month

Changes from v1:
- DSI driver headers were optimized
- Tested on Paz00 board

---

Marcel Ziswiler (1):
  tegra: lcd: video: integrate display driver for t30

Svyatoslav Ryhel (10):
  video: move tegra dc driver into own folder
  video: tegra-dc: get clocks from device tree
  video: tegra-dc: request timings from panel driver first
  video: tegra-dc: assign regmap directly
  video: tegra-dc: add 180 degree panel rotation
  video: tegra-dc: add panel_set_backlight call
  video: tegra-dc: pass DC regmap to internal devices
  video: tegra20: add DSI controller driver
  simple_panel: add support for get_display_timing
  simple_panel: support simple MIPI DSI panels

 arch/arm/dts/tegra30-u-boot.dtsi              |   9 +
 arch/arm/include/asm/arch-tegra/dc.h          |   8 +
 arch/arm/include/asm/arch-tegra30/display.h   |  28 +
 arch/arm/include/asm/arch-tegra30/dsi.h       | 217 +++++
 arch/arm/include/asm/arch-tegra30/pwm.h       |  13 +
 drivers/video/Kconfig                         |  11 +-
 drivers/video/Makefile                        |   2 +-
 drivers/video/simple_panel.c                  |  47 +-
 drivers/video/tegra20/Kconfig                 |  17 +
 drivers/video/tegra20/Makefile                |   4 +
 drivers/video/tegra20/mipi-phy.c              | 134 +++
 drivers/video/tegra20/mipi-phy.h              |  48 +
 drivers/video/{tegra.c => tegra20/tegra-dc.c} | 123 ++-
 drivers/video/tegra20/tegra-dsi.c             | 864 ++++++++++++++++++
 14 files changed, 1476 insertions(+), 49 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-tegra30/display.h
 create mode 100644 arch/arm/include/asm/arch-tegra30/dsi.h
 create mode 100644 arch/arm/include/asm/arch-tegra30/pwm.h
 create mode 100644 drivers/video/tegra20/Kconfig
 create mode 100644 drivers/video/tegra20/Makefile
 create mode 100644 drivers/video/tegra20/mipi-phy.c
 create mode 100644 drivers/video/tegra20/mipi-phy.h
 rename drivers/video/{tegra.c => tegra20/tegra-dc.c} (82%)
 create mode 100644 drivers/video/tegra20/tegra-dsi.c

-- 
2.37.2


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

* [PATCH v3 01/11] tegra: lcd: video: integrate display driver for t30
  2023-03-27  8:11 [PATCH v3 00/11] Tegra DC improvements Svyatoslav Ryhel
@ 2023-03-27  8:11 ` Svyatoslav Ryhel
  2023-03-27  8:11 ` [PATCH v3 02/11] video: move tegra dc driver into own folder Svyatoslav Ryhel
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Svyatoslav Ryhel @ 2023-03-27  8:11 UTC (permalink / raw)
  To: Tom Warren, Anatolij Gustschin, Simon Glass, Svyatoslav Ryhel,
	Marcel Ziswiler, Thierry Reding, Maxim Schwalm, Dmitry Osipenko,
	Nicolas Chauvet
  Cc: u-boot

From: Marcel Ziswiler <marcel.ziswiler@toradex.com>

On popular request make the display driver from T20 work on T30 as
well. Turned out to be quite straight forward. However a few notes
about some things encountered during porting: Of course the T30 device
tree was completely missing host1x as well as PWM support but it turns
out this can simply be copied from T20. The only trouble compiling the
Tegra video driver for T30 had to do with some hard-coded PWM pin
muxing for T20 which is quite ugly anyway. On T30 this gets handled by
a board specific complete pin muxing table. The older Chromium U-Boot
2011.06 which to my knowledge was the only prior attempt at enabling a
display driver for T30 for whatever reason got some clocking stuff
mixed up. Turns out at least for a single display controller T20 and
T30 can be clocked quite similar. Enjoy.

Tested-by: Andreas Westman Dorcsak <hedmoo@yahoo.com> # ASUS TF T30
Tested-by: Jonas Schwöbel <jonasschwoebel@yahoo.de> # Surface RT T30
Tested-by: Svyatoslav Ryhel <clamor95@gmail.com> # LG P895 T30
Signed-off-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 arch/arm/dts/tegra30-u-boot.dtsi            |  9 +++++++
 arch/arm/include/asm/arch-tegra30/display.h | 28 +++++++++++++++++++++
 arch/arm/include/asm/arch-tegra30/pwm.h     | 13 ++++++++++
 drivers/video/tegra.c                       | 10 ++++++--
 4 files changed, 58 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-tegra30/display.h
 create mode 100644 arch/arm/include/asm/arch-tegra30/pwm.h

diff --git a/arch/arm/dts/tegra30-u-boot.dtsi b/arch/arm/dts/tegra30-u-boot.dtsi
index 7c11972552..cf17fa803b 100644
--- a/arch/arm/dts/tegra30-u-boot.dtsi
+++ b/arch/arm/dts/tegra30-u-boot.dtsi
@@ -1,3 +1,12 @@
 #include <config.h>
 
 #include "tegra-u-boot.dtsi"
+
+/ {
+	host1x@50000000 {
+		u-boot,dm-pre-reloc;
+		dc@54200000 {
+			u-boot,dm-pre-reloc;
+		};
+	};
+};
diff --git a/arch/arm/include/asm/arch-tegra30/display.h b/arch/arm/include/asm/arch-tegra30/display.h
new file mode 100644
index 0000000000..9411525799
--- /dev/null
+++ b/arch/arm/include/asm/arch-tegra30/display.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ *  (C) Copyright 2010
+ *  NVIDIA Corporation <www.nvidia.com>
+ */
+
+#ifndef __ASM_ARCH_TEGRA_DISPLAY_H
+#define __ASM_ARCH_TEGRA_DISPLAY_H
+
+#include <asm/arch-tegra/dc.h>
+
+/* This holds information about a window which can be displayed */
+struct disp_ctl_win {
+	enum win_color_depth_id fmt;	/* Color depth/format */
+	unsigned int	bpp;		/* Bits per pixel */
+	phys_addr_t	phys_addr;	/* Physical address in memory */
+	unsigned int	x;		/* Horizontal address offset (bytes) */
+	unsigned int	y;		/* Veritical address offset (bytes) */
+	unsigned int	w;		/* Width of source window */
+	unsigned int	h;		/* Height of source window */
+	unsigned int	stride;		/* Number of bytes per line */
+	unsigned int	out_x;		/* Left edge of output window (col) */
+	unsigned int	out_y;		/* Top edge of output window (row) */
+	unsigned int	out_w;		/* Width of output window in pixels */
+	unsigned int	out_h;		/* Height of output window in pixels */
+};
+
+#endif /*__ASM_ARCH_TEGRA_DISPLAY_H*/
diff --git a/arch/arm/include/asm/arch-tegra30/pwm.h b/arch/arm/include/asm/arch-tegra30/pwm.h
new file mode 100644
index 0000000000..c314e2b5ad
--- /dev/null
+++ b/arch/arm/include/asm/arch-tegra30/pwm.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Tegra pulse width frequency modulator definitions
+ *
+ * Copyright (c) 2011 The Chromium OS Authors.
+ */
+
+#ifndef __ASM_ARCH_TEGRA30_PWM_H
+#define __ASM_ARCH_TEGRA30_PWM_H
+
+#include <asm/arch-tegra/pwm.h>
+
+#endif	/* __ASM_ARCH_TEGRA30_PWM_H */
diff --git a/drivers/video/tegra.c b/drivers/video/tegra.c
index 3f9fcd0403..5e3f6bf029 100644
--- a/drivers/video/tegra.c
+++ b/drivers/video/tegra.c
@@ -40,8 +40,8 @@ struct tegra_lcd_priv {
 
 enum {
 	/* Maximum LCD size we support */
-	LCD_MAX_WIDTH		= 1366,
-	LCD_MAX_HEIGHT		= 768,
+	LCD_MAX_WIDTH		= 1920,
+	LCD_MAX_HEIGHT		= 1200,
 	LCD_MAX_LOG2_BPP	= VIDEO_BPP16,
 };
 
@@ -307,14 +307,19 @@ static int tegra_lcd_probe(struct udevice *dev)
 	int ret;
 
 	/* Initialize the Tegra display controller */
+#ifdef CONFIG_TEGRA20
 	funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT);
+#endif
+
 	if (tegra_display_probe(blob, priv, (void *)plat->base)) {
 		printf("%s: Failed to probe display driver\n", __func__);
 		return -1;
 	}
 
+#ifdef CONFIG_TEGRA20
 	pinmux_set_func(PMUX_PINGRP_GPU, PMUX_FUNC_PWM);
 	pinmux_tristate_disable(PMUX_PINGRP_GPU);
+#endif
 
 	ret = panel_enable_backlight(priv->panel);
 	if (ret) {
@@ -414,6 +419,7 @@ static const struct video_ops tegra_lcd_ops = {
 
 static const struct udevice_id tegra_lcd_ids[] = {
 	{ .compatible = "nvidia,tegra20-dc" },
+	{ .compatible = "nvidia,tegra30-dc" },
 	{ }
 };
 
-- 
2.37.2


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

* [PATCH v3 02/11] video: move tegra dc driver into own folder
  2023-03-27  8:11 [PATCH v3 00/11] Tegra DC improvements Svyatoslav Ryhel
  2023-03-27  8:11 ` [PATCH v3 01/11] tegra: lcd: video: integrate display driver for t30 Svyatoslav Ryhel
@ 2023-03-27  8:11 ` Svyatoslav Ryhel
  2023-03-27  8:11 ` [PATCH v3 03/11] video: tegra-dc: get clocks from device tree Svyatoslav Ryhel
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Svyatoslav Ryhel @ 2023-03-27  8:11 UTC (permalink / raw)
  To: Tom Warren, Anatolij Gustschin, Simon Glass, Svyatoslav Ryhel,
	Marcel Ziswiler, Thierry Reding, Maxim Schwalm, Dmitry Osipenko,
	Nicolas Chauvet
  Cc: u-boot

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/video/Kconfig                         | 11 ++---------
 drivers/video/Makefile                        |  2 +-
 drivers/video/tegra20/Kconfig                 |  8 ++++++++
 drivers/video/tegra20/Makefile                |  3 +++
 drivers/video/{tegra.c => tegra20/tegra-dc.c} |  0
 5 files changed, 14 insertions(+), 10 deletions(-)
 create mode 100644 drivers/video/tegra20/Kconfig
 create mode 100644 drivers/video/tegra20/Makefile
 rename drivers/video/{tegra.c => tegra20/tegra-dc.c} (100%)

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 2a76d19cc8..a7f38efbb7 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -638,15 +638,6 @@ source "drivers/video/stm32/Kconfig"
 
 source "drivers/video/tidss/Kconfig"
 
-config VIDEO_TEGRA20
-	bool "Enable LCD support on Tegra20"
-	depends on OF_CONTROL
-	help
-	   Tegra20 supports video output to an attached LCD panel as well as
-	   other options such as HDMI. Only the LCD is supported in U-Boot.
-	   This option enables this support which can be used on devices which
-	   have an LCD display connected.
-
 config VIDEO_TEGRA124
 	bool "Enable video support on Tegra124"
 	help
@@ -657,6 +648,8 @@ config VIDEO_TEGRA124
 
 source "drivers/video/bridge/Kconfig"
 
+source "drivers/video/tegra20/Kconfig"
+
 source "drivers/video/imx/Kconfig"
 
 config VIDEO_MXS
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index cdb7d9a54d..e3c7b15d15 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -61,10 +61,10 @@ obj-$(CONFIG_VIDEO_OMAP3) += omap3_dss.o
 obj-$(CONFIG_VIDEO_DSI_HOST_SANDBOX) += sandbox_dsi_host.o
 obj-$(CONFIG_VIDEO_SANDBOX_SDL) += sandbox_sdl.o
 obj-$(CONFIG_VIDEO_SIMPLE) += simplefb.o
-obj-$(CONFIG_VIDEO_TEGRA20) += tegra.o
 obj-$(CONFIG_VIDEO_VESA) += vesa.o
 obj-$(CONFIG_VIDEO_SEPS525) += seps525.o
 obj-$(CONFIG_VIDEO_ZYNQMP_DPSUB) += zynqmp_dpsub.o
 
 obj-y += bridge/
 obj-y += sunxi/
+obj-y += tegra20/
diff --git a/drivers/video/tegra20/Kconfig b/drivers/video/tegra20/Kconfig
new file mode 100644
index 0000000000..2a4036b898
--- /dev/null
+++ b/drivers/video/tegra20/Kconfig
@@ -0,0 +1,8 @@
+config VIDEO_TEGRA20
+	bool "Enable Display Controller support on Tegra20 and Tegra 30"
+	depends on OF_CONTROL
+	help
+	   T20/T30 support video output to an attached LCD panel as well as
+	   other options such as HDMI. Only the LCD is supported in U-Boot.
+	   This option enables this support which can be used on devices which
+	   have an LCD display connected.
diff --git a/drivers/video/tegra20/Makefile b/drivers/video/tegra20/Makefile
new file mode 100644
index 0000000000..4517923025
--- /dev/null
+++ b/drivers/video/tegra20/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+obj-$(CONFIG_VIDEO_TEGRA20) += tegra-dc.o
diff --git a/drivers/video/tegra.c b/drivers/video/tegra20/tegra-dc.c
similarity index 100%
rename from drivers/video/tegra.c
rename to drivers/video/tegra20/tegra-dc.c
-- 
2.37.2


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

* [PATCH v3 03/11] video: tegra-dc: get clocks from device tree
  2023-03-27  8:11 [PATCH v3 00/11] Tegra DC improvements Svyatoslav Ryhel
  2023-03-27  8:11 ` [PATCH v3 01/11] tegra: lcd: video: integrate display driver for t30 Svyatoslav Ryhel
  2023-03-27  8:11 ` [PATCH v3 02/11] video: move tegra dc driver into own folder Svyatoslav Ryhel
@ 2023-03-27  8:11 ` Svyatoslav Ryhel
  2023-03-27  8:11 ` [PATCH v3 04/11] video: tegra-dc: request timings from panel driver first Svyatoslav Ryhel
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Svyatoslav Ryhel @ 2023-03-27  8:11 UTC (permalink / raw)
  To: Tom Warren, Anatolij Gustschin, Simon Glass, Svyatoslav Ryhel,
	Marcel Ziswiler, Thierry Reding, Maxim Schwalm, Dmitry Osipenko,
	Nicolas Chauvet
  Cc: u-boot

DISP1 clock may use PLLP, PLLC and PLLD as parents.
Instead of hardcoding, lets pass clock and its
parent from device tree. Default parent is PLLP.

Tested-by: Robert Eckelmann <longnoserob@gmail.com> # ASUS TF101 T20
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # Paz00
Tested-by: Andreas Westman Dorcsak <hedmoo@yahoo.com> # ASUS TF T30
Tested-by: Svyatoslav Ryhel <clamor95@gmail.com> # HTC One X T30
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/video/tegra20/tegra-dc.c | 31 +++++++++++++++++++++++--------
 1 file changed, 23 insertions(+), 8 deletions(-)

diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra20/tegra-dc.c
index 5e3f6bf029..ff67cc8989 100644
--- a/drivers/video/tegra20/tegra-dc.c
+++ b/drivers/video/tegra20/tegra-dc.c
@@ -36,6 +36,7 @@ struct tegra_lcd_priv {
 	struct disp_ctlr *disp;		/* Display controller to use */
 	fdt_addr_t frame_buffer;	/* Address of frame buffer */
 	unsigned pixel_clock;		/* Pixel clock in Hz */
+	int dc_clk[2];			/* Contains clk and its parent */
 };
 
 enum {
@@ -134,7 +135,7 @@ static int update_display_mode(struct dc_disp_reg *disp,
 	 * the display clock (typically 600MHz) to the pixel clock. We round
 	 * up or down as requried.
 	 */
-	rate = clock_get_periph_rate(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL);
+	rate = clock_get_periph_rate(priv->dc_clk[0], priv->dc_clk[1]);
 	div = ((rate * 2 + priv->pixel_clock / 2) / priv->pixel_clock) - 2;
 	debug("Display clock %lu, divider %lu\n", rate, div);
 
@@ -269,20 +270,27 @@ static int tegra_display_probe(const void *blob, struct tegra_lcd_priv *priv,
 {
 	struct disp_ctl_win window;
 	struct dc_ctlr *dc;
+	unsigned long rate = clock_get_rate(priv->dc_clk[1]);
 
 	priv->frame_buffer = (u32)default_lcd_base;
 
 	dc = (struct dc_ctlr *)priv->disp;
 
 	/*
-	 * A header file for clock constants was NAKed upstream.
-	 * TODO: Put this into the FDT and fdt_lcd struct when we have clock
-	 * support there
+	 * We halve the rate if DISP1 paret is PLLD, since actual parent
+	 * is plld_out0 which is PLLD divided by 2.
 	 */
-	clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_PERIPH,
-			       144 * 1000000);
-	clock_start_periph_pll(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL,
-			       600 * 1000000);
+	if (priv->dc_clk[1] == CLOCK_ID_DISPLAY)
+		rate /= 2;
+
+	/*
+	 * HOST1X is init by default at 150MHz with PLLC as parent
+	 */
+	clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_CGENERAL,
+			       150 * 1000000);
+	clock_start_periph_pll(priv->dc_clk[0], priv->dc_clk[1],
+			       rate);
+
 	basic_init(&dc->cmd);
 	basic_init_timer(&dc->disp);
 	rgb_enable(&dc->com);
@@ -358,6 +366,13 @@ static int tegra_lcd_of_to_plat(struct udevice *dev)
 		return -EINVAL;
 	}
 
+	ret = clock_decode_pair(dev, priv->dc_clk);
+	if (ret < 0) {
+		debug("%s: Cannot decode clocks for '%s' (ret = %d)\n",
+		      __func__, dev->name, ret);
+		return -EINVAL;
+	}
+
 	rgb = fdt_subnode_offset(blob, node, "rgb");
 	if (rgb < 0) {
 		debug("%s: Cannot find rgb subnode for '%s' (ret=%d)\n",
-- 
2.37.2


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

* [PATCH v3 04/11] video: tegra-dc: request timings from panel driver first
  2023-03-27  8:11 [PATCH v3 00/11] Tegra DC improvements Svyatoslav Ryhel
                   ` (2 preceding siblings ...)
  2023-03-27  8:11 ` [PATCH v3 03/11] video: tegra-dc: get clocks from device tree Svyatoslav Ryhel
@ 2023-03-27  8:11 ` Svyatoslav Ryhel
  2023-03-27  8:11 ` [PATCH v3 05/11] video: tegra-dc: assign regmap directly Svyatoslav Ryhel
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Svyatoslav Ryhel @ 2023-03-27  8:11 UTC (permalink / raw)
  To: Tom Warren, Anatolij Gustschin, Simon Glass, Svyatoslav Ryhel,
	Marcel Ziswiler, Thierry Reding, Maxim Schwalm, Dmitry Osipenko,
	Nicolas Chauvet
  Cc: u-boot

Check if panel driver has display timings and get those.
If panel driver does not pass timing, try to find timing
under rgb node for backwards compatibility.

Tested-by: Robert Eckelmann <longnoserob@gmail.com> # ASUS TF101 T20
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # Paz00
Tested-by: Andreas Westman Dorcsak <hedmoo@yahoo.com> # ASUS TF T30
Tested-by: Svyatoslav Ryhel <clamor95@gmail.com> # LG P895 T30
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/video/tegra20/tegra-dc.c | 29 +++++++++++++++++------------
 1 file changed, 17 insertions(+), 12 deletions(-)

diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra20/tegra-dc.c
index ff67cc8989..91298b7b7f 100644
--- a/drivers/video/tegra20/tegra-dc.c
+++ b/drivers/video/tegra20/tegra-dc.c
@@ -380,18 +380,6 @@ static int tegra_lcd_of_to_plat(struct udevice *dev)
 		return -EINVAL;
 	}
 
-	ret = fdtdec_decode_display_timing(blob, rgb, 0, &priv->timing);
-	if (ret) {
-		debug("%s: Cannot read display timing for '%s' (ret=%d)\n",
-		      __func__, dev->name, ret);
-		return -EINVAL;
-	}
-	timing = &priv->timing;
-	priv->width = timing->hactive.typ;
-	priv->height = timing->vactive.typ;
-	priv->pixel_clock = timing->pixelclock.typ;
-	priv->log2_bpp = VIDEO_BPP16;
-
 	/*
 	 * Sadly the panel phandle is in an rgb subnode so we cannot use
 	 * uclass_get_device_by_phandle().
@@ -401,6 +389,7 @@ static int tegra_lcd_of_to_plat(struct udevice *dev)
 		debug("%s: Cannot find panel information\n", __func__);
 		return -EINVAL;
 	}
+
 	ret = uclass_get_device_by_of_offset(UCLASS_PANEL, panel_node,
 					     &priv->panel);
 	if (ret) {
@@ -409,6 +398,22 @@ static int tegra_lcd_of_to_plat(struct udevice *dev)
 		return ret;
 	}
 
+	ret = panel_get_display_timing(priv->panel, &priv->timing);
+	if (ret) {
+		ret = fdtdec_decode_display_timing(blob, rgb, 0, &priv->timing);
+		if (ret) {
+			debug("%s: Cannot read display timing for '%s' (ret=%d)\n",
+			      __func__, dev->name, ret);
+			return -EINVAL;
+		}
+	}
+
+	timing = &priv->timing;
+	priv->width = timing->hactive.typ;
+	priv->height = timing->vactive.typ;
+	priv->pixel_clock = timing->pixelclock.typ;
+	priv->log2_bpp = VIDEO_BPP16;
+
 	return 0;
 }
 
-- 
2.37.2


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

* [PATCH v3 05/11] video: tegra-dc: assign regmap directly
  2023-03-27  8:11 [PATCH v3 00/11] Tegra DC improvements Svyatoslav Ryhel
                   ` (3 preceding siblings ...)
  2023-03-27  8:11 ` [PATCH v3 04/11] video: tegra-dc: request timings from panel driver first Svyatoslav Ryhel
@ 2023-03-27  8:11 ` Svyatoslav Ryhel
  2023-03-27  8:11 ` [PATCH v3 06/11] video: tegra-dc: add 180 degree panel rotation Svyatoslav Ryhel
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Svyatoslav Ryhel @ 2023-03-27  8:11 UTC (permalink / raw)
  To: Tom Warren, Anatolij Gustschin, Simon Glass, Svyatoslav Ryhel,
	Marcel Ziswiler, Thierry Reding, Maxim Schwalm, Dmitry Osipenko,
	Nicolas Chauvet
  Cc: u-boot

Tested-by: Robert Eckelmann <longnoserob@gmail.com> # ASUS TF101 T20
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # Paz00
Tested-by: Andreas Westman Dorcsak <hedmoo@yahoo.com> # ASUS TF T30
Tested-by: Svyatoslav Ryhel <clamor95@gmail.com> # LG P895 T30
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/video/tegra20/tegra-dc.c | 19 ++++++++-----------
 1 file changed, 8 insertions(+), 11 deletions(-)

diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra20/tegra-dc.c
index 91298b7b7f..e004ee362f 100644
--- a/drivers/video/tegra20/tegra-dc.c
+++ b/drivers/video/tegra20/tegra-dc.c
@@ -33,7 +33,7 @@ struct tegra_lcd_priv {
 	enum video_log2_bpp log2_bpp;	/* colour depth */
 	struct display_timing timing;
 	struct udevice *panel;
-	struct disp_ctlr *disp;		/* Display controller to use */
+	struct dc_ctlr *dc;		/* Display controller regmap */
 	fdt_addr_t frame_buffer;	/* Address of frame buffer */
 	unsigned pixel_clock;		/* Pixel clock in Hz */
 	int dc_clk[2];			/* Contains clk and its parent */
@@ -269,13 +269,10 @@ static int tegra_display_probe(const void *blob, struct tegra_lcd_priv *priv,
 			       void *default_lcd_base)
 {
 	struct disp_ctl_win window;
-	struct dc_ctlr *dc;
 	unsigned long rate = clock_get_rate(priv->dc_clk[1]);
 
 	priv->frame_buffer = (u32)default_lcd_base;
 
-	dc = (struct dc_ctlr *)priv->disp;
-
 	/*
 	 * We halve the rate if DISP1 paret is PLLD, since actual parent
 	 * is plld_out0 which is PLLD divided by 2.
@@ -291,17 +288,17 @@ static int tegra_display_probe(const void *blob, struct tegra_lcd_priv *priv,
 	clock_start_periph_pll(priv->dc_clk[0], priv->dc_clk[1],
 			       rate);
 
-	basic_init(&dc->cmd);
-	basic_init_timer(&dc->disp);
-	rgb_enable(&dc->com);
+	basic_init(&priv->dc->cmd);
+	basic_init_timer(&priv->dc->disp);
+	rgb_enable(&priv->dc->com);
 
 	if (priv->pixel_clock)
-		update_display_mode(&dc->disp, priv);
+		update_display_mode(&priv->dc->disp, priv);
 
 	if (setup_window(&window, priv))
 		return -1;
 
-	update_window(dc, &window);
+	update_window(priv->dc, &window);
 
 	return 0;
 }
@@ -360,8 +357,8 @@ static int tegra_lcd_of_to_plat(struct udevice *dev)
 	int rgb;
 	int ret;
 
-	priv->disp = dev_read_addr_ptr(dev);
-	if (!priv->disp) {
+	priv->dc = (struct dc_ctlr *)dev_read_addr_ptr(dev);
+	if (!priv->dc) {
 		debug("%s: No display controller address\n", __func__);
 		return -EINVAL;
 	}
-- 
2.37.2


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

* [PATCH v3 06/11] video: tegra-dc: add 180 degree panel rotation
  2023-03-27  8:11 [PATCH v3 00/11] Tegra DC improvements Svyatoslav Ryhel
                   ` (4 preceding siblings ...)
  2023-03-27  8:11 ` [PATCH v3 05/11] video: tegra-dc: assign regmap directly Svyatoslav Ryhel
@ 2023-03-27  8:11 ` Svyatoslav Ryhel
  2023-04-16 17:29   ` Dmitry Osipenko
  2023-03-27  8:11 ` [PATCH v3 07/11] video: tegra-dc: add panel_set_backlight call Svyatoslav Ryhel
                   ` (5 subsequent siblings)
  11 siblings, 1 reply; 16+ messages in thread
From: Svyatoslav Ryhel @ 2023-03-27  8:11 UTC (permalink / raw)
  To: Tom Warren, Anatolij Gustschin, Simon Glass, Svyatoslav Ryhel,
	Marcel Ziswiler, Thierry Reding, Maxim Schwalm, Dmitry Osipenko,
	Nicolas Chauvet
  Cc: u-boot

Unlike 90 and 270 degree rotation, 180 degree rotation is more
common and does not require scaling. Implement it for correct
grouper support.

Tested-by: Andreas Westman Dorcsak <hedmoo@yahoo.com> # Google Nexus 7 2012
Tested-by: Svyatoslav Ryhel <clamor95@gmail.com> # Google Nexus 7 2012
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/video/tegra20/tegra-dc.c | 23 +++++++++++++++++++----
 1 file changed, 19 insertions(+), 4 deletions(-)

diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra20/tegra-dc.c
index e004ee362f..e279650922 100644
--- a/drivers/video/tegra20/tegra-dc.c
+++ b/drivers/video/tegra20/tegra-dc.c
@@ -37,6 +37,7 @@ struct tegra_lcd_priv {
 	fdt_addr_t frame_buffer;	/* Address of frame buffer */
 	unsigned pixel_clock;		/* Pixel clock in Hz */
 	int dc_clk[2];			/* Contains clk and its parent */
+	bool rotation;			/* 180 degree panel turn */
 };
 
 enum {
@@ -46,8 +47,10 @@ enum {
 	LCD_MAX_LOG2_BPP	= VIDEO_BPP16,
 };
 
-static void update_window(struct dc_ctlr *dc, struct disp_ctl_win *win)
+static void update_window(struct tegra_lcd_priv *priv,
+			  struct disp_ctl_win *win)
 {
+	struct dc_ctlr *dc = priv->dc;
 	unsigned h_dda, v_dda;
 	unsigned long val;
 
@@ -88,6 +91,10 @@ static void update_window(struct dc_ctlr *dc, struct disp_ctl_win *win)
 	val = WIN_ENABLE;
 	if (win->bpp < 24)
 		val |= COLOR_EXPAND;
+
+	if (priv->rotation)
+		val |= H_DIRECTION | V_DIRECTION;
+
 	writel(val, &dc->win.win_opt);
 
 	writel((unsigned long)win->phys_addr, &dc->winbuf.start_addr);
@@ -224,8 +231,14 @@ static void rgb_enable(struct dc_com_reg *com)
 static int setup_window(struct disp_ctl_win *win,
 			struct tegra_lcd_priv *priv)
 {
-	win->x = 0;
-	win->y = 0;
+	if (priv->rotation) {
+		win->x = priv->width * 2;
+		win->y = priv->height;
+	} else {
+		win->x = 0;
+		win->y = 0;
+	}
+
 	win->w = priv->width;
 	win->h = priv->height;
 	win->out_x = 0;
@@ -298,7 +311,7 @@ static int tegra_display_probe(const void *blob, struct tegra_lcd_priv *priv,
 	if (setup_window(&window, priv))
 		return -1;
 
-	update_window(priv->dc, &window);
+	update_window(priv, &window);
 
 	return 0;
 }
@@ -370,6 +383,8 @@ static int tegra_lcd_of_to_plat(struct udevice *dev)
 		return -EINVAL;
 	}
 
+	priv->rotation = dev_read_bool(dev, "nvidia,180-rotation");
+
 	rgb = fdt_subnode_offset(blob, node, "rgb");
 	if (rgb < 0) {
 		debug("%s: Cannot find rgb subnode for '%s' (ret=%d)\n",
-- 
2.37.2


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

* [PATCH v3 07/11] video: tegra-dc: add panel_set_backlight call
  2023-03-27  8:11 [PATCH v3 00/11] Tegra DC improvements Svyatoslav Ryhel
                   ` (5 preceding siblings ...)
  2023-03-27  8:11 ` [PATCH v3 06/11] video: tegra-dc: add 180 degree panel rotation Svyatoslav Ryhel
@ 2023-03-27  8:11 ` Svyatoslav Ryhel
  2023-03-27  8:11 ` [PATCH v3 08/11] video: tegra-dc: pass DC regmap to internal devices Svyatoslav Ryhel
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Svyatoslav Ryhel @ 2023-03-27  8:11 UTC (permalink / raw)
  To: Tom Warren, Anatolij Gustschin, Simon Glass, Svyatoslav Ryhel,
	Marcel Ziswiler, Thierry Reding, Maxim Schwalm, Dmitry Osipenko,
	Nicolas Chauvet
  Cc: u-boot

Tegra DC driver does not call panel_set_backlight, which can
result in absence of backlight on device. Fix this by calling
panel_set_backlight with BACKLIGHT_DEFAULT just after
panel_enable_backlight.

Tested-by: Robert Eckelmann <longnoserob@gmail.com> # ASUS TF101 T20
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # Paz00
Tested-by: Andreas Westman Dorcsak <hedmoo@yahoo.com> # ASUS TF T30
Tested-by: Svyatoslav Ryhel <clamor95@gmail.com> # LG P895 T30
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/video/tegra20/tegra-dc.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra20/tegra-dc.c
index e279650922..00462fa188 100644
--- a/drivers/video/tegra20/tegra-dc.c
+++ b/drivers/video/tegra20/tegra-dc.c
@@ -4,6 +4,7 @@
  */
 
 #include <common.h>
+#include <backlight.h>
 #include <dm.h>
 #include <fdtdec.h>
 #include <log.h>
@@ -345,6 +346,12 @@ static int tegra_lcd_probe(struct udevice *dev)
 		return ret;
 	}
 
+	ret = panel_set_backlight(priv->panel, BACKLIGHT_DEFAULT);
+	if (ret) {
+		debug("%s: Cannot set backlight to default, ret=%d\n", __func__, ret);
+		return ret;
+	}
+
 	mmu_set_region_dcache_behaviour(priv->frame_buffer, plat->size,
 					DCACHE_WRITETHROUGH);
 
-- 
2.37.2


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

* [PATCH v3 08/11] video: tegra-dc: pass DC regmap to internal devices
  2023-03-27  8:11 [PATCH v3 00/11] Tegra DC improvements Svyatoslav Ryhel
                   ` (6 preceding siblings ...)
  2023-03-27  8:11 ` [PATCH v3 07/11] video: tegra-dc: add panel_set_backlight call Svyatoslav Ryhel
@ 2023-03-27  8:11 ` Svyatoslav Ryhel
  2023-03-27  8:11 ` [PATCH v3 09/11] video: tegra20: add DSI controller driver Svyatoslav Ryhel
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Svyatoslav Ryhel @ 2023-03-27  8:11 UTC (permalink / raw)
  To: Tom Warren, Anatolij Gustschin, Simon Glass, Svyatoslav Ryhel,
	Marcel Ziswiler, Thierry Reding, Maxim Schwalm, Dmitry Osipenko,
	Nicolas Chauvet
  Cc: u-boot

Internal video devices like DSI and HDMI controllers
require sending commands into DC register field.
To make this available, lets create platform data,
which is restricted to pass DC regmap only to
pre-defined devices.

Tested-by: Andreas Westman Dorcsak <hedmoo@yahoo.com> # ASUS TF T30
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # Paz00
Tested-by: Robert Eckelmann <longnoserob@gmail.com> # ASUS TF101 T20
Tested-by: Svyatoslav Ryhel <clamor95@gmail.com> # HTC One X T30
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 arch/arm/include/asm/arch-tegra/dc.h | 8 ++++++++
 drivers/video/tegra20/tegra-dc.c     | 8 ++++++++
 2 files changed, 16 insertions(+)

diff --git a/arch/arm/include/asm/arch-tegra/dc.h b/arch/arm/include/asm/arch-tegra/dc.h
index 6444af2993..7613d84f22 100644
--- a/arch/arm/include/asm/arch-tegra/dc.h
+++ b/arch/arm/include/asm/arch-tegra/dc.h
@@ -569,4 +569,12 @@ enum {
 #define DC_N_WINDOWS			5
 #define DC_REG_SAVE_SPACE		(DC_N_WINDOWS + 5)
 
+#define TEGRA_DSI_A		"dsi@54300000"
+#define TEGRA_DSI_B		"dsi@54400000"
+
+struct tegra_dc_plat {
+	struct udevice *dev;		/* Display controller device */
+	struct dc_ctlr *dc;		/* Display controller regmap */
+};
+
 #endif /* __ASM_ARCH_TEGRA_DC_H */
diff --git a/drivers/video/tegra20/tegra-dc.c b/drivers/video/tegra20/tegra-dc.c
index 00462fa188..f53ad46397 100644
--- a/drivers/video/tegra20/tegra-dc.c
+++ b/drivers/video/tegra20/tegra-dc.c
@@ -417,6 +417,14 @@ static int tegra_lcd_of_to_plat(struct udevice *dev)
 		return ret;
 	}
 
+	if (!strcmp(priv->panel->name, TEGRA_DSI_A) ||
+	    !strcmp(priv->panel->name, TEGRA_DSI_B)) {
+		struct tegra_dc_plat *dc_plat = dev_get_plat(priv->panel);
+
+		dc_plat->dev = dev;
+		dc_plat->dc = priv->dc;
+	}
+
 	ret = panel_get_display_timing(priv->panel, &priv->timing);
 	if (ret) {
 		ret = fdtdec_decode_display_timing(blob, rgb, 0, &priv->timing);
-- 
2.37.2


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

* [PATCH v3 09/11] video: tegra20: add DSI controller driver
  2023-03-27  8:11 [PATCH v3 00/11] Tegra DC improvements Svyatoslav Ryhel
                   ` (7 preceding siblings ...)
  2023-03-27  8:11 ` [PATCH v3 08/11] video: tegra-dc: pass DC regmap to internal devices Svyatoslav Ryhel
@ 2023-03-27  8:11 ` Svyatoslav Ryhel
  2023-03-27  8:11 ` [PATCH v3 10/11] simple_panel: add support for get_display_timing Svyatoslav Ryhel
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Svyatoslav Ryhel @ 2023-03-27  8:11 UTC (permalink / raw)
  To: Tom Warren, Anatolij Gustschin, Simon Glass, Svyatoslav Ryhel,
	Marcel Ziswiler, Thierry Reding, Maxim Schwalm, Dmitry Osipenko,
	Nicolas Chauvet
  Cc: u-boot

Adds support for both DSI outputs found on Tegra. Only very
minimal functionality is implemented, so advanced features
like ganged mode won't work. Driver is heavily based on
mainline Tegra DSI and re-uses much of its features.

Only T30 is supported for now but T20 support can be added
if any supported devices will be found.

Driver is wrapped as panel driver since Tegra DC driver supports
only panel drivers calls.

Tested-by: Andreas Westman Dorcsak <hedmoo@yahoo.com> # ASUS TF600T T30
Tested-by: Svyatoslav Ryhel <clamor95@gmail.com> # HTC One X T30
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 arch/arm/include/asm/arch-tegra30/dsi.h | 217 ++++++
 drivers/video/tegra20/Kconfig           |   9 +
 drivers/video/tegra20/Makefile          |   1 +
 drivers/video/tegra20/mipi-phy.c        | 134 ++++
 drivers/video/tegra20/mipi-phy.h        |  48 ++
 drivers/video/tegra20/tegra-dsi.c       | 864 ++++++++++++++++++++++++
 6 files changed, 1273 insertions(+)
 create mode 100644 arch/arm/include/asm/arch-tegra30/dsi.h
 create mode 100644 drivers/video/tegra20/mipi-phy.c
 create mode 100644 drivers/video/tegra20/mipi-phy.h
 create mode 100644 drivers/video/tegra20/tegra-dsi.c

diff --git a/arch/arm/include/asm/arch-tegra30/dsi.h b/arch/arm/include/asm/arch-tegra30/dsi.h
new file mode 100644
index 0000000000..7ade132613
--- /dev/null
+++ b/arch/arm/include/asm/arch-tegra30/dsi.h
@@ -0,0 +1,217 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ *  (C) Copyright 2010
+ *  NVIDIA Corporation <www.nvidia.com>
+ */
+
+#ifndef __ASM_ARCH_TEGRA_DSI_H
+#define __ASM_ARCH_TEGRA_DSI_H
+
+#ifndef __ASSEMBLY__
+#include <linux/bitops.h>
+#endif
+
+/* Register definitions for the Tegra display serial interface */
+
+/* DSI syncpoint register 0x000 ~ 0x002 */
+struct dsi_syncpt_reg {
+	/* Address 0x000 ~ 0x002 */
+	uint incr_syncpt;		/* _INCR_SYNCPT_0 */
+	uint incr_syncpt_ctrl;		/* _INCR_SYNCPT_CNTRL_0 */
+	uint incr_syncpt_err;		/* _INCR_SYNCPT_ERROR_0 */
+};
+
+/* DSI misc register 0x008 ~ 0x015 */
+struct dsi_misc_reg {
+	/* Address 0x008 ~ 0x015 */
+	uint ctxsw;			/* _CTXSW_0 */
+	uint dsi_rd_data;		/* _DSI_RD_DATA_0 */
+	uint dsi_wr_data;		/* _DSI_WR_DATA_0 */
+	uint dsi_pwr_ctrl;		/* _DSI_POWER_CONTROL_0 */
+	uint int_enable;		/* _INT_ENABLE_0 */
+	uint int_status;		/* _INT_STATUS_0 */
+	uint int_mask;			/* _INT_MASK_0 */
+	uint host_dsi_ctrl;		/* _HOST_DSI_CONTROL_0 */
+	uint dsi_ctrl;			/* _DSI_CONTROL_0 */
+	uint dsi_sol_delay;		/* _DSI_SOL_DELAY_0 */
+	uint dsi_max_threshold;		/* _DSI_MAX_THRESHOLD_0 */
+	uint dsi_trigger;		/* _DSI_TRIGGER_0 */
+	uint dsi_tx_crc;		/* _DSI_TX_CRC_0 */
+	uint dsi_status;		/* _DSI_STATUS_0 */
+};
+
+/* DSI init sequence register 0x01a ~ 0x022 */
+struct dsi_init_seq_reg {
+	/* Address 0x01a ~ 0x022 */
+	uint dsi_init_seq_ctrl;		/* _DSI_INIT_SEQ_CONTROL_0 */
+	uint dsi_init_seq_data_0;	/* _DSI_INIT_SEQ_DATA_0_0 */
+	uint dsi_init_seq_data_1;	/* _DSI_INIT_SEQ_DATA_1_0 */
+	uint dsi_init_seq_data_2;	/* _DSI_INIT_SEQ_DATA_2_0 */
+	uint dsi_init_seq_data_3;	/* _DSI_INIT_SEQ_DATA_3_0 */
+	uint dsi_init_seq_data_4;	/* _DSI_INIT_SEQ_DATA_4_0 */
+	uint dsi_init_seq_data_5;	/* _DSI_INIT_SEQ_DATA_5_0 */
+	uint dsi_init_seq_data_6;	/* _DSI_INIT_SEQ_DATA_6_0 */
+	uint dsi_init_seq_data_7;	/* _DSI_INIT_SEQ_DATA_7_0 */
+};
+
+/* DSI packet sequence register 0x023 ~ 0x02e */
+struct dsi_pkt_seq_reg {
+	/* Address 0x023 ~ 0x02e */
+	uint dsi_pkt_seq_0_lo;		/* _DSI_PKT_SEQ_0_LO_0 */
+	uint dsi_pkt_seq_0_hi;		/* _DSI_PKT_SEQ_0_HI_0 */
+	uint dsi_pkt_seq_1_lo;		/* _DSI_PKT_SEQ_1_LO_0 */
+	uint dsi_pkt_seq_1_hi;		/* _DSI_PKT_SEQ_1_HI_0 */
+	uint dsi_pkt_seq_2_lo;		/* _DSI_PKT_SEQ_2_LO_0 */
+	uint dsi_pkt_seq_2_hi;		/* _DSI_PKT_SEQ_2_HI_0 */
+	uint dsi_pkt_seq_3_lo;		/* _DSI_PKT_SEQ_3_LO_0 */
+	uint dsi_pkt_seq_3_hi;		/* _DSI_PKT_SEQ_3_HI_0 */
+	uint dsi_pkt_seq_4_lo;		/* _DSI_PKT_SEQ_4_LO_0 */
+	uint dsi_pkt_seq_4_hi;		/* _DSI_PKT_SEQ_4_HI_0 */
+	uint dsi_pkt_seq_5_lo;		/* _DSI_PKT_SEQ_5_LO_0 */
+	uint dsi_pkt_seq_5_hi;		/* _DSI_PKT_SEQ_5_HI_0 */
+};
+
+/* DSI packet length register 0x033 ~ 0x037 */
+struct dsi_pkt_len_reg {
+	/* Address 0x033 ~ 0x037 */
+	uint dsi_dcs_cmds;		/* _DSI_DCS_CMDS_0 */
+	uint dsi_pkt_len_0_1;		/* _DSI_PKT_LEN_0_1_0 */
+	uint dsi_pkt_len_2_3;		/* _DSI_PKT_LEN_2_3_0 */
+	uint dsi_pkt_len_4_5;		/* _DSI_PKT_LEN_4_5_0 */
+	uint dsi_pkt_len_6_7;		/* _DSI_PKT_LEN_6_7_0 */
+};
+
+/* DSI PHY timing register 0x03c ~ 0x03f */
+struct dsi_timing_reg {
+	/* Address 0x03c ~ 0x03f */
+	uint dsi_phy_timing_0;		/* _DSI_PHY_TIMING_0_0 */
+	uint dsi_phy_timing_1;		/* _DSI_PHY_TIMING_1_0 */
+	uint dsi_phy_timing_2;		/* _DSI_PHY_TIMING_2_0 */
+	uint dsi_bta_timing;		/* _DSI_BTA_TIMING_0 */
+};
+
+/* DSI timeout register 0x044 ~ 0x046 */
+struct dsi_timeout_reg {
+	/* Address 0x044 ~ 0x046 */
+	uint dsi_timeout_0;		/* _DSI_TIMEOUT_0_0 */
+	uint dsi_timeout_1;		/* _DSI_TIMEOUT_1_0 */
+	uint dsi_to_tally;		/* _DSI_TO_TALLY_0 */
+};
+
+/* DSI PAD control register 0x04b ~ 0x04e */
+struct dsi_pad_ctrl_reg {
+	/* Address 0x04b ~ 0x04e */
+	uint pad_ctrl;			/* _PAD_CONTROL_0 */
+	uint pad_ctrl_cd;		/* _PAD_CONTROL_CD_0 */
+	uint pad_cd_status;		/* _PAD_CD_STATUS_0 */
+	uint dsi_vid_mode_control;	/* _DSI_VID_MODE_CONTROL_0 */
+};
+
+/* Display Serial Interface (DSI_) regs */
+struct dsi_ctlr {
+	struct dsi_syncpt_reg syncpt;	/* SYNCPT register 0x000 ~ 0x002 */
+	uint reserved0[5];		/* reserved_0[5] */
+
+	struct dsi_misc_reg misc;	/* MISC register 0x008 ~ 0x015 */
+	uint reserved1[4];		/* reserved_1[4] */
+
+	struct dsi_init_seq_reg init;	/* INIT register 0x01a ~ 0x022 */
+	struct dsi_pkt_seq_reg pkt;	/* PKT register 0x023 ~ 0x02e */
+	uint reserved2[4];		/* reserved_2[4] */
+
+	struct dsi_pkt_len_reg len;	/* LEN registers 0x033 ~ 0x037 */
+	uint reserved3[4];		/* reserved_3[4] */
+
+	struct dsi_timing_reg ptiming;	/* TIMING registers 0x03c ~ 0x03f */
+	uint reserved4[4];		/* reserved_4[4] */
+
+	struct dsi_timeout_reg timeout;	/* TIMEOUT registers 0x044 ~ 0x046 */
+	uint reserved5[4];		/* reserved_5[4] */
+
+	struct dsi_pad_ctrl_reg pad;	/* PAD registers 0x04b ~ 0x04e */
+};
+
+#define DSI_POWER_CONTROL_ENABLE	BIT(0)
+
+#define DSI_HOST_CONTROL_FIFO_RESET	BIT(21)
+#define DSI_HOST_CONTROL_CRC_RESET	BIT(20)
+#define DSI_HOST_CONTROL_TX_TRIG_SOL	(0 << 12)
+#define DSI_HOST_CONTROL_TX_TRIG_FIFO	(1 << 12)
+#define DSI_HOST_CONTROL_TX_TRIG_HOST	(2 << 12)
+#define DSI_HOST_CONTROL_RAW		BIT(6)
+#define DSI_HOST_CONTROL_HS		BIT(5)
+#define DSI_HOST_CONTROL_FIFO_SEL	BIT(4)
+#define DSI_HOST_CONTROL_IMM_BTA	BIT(3)
+#define DSI_HOST_CONTROL_PKT_BTA	BIT(2)
+#define DSI_HOST_CONTROL_CS		BIT(1)
+#define DSI_HOST_CONTROL_ECC		BIT(0)
+
+#define DSI_CONTROL_HS_CLK_CTRL		BIT(20)
+#define DSI_CONTROL_CHANNEL(c)		(((c) & 0x3) << 16)
+#define DSI_CONTROL_FORMAT(f)		(((f) & 0x3) << 12)
+#define DSI_CONTROL_TX_TRIG(x)		(((x) & 0x3) <<  8)
+#define DSI_CONTROL_LANES(n)		(((n) & 0x3) <<  4)
+#define DSI_CONTROL_DCS_ENABLE		BIT(3)
+#define DSI_CONTROL_SOURCE(s)		(((s) & 0x1) <<  2)
+#define DSI_CONTROL_VIDEO_ENABLE	BIT(1)
+#define DSI_CONTROL_HOST_ENABLE		BIT(0)
+
+#define DSI_TRIGGER_HOST		BIT(1)
+#define DSI_TRIGGER_VIDEO		BIT(0)
+
+#define DSI_STATUS_IDLE			BIT(10)
+#define DSI_STATUS_UNDERFLOW		BIT(9)
+#define DSI_STATUS_OVERFLOW		BIT(8)
+
+#define DSI_TIMING_FIELD(value, period, hwinc) \
+	((DIV_ROUND_CLOSEST(value, period) - (hwinc)) & 0xff)
+
+#define DSI_TIMEOUT_LRX(x)		(((x) & 0xffff) << 16)
+#define DSI_TIMEOUT_HTX(x)		(((x) & 0xffff) <<  0)
+#define DSI_TIMEOUT_PR(x)		(((x) & 0xffff) << 16)
+#define DSI_TIMEOUT_TA(x)		(((x) & 0xffff) <<  0)
+
+#define DSI_TALLY_TA(x)			(((x) & 0xff) << 16)
+#define DSI_TALLY_LRX(x)		(((x) & 0xff) <<  8)
+#define DSI_TALLY_HTX(x)		(((x) & 0xff) <<  0)
+
+#define DSI_PAD_CONTROL_PAD_PULLDN_ENAB(x)	(((x) & 0x1) << 28)
+#define DSI_PAD_CONTROL_PAD_SLEWUPADJ(x)	(((x) & 0x7) << 24)
+#define DSI_PAD_CONTROL_PAD_SLEWDNADJ(x)	(((x) & 0x7) << 20)
+#define DSI_PAD_CONTROL_PAD_PREEMP_EN(x)	(((x) & 0x1) << 19)
+#define DSI_PAD_CONTROL_PAD_PDIO_CLK(x)		(((x) & 0x1) << 18)
+#define DSI_PAD_CONTROL_PAD_PDIO(x)		(((x) & 0x3) << 16)
+#define DSI_PAD_CONTROL_PAD_LPUPADJ(x)		(((x) & 0x3) << 14)
+#define DSI_PAD_CONTROL_PAD_LPDNADJ(x)		(((x) & 0x3) << 12)
+
+/*
+ * pixel format as used in the DSI_CONTROL_FORMAT field
+ */
+enum tegra_dsi_format {
+	TEGRA_DSI_FORMAT_16P,
+	TEGRA_DSI_FORMAT_18NP,
+	TEGRA_DSI_FORMAT_18P,
+	TEGRA_DSI_FORMAT_24P,
+};
+
+/* DSI calibration in VI region */
+#define TEGRA_VI_BASE			0x54080000
+
+#define CSI_CILA_MIPI_CAL_CONFIG_0	0x22a
+#define  MIPI_CAL_TERMOSA(x)		(((x) & 0x1f) << 0)
+
+#define CSI_CILB_MIPI_CAL_CONFIG_0	0x22b
+#define  MIPI_CAL_TERMOSB(x)		(((x) & 0x1f) << 0)
+
+#define CSI_CIL_PAD_CONFIG		0x229
+#define  PAD_CIL_PDVREG(x)		(((x) & 0x01) << 1)
+
+#define CSI_DSI_MIPI_CAL_CONFIG		0x234
+#define  MIPI_CAL_HSPDOSD(x)		(((x) & 0x1f) << 16)
+#define  MIPI_CAL_HSPUOSD(x)		(((x) & 0x1f) << 8)
+
+#define CSI_MIPIBIAS_PAD_CONFIG		0x235
+#define  PAD_DRIV_DN_REF(x)		(((x) & 0x7) << 16)
+#define  PAD_DRIV_UP_REF(x)		(((x) & 0x7) << 8)
+
+#endif /* __ASM_ARCH_TEGRA_DSI_H */
diff --git a/drivers/video/tegra20/Kconfig b/drivers/video/tegra20/Kconfig
index 2a4036b898..5b1dfbfbbe 100644
--- a/drivers/video/tegra20/Kconfig
+++ b/drivers/video/tegra20/Kconfig
@@ -6,3 +6,12 @@ config VIDEO_TEGRA20
 	   other options such as HDMI. Only the LCD is supported in U-Boot.
 	   This option enables this support which can be used on devices which
 	   have an LCD display connected.
+
+config VIDEO_DSI_TEGRA30
+	bool "Enable Tegra 30 DSI support"
+	depends on PANEL && DM_GPIO
+	select VIDEO_TEGRA20
+	select VIDEO_MIPI_DSI
+	help
+	   T30 has native support for DSI panels. This option enables support
+	   for such panels which can be used on endeavoru and tf600t.
diff --git a/drivers/video/tegra20/Makefile b/drivers/video/tegra20/Makefile
index 4517923025..e82ee96962 100644
--- a/drivers/video/tegra20/Makefile
+++ b/drivers/video/tegra20/Makefile
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0+
 
 obj-$(CONFIG_VIDEO_TEGRA20) += tegra-dc.o
+obj-$(CONFIG_VIDEO_DSI_TEGRA30) += tegra-dsi.o mipi-phy.o
diff --git a/drivers/video/tegra20/mipi-phy.c b/drivers/video/tegra20/mipi-phy.c
new file mode 100644
index 0000000000..c3ebc4074b
--- /dev/null
+++ b/drivers/video/tegra20/mipi-phy.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ */
+
+#include <common.h>
+#include <linux/err.h>
+
+#include "mipi-phy.h"
+
+/*
+ * Default D-PHY timings based on MIPI D-PHY specification. Derived from the
+ * valid ranges specified in Section 6.9, Table 14, Page 40 of the D-PHY
+ * specification (v1.2) with minor adjustments.
+ */
+int mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing,
+				 unsigned long period)
+{
+	timing->clkmiss = 0;
+	timing->clkpost = 70 + 52 * period;
+	timing->clkpre = 8;
+	timing->clkprepare = 65;
+	timing->clksettle = 95;
+	timing->clktermen = 0;
+	timing->clktrail = 80;
+	timing->clkzero = 260;
+	timing->dtermen = 0;
+	timing->eot = 0;
+	timing->hsexit = 120;
+	timing->hsprepare = 65 + 5 * period;
+	timing->hszero = 145 + 5 * period;
+	timing->hssettle = 85 + 6 * period;
+	timing->hsskip = 40;
+
+	/*
+	 * The MIPI D-PHY specification (Section 6.9, v1.2, Table 14, Page 40)
+	 * contains this formula as:
+	 *
+	 *     T_HS-TRAIL = max(n * 8 * period, 60 + n * 4 * period)
+	 *
+	 * where n = 1 for forward-direction HS mode and n = 4 for reverse-
+	 * direction HS mode. There's only one setting and this function does
+	 * not parameterize on anything other that period, so this code will
+	 * assumes that reverse-direction HS mode is supported and uses n = 4.
+	 */
+	timing->hstrail = max(4 * 8 * period, 60 + 4 * 4 * period);
+
+	timing->init = 100000;
+	timing->lpx = 60;
+	timing->taget = 5 * timing->lpx;
+	timing->tago = 4 * timing->lpx;
+	timing->tasure = 2 * timing->lpx;
+	timing->wakeup = 1000000;
+
+	return 0;
+}
+
+/*
+ * Validate D-PHY timing according to MIPI D-PHY specification
+ * (v1.2, Section 6.9 "Global Operation Timing Parameters").
+ */
+int mipi_dphy_timing_validate(struct mipi_dphy_timing *timing,
+			      unsigned long period)
+{
+	if (timing->clkmiss > 60)
+		return -EINVAL;
+
+	if (timing->clkpost < (60 + 52 * period))
+		return -EINVAL;
+
+	if (timing->clkpre < 8)
+		return -EINVAL;
+
+	if (timing->clkprepare < 38 || timing->clkprepare > 95)
+		return -EINVAL;
+
+	if (timing->clksettle < 95 || timing->clksettle > 300)
+		return -EINVAL;
+
+	if (timing->clktermen > 38)
+		return -EINVAL;
+
+	if (timing->clktrail < 60)
+		return -EINVAL;
+
+	if (timing->clkprepare + timing->clkzero < 300)
+		return -EINVAL;
+
+	if (timing->dtermen > 35 + 4 * period)
+		return -EINVAL;
+
+	if (timing->eot > 105 + 12 * period)
+		return -EINVAL;
+
+	if (timing->hsexit < 100)
+		return -EINVAL;
+
+	if (timing->hsprepare < 40 + 4 * period ||
+	    timing->hsprepare > 85 + 6 * period)
+		return -EINVAL;
+
+	if (timing->hsprepare + timing->hszero < 145 + 10 * period)
+		return -EINVAL;
+
+	if ((timing->hssettle < 85 + 6 * period) ||
+	    (timing->hssettle > 145 + 10 * period))
+		return -EINVAL;
+
+	if (timing->hsskip < 40 || timing->hsskip > 55 + 4 * period)
+		return -EINVAL;
+
+	if (timing->hstrail < max(8 * period, 60 + 4 * period))
+		return -EINVAL;
+
+	if (timing->init < 100000)
+		return -EINVAL;
+
+	if (timing->lpx < 50)
+		return -EINVAL;
+
+	if (timing->taget != 5 * timing->lpx)
+		return -EINVAL;
+
+	if (timing->tago != 4 * timing->lpx)
+		return -EINVAL;
+
+	if (timing->tasure < timing->lpx || timing->tasure > 2 * timing->lpx)
+		return -EINVAL;
+
+	if (timing->wakeup < 1000000)
+		return -EINVAL;
+
+	return 0;
+}
diff --git a/drivers/video/tegra20/mipi-phy.h b/drivers/video/tegra20/mipi-phy.h
new file mode 100644
index 0000000000..41889a7503
--- /dev/null
+++ b/drivers/video/tegra20/mipi-phy.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ */
+
+#ifndef DRM_TEGRA_MIPI_PHY_H
+#define DRM_TEGRA_MIPI_PHY_H
+
+/*
+ * D-PHY timing parameters
+ *
+ * A detailed description of these parameters can be found in the  MIPI
+ * Alliance Specification for D-PHY, Section 5.9 "Global Operation Timing
+ * Parameters".
+ *
+ * All parameters are specified in nanoseconds.
+ */
+struct mipi_dphy_timing {
+	unsigned int clkmiss;
+	unsigned int clkpost;
+	unsigned int clkpre;
+	unsigned int clkprepare;
+	unsigned int clksettle;
+	unsigned int clktermen;
+	unsigned int clktrail;
+	unsigned int clkzero;
+	unsigned int dtermen;
+	unsigned int eot;
+	unsigned int hsexit;
+	unsigned int hsprepare;
+	unsigned int hszero;
+	unsigned int hssettle;
+	unsigned int hsskip;
+	unsigned int hstrail;
+	unsigned int init;
+	unsigned int lpx;
+	unsigned int taget;
+	unsigned int tago;
+	unsigned int tasure;
+	unsigned int wakeup;
+};
+
+int mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing,
+				 unsigned long period);
+int mipi_dphy_timing_validate(struct mipi_dphy_timing *timing,
+			      unsigned long period);
+
+#endif
diff --git a/drivers/video/tegra20/tegra-dsi.c b/drivers/video/tegra20/tegra-dsi.c
new file mode 100644
index 0000000000..8c3404e085
--- /dev/null
+++ b/drivers/video/tegra20/tegra-dsi.c
@@ -0,0 +1,864 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ * Copyright (c) 2022 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <log.h>
+#include <misc.h>
+#include <mipi_display.h>
+#include <mipi_dsi.h>
+#include <backlight.h>
+#include <panel.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <power/regulator.h>
+
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/display.h>
+#include <asm/arch-tegra30/dsi.h>
+
+#include "mipi-phy.h"
+
+#define USEC_PER_SEC	1000000L
+#define NSEC_PER_SEC	1000000000L
+
+struct tegra_dsi_priv {
+	struct mipi_dsi_host host;
+	struct mipi_dsi_device device;
+	struct mipi_dphy_timing dphy_timing;
+
+	struct udevice *panel;
+	struct display_timing timing;
+
+	struct dsi_ctlr *dsi;
+	struct udevice *avdd;
+
+	enum tegra_dsi_format format;
+
+	int dsi_clk;
+	int video_fifo_depth;
+	int host_fifo_depth;
+};
+
+static void tegra_dc_enable_controller(struct udevice *dev)
+{
+	struct tegra_dc_plat *dc_plat = dev_get_plat(dev);
+	struct dc_ctlr *dc = dc_plat->dc;
+	u32 value;
+
+	value = readl(&dc->disp.disp_win_opt);
+	value |= DSI_ENABLE;
+	writel(value, &dc->disp.disp_win_opt);
+
+	writel(GENERAL_UPDATE, &dc->cmd.state_ctrl);
+	writel(GENERAL_ACT_REQ, &dc->cmd.state_ctrl);
+}
+
+static const char * const error_report[16] = {
+	"SoT Error",
+	"SoT Sync Error",
+	"EoT Sync Error",
+	"Escape Mode Entry Command Error",
+	"Low-Power Transmit Sync Error",
+	"Peripheral Timeout Error",
+	"False Control Error",
+	"Contention Detected",
+	"ECC Error, single-bit",
+	"ECC Error, multi-bit",
+	"Checksum Error",
+	"DSI Data Type Not Recognized",
+	"DSI VC ID Invalid",
+	"Invalid Transmission Length",
+	"Reserved",
+	"DSI Protocol Violation",
+};
+
+static ssize_t tegra_dsi_read_response(struct dsi_misc_reg *misc,
+				       const struct mipi_dsi_msg *msg,
+				       size_t count)
+{
+	u8 *rx = msg->rx_buf;
+	unsigned int i, j, k;
+	size_t size = 0;
+	u16 errors;
+	u32 value;
+
+	/* read and parse packet header */
+	value = readl(&misc->dsi_rd_data);
+
+	switch (value & 0x3f) {
+	case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
+		errors = (value >> 8) & 0xffff;
+		printf("%s: Acknowledge and error report: %04x\n",
+		       __func__, errors);
+		for (i = 0; i < ARRAY_SIZE(error_report); i++)
+			if (errors & BIT(i))
+				printf("%s:  %2u: %s\n", __func__, i,
+				       error_report[i]);
+		break;
+
+	case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
+		rx[0] = (value >> 8) & 0xff;
+		size = 1;
+		break;
+
+	case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
+		rx[0] = (value >>  8) & 0xff;
+		rx[1] = (value >> 16) & 0xff;
+		size = 2;
+		break;
+
+	case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE:
+		size = ((value >> 8) & 0xff00) | ((value >> 8) & 0xff);
+		break;
+
+	case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE:
+		size = ((value >> 8) & 0xff00) | ((value >> 8) & 0xff);
+		break;
+
+	default:
+		printf("%s: unhandled response type: %02x\n",
+		       __func__, value & 0x3f);
+		return -EPROTO;
+	}
+
+	size = min(size, msg->rx_len);
+
+	if (msg->rx_buf && size > 0) {
+		for (i = 0, j = 0; i < count - 1; i++, j += 4) {
+			u8 *rx = msg->rx_buf + j;
+
+			value = readl(&misc->dsi_rd_data);
+
+			for (k = 0; k < 4 && (j + k) < msg->rx_len; k++)
+				rx[j + k] = (value >> (k << 3)) & 0xff;
+		}
+	}
+
+	return size;
+}
+
+static int tegra_dsi_transmit(struct dsi_misc_reg *misc,
+			      unsigned long timeout)
+{
+	writel(DSI_TRIGGER_HOST, &misc->dsi_trigger);
+
+	while (timeout--) {
+		u32 value = readl(&misc->dsi_trigger);
+
+		if ((value & DSI_TRIGGER_HOST) == 0)
+			return 0;
+
+		udelay(1000);
+	}
+
+	debug("timeout waiting for transmission to complete\n");
+	return -ETIMEDOUT;
+}
+
+static int tegra_dsi_wait_for_response(struct dsi_misc_reg *misc,
+				       unsigned long timeout)
+{
+	while (timeout--) {
+		u32 value = readl(&misc->dsi_status);
+		u8 count = value & 0x1f;
+
+		if (count > 0)
+			return count;
+
+		udelay(1000);
+	}
+
+	debug("peripheral returned no data\n");
+	return -ETIMEDOUT;
+}
+
+static void tegra_dsi_writesl(struct dsi_misc_reg *misc,
+			      const void *buffer, size_t size)
+{
+	const u8 *buf = buffer;
+	size_t i, j;
+	u32 value;
+
+	for (j = 0; j < size; j += 4) {
+		value = 0;
+
+		for (i = 0; i < 4 && j + i < size; i++)
+			value |= buf[j + i] << (i << 3);
+
+		writel(value, &misc->dsi_wr_data);
+	}
+}
+
+static ssize_t tegra_dsi_host_transfer(struct mipi_dsi_host *host,
+				       const struct mipi_dsi_msg *msg)
+{
+	struct udevice *dev = (struct udevice *)host->dev;
+	struct tegra_dsi_priv *priv = dev_get_priv(dev);
+	struct dsi_misc_reg *misc = &priv->dsi->misc;
+	struct mipi_dsi_packet packet;
+	const u8 *header;
+	size_t count;
+	ssize_t err;
+	u32 value;
+
+	err = mipi_dsi_create_packet(&packet, msg);
+	if (err < 0)
+		return err;
+
+	header = packet.header;
+
+	/* maximum FIFO depth is 1920 words */
+	if (packet.size > priv->video_fifo_depth * 4)
+		return -ENOSPC;
+
+	/* reset underflow/overflow flags */
+	value = readl(&misc->dsi_status);
+	if (value & (DSI_STATUS_UNDERFLOW | DSI_STATUS_OVERFLOW)) {
+		value = DSI_HOST_CONTROL_FIFO_RESET;
+		writel(value, &misc->host_dsi_ctrl);
+		udelay(10);
+	}
+
+	value = readl(&misc->dsi_pwr_ctrl);
+	value |= DSI_POWER_CONTROL_ENABLE;
+	writel(value, &misc->dsi_pwr_ctrl);
+
+	mdelay(5);
+
+	value = DSI_HOST_CONTROL_CRC_RESET | DSI_HOST_CONTROL_TX_TRIG_HOST |
+		DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC;
+
+	/*
+	 * The host FIFO has a maximum of 64 words, so larger transmissions
+	 * need to use the video FIFO.
+	 */
+	if (packet.size > priv->host_fifo_depth * 4)
+		value |= DSI_HOST_CONTROL_FIFO_SEL;
+
+	writel(value, &misc->host_dsi_ctrl);
+
+	/*
+	 * For reads and messages with explicitly requested ACK, generate a
+	 * BTA sequence after the transmission of the packet.
+	 */
+	if ((msg->flags & MIPI_DSI_MSG_REQ_ACK) ||
+	    (msg->rx_buf && msg->rx_len > 0)) {
+		value = readl(&misc->host_dsi_ctrl);
+		value |= DSI_HOST_CONTROL_PKT_BTA;
+		writel(value, &misc->host_dsi_ctrl);
+	}
+
+	value = DSI_CONTROL_LANES(0) | DSI_CONTROL_HOST_ENABLE;
+	writel(value, &misc->dsi_ctrl);
+
+	/* write packet header, ECC is generated by hardware */
+	value = header[2] << 16 | header[1] << 8 | header[0];
+	writel(value, &misc->dsi_wr_data);
+
+	/* write payload (if any) */
+	if (packet.payload_length > 0)
+		tegra_dsi_writesl(misc, packet.payload,
+				  packet.payload_length);
+
+	err = tegra_dsi_transmit(misc, 250);
+	if (err < 0)
+		return err;
+
+	if ((msg->flags & MIPI_DSI_MSG_REQ_ACK) ||
+	    (msg->rx_buf && msg->rx_len > 0)) {
+		err = tegra_dsi_wait_for_response(misc, 250);
+		if (err < 0)
+			return err;
+
+		count = err;
+
+		value = readl(&misc->dsi_rd_data);
+		switch (value) {
+		case 0x84:
+			debug("%s: ACK\n", __func__);
+			break;
+
+		case 0x87:
+			debug("%s: ESCAPE\n", __func__);
+			break;
+
+		default:
+			printf("%s: unknown status: %08x\n", __func__, value);
+			break;
+		}
+
+		if (count > 1) {
+			err = tegra_dsi_read_response(misc, msg, count);
+			if (err < 0) {
+				printf("%s: failed to parse response: %zd\n",
+				       __func__, err);
+			} else {
+				/*
+				 * For read commands, return the number of
+				 * bytes returned by the peripheral.
+				 */
+				count = err;
+			}
+		}
+	} else {
+		/*
+		 * For write commands, we have transmitted the 4-byte header
+		 * plus the variable-length payload.
+		 */
+		count = 4 + packet.payload_length;
+	}
+
+	return count;
+}
+
+struct mipi_dsi_host_ops tegra_dsi_bridge_host_ops = {
+	.transfer	= tegra_dsi_host_transfer,
+};
+
+#define PKT_ID0(id)	((((id) & 0x3f) <<  3) | (1 <<  9))
+#define PKT_LEN0(len)	(((len) & 0x07) <<  0)
+#define PKT_ID1(id)	((((id) & 0x3f) << 13) | (1 << 19))
+#define PKT_LEN1(len)	(((len) & 0x07) << 10)
+#define PKT_ID2(id)	((((id) & 0x3f) << 23) | (1 << 29))
+#define PKT_LEN2(len)	(((len) & 0x07) << 20)
+
+#define PKT_LP		BIT(30)
+#define NUM_PKT_SEQ	12
+
+/*
+ * non-burst mode with sync pulses
+ */
+static const u32 pkt_seq_video_non_burst_sync_pulses[NUM_PKT_SEQ] = {
+	[ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) |
+	       PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
+	       PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) |
+	       PKT_LP,
+	[ 1] = 0,
+	[ 2] = PKT_ID0(MIPI_DSI_V_SYNC_END) | PKT_LEN0(0) |
+	       PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
+	       PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) |
+	       PKT_LP,
+	[ 3] = 0,
+	[ 4] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+	       PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
+	       PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) |
+	       PKT_LP,
+	[ 5] = 0,
+	[ 6] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+	       PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
+	       PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0),
+	[ 7] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(2) |
+	       PKT_ID1(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN1(3) |
+	       PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4),
+	[ 8] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+	       PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
+	       PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0) |
+	       PKT_LP,
+	[ 9] = 0,
+	[10] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+	       PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(1) |
+	       PKT_ID2(MIPI_DSI_H_SYNC_END) | PKT_LEN2(0),
+	[11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(2) |
+	       PKT_ID1(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN1(3) |
+	       PKT_ID2(MIPI_DSI_BLANKING_PACKET) | PKT_LEN2(4),
+};
+
+/*
+ * non-burst mode with sync events
+ */
+static const u32 pkt_seq_video_non_burst_sync_events[NUM_PKT_SEQ] = {
+	[ 0] = PKT_ID0(MIPI_DSI_V_SYNC_START) | PKT_LEN0(0) |
+	       PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
+	       PKT_LP,
+	[ 1] = 0,
+	[ 2] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+	       PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
+	       PKT_LP,
+	[ 3] = 0,
+	[ 4] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+	       PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
+	       PKT_LP,
+	[ 5] = 0,
+	[ 6] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+	       PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) |
+	       PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3),
+	[ 7] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4),
+	[ 8] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+	       PKT_ID1(MIPI_DSI_END_OF_TRANSMISSION) | PKT_LEN1(7) |
+	       PKT_LP,
+	[ 9] = 0,
+	[10] = PKT_ID0(MIPI_DSI_H_SYNC_START) | PKT_LEN0(0) |
+	       PKT_ID1(MIPI_DSI_BLANKING_PACKET) | PKT_LEN1(2) |
+	       PKT_ID2(MIPI_DSI_PACKED_PIXEL_STREAM_24) | PKT_LEN2(3),
+	[11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4),
+};
+
+static const u32 pkt_seq_command_mode[NUM_PKT_SEQ] = {
+	[ 0] = 0,
+	[ 1] = 0,
+	[ 2] = 0,
+	[ 3] = 0,
+	[ 4] = 0,
+	[ 5] = 0,
+	[ 6] = PKT_ID0(MIPI_DSI_DCS_LONG_WRITE) | PKT_LEN0(3) | PKT_LP,
+	[ 7] = 0,
+	[ 8] = 0,
+	[ 9] = 0,
+	[10] = PKT_ID0(MIPI_DSI_DCS_LONG_WRITE) | PKT_LEN0(5) | PKT_LP,
+	[11] = 0,
+};
+
+static void tegra_dsi_get_muldiv(enum mipi_dsi_pixel_format format,
+				 unsigned int *mulp, unsigned int *divp)
+{
+	switch (format) {
+	case MIPI_DSI_FMT_RGB666_PACKED:
+	case MIPI_DSI_FMT_RGB888:
+		*mulp = 3;
+		*divp = 1;
+		break;
+
+	case MIPI_DSI_FMT_RGB565:
+		*mulp = 2;
+		*divp = 1;
+		break;
+
+	case MIPI_DSI_FMT_RGB666:
+		*mulp = 9;
+		*divp = 4;
+		break;
+
+	default:
+		break;
+	}
+}
+
+static int tegra_dsi_get_format(enum mipi_dsi_pixel_format format,
+				enum tegra_dsi_format *fmt)
+{
+	switch (format) {
+	case MIPI_DSI_FMT_RGB888:
+		*fmt = TEGRA_DSI_FORMAT_24P;
+		break;
+
+	case MIPI_DSI_FMT_RGB666:
+		*fmt = TEGRA_DSI_FORMAT_18NP;
+		break;
+
+	case MIPI_DSI_FMT_RGB666_PACKED:
+		*fmt = TEGRA_DSI_FORMAT_18P;
+		break;
+
+	case MIPI_DSI_FMT_RGB565:
+		*fmt = TEGRA_DSI_FORMAT_16P;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void tegra_dsi_pad_calibrate(struct dsi_pad_ctrl_reg *pad)
+{
+	u32 value;
+
+	/* start calibration */
+	value = DSI_PAD_CONTROL_PAD_LPUPADJ(0x1) |
+		DSI_PAD_CONTROL_PAD_LPDNADJ(0x1) |
+		DSI_PAD_CONTROL_PAD_PREEMP_EN(0x1) |
+		DSI_PAD_CONTROL_PAD_SLEWDNADJ(0x6) |
+		DSI_PAD_CONTROL_PAD_SLEWUPADJ(0x6) |
+		DSI_PAD_CONTROL_PAD_PDIO(0) |
+		DSI_PAD_CONTROL_PAD_PDIO_CLK(0) |
+		DSI_PAD_CONTROL_PAD_PULLDN_ENAB(0);
+	writel(value, &pad->pad_ctrl);
+
+	clock_enable(PERIPH_ID_VI);
+	clock_enable(PERIPH_ID_CSI);
+	udelay(2);
+	reset_set_enable(PERIPH_ID_VI, 0);
+	reset_set_enable(PERIPH_ID_CSI, 0);
+
+	value = MIPI_CAL_TERMOSA(0x4);
+	writel(value, TEGRA_VI_BASE + (CSI_CILA_MIPI_CAL_CONFIG_0 << 2));
+
+	value = MIPI_CAL_TERMOSB(0x4);
+	writel(value, TEGRA_VI_BASE + (CSI_CILB_MIPI_CAL_CONFIG_0 << 2));
+
+	value = MIPI_CAL_HSPUOSD(0x3) | MIPI_CAL_HSPDOSD(0x4);
+	writel(value, TEGRA_VI_BASE + (CSI_DSI_MIPI_CAL_CONFIG << 2));
+
+	value = PAD_DRIV_DN_REF(0x5) | PAD_DRIV_UP_REF(0x7);
+	writel(value, TEGRA_VI_BASE + (CSI_MIPIBIAS_PAD_CONFIG << 2));
+
+	value = PAD_CIL_PDVREG(0x0);
+	writel(value, TEGRA_VI_BASE + (CSI_CIL_PAD_CONFIG << 2));
+}
+
+static void tegra_dsi_set_timeout(struct dsi_timeout_reg *rtimeout,
+				  unsigned long bclk,
+				  unsigned int vrefresh)
+{
+	unsigned int timeout;
+	u32 value;
+
+	/* one frame high-speed transmission timeout */
+	timeout = (bclk / vrefresh) / 512;
+	value = DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(timeout);
+	writel(value, &rtimeout->dsi_timeout_0);
+
+	/* 2 ms peripheral timeout for panel */
+	timeout = 2 * bclk / 512 * 1000;
+	value = DSI_TIMEOUT_PR(timeout) | DSI_TIMEOUT_TA(0x2000);
+	writel(value, &rtimeout->dsi_timeout_1);
+
+	value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0);
+	writel(value, &rtimeout->dsi_to_tally);
+}
+
+static void tegra_dsi_set_phy_timing(struct dsi_timing_reg *ptiming,
+				     unsigned long period,
+				     const struct mipi_dphy_timing *dphy_timing)
+{
+	u32 value;
+
+	value = DSI_TIMING_FIELD(dphy_timing->hsexit, period, 1) << 24 |
+		DSI_TIMING_FIELD(dphy_timing->hstrail, period, 0) << 16 |
+		DSI_TIMING_FIELD(dphy_timing->hszero, period, 3) << 8 |
+		DSI_TIMING_FIELD(dphy_timing->hsprepare, period, 1);
+	writel(value, &ptiming->dsi_phy_timing_0);
+
+	value = DSI_TIMING_FIELD(dphy_timing->clktrail, period, 1) << 24 |
+		DSI_TIMING_FIELD(dphy_timing->clkpost, period, 1) << 16 |
+		DSI_TIMING_FIELD(dphy_timing->clkzero, period, 1) << 8 |
+		DSI_TIMING_FIELD(dphy_timing->lpx, period, 1);
+	writel(value, &ptiming->dsi_phy_timing_1);
+
+	value = DSI_TIMING_FIELD(dphy_timing->clkprepare, period, 1) << 16 |
+		DSI_TIMING_FIELD(dphy_timing->clkpre, period, 1) << 8 |
+		DSI_TIMING_FIELD(0xff * period, period, 0) << 0;
+	writel(value, &ptiming->dsi_phy_timing_2);
+
+	value = DSI_TIMING_FIELD(dphy_timing->taget, period, 1) << 16 |
+		DSI_TIMING_FIELD(dphy_timing->tasure, period, 1) << 8 |
+		DSI_TIMING_FIELD(dphy_timing->tago, period, 1);
+	writel(value, &ptiming->dsi_bta_timing);
+}
+
+static void tegra_dsi_configure(struct udevice *dev,
+				unsigned long mode_flags)
+{
+	struct tegra_dsi_priv *priv = dev_get_priv(dev);
+	struct mipi_dsi_device *device = &priv->device;
+	struct display_timing *timing = &priv->timing;
+
+	struct dsi_misc_reg *misc = &priv->dsi->misc;
+	struct dsi_pkt_seq_reg *pkt = &priv->dsi->pkt;
+	struct dsi_pkt_len_reg *len = &priv->dsi->len;
+
+	unsigned int hact, hsw, hbp, hfp, i, mul, div;
+	const u32 *pkt_seq;
+	u32 value;
+
+	tegra_dsi_get_muldiv(device->format, &mul, &div);
+
+	if (mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
+		printf("[DSI] Non-burst video mode with sync pulses\n");
+		pkt_seq = pkt_seq_video_non_burst_sync_pulses;
+	} else if (mode_flags & MIPI_DSI_MODE_VIDEO) {
+		printf("[DSI] Non-burst video mode with sync events\n");
+		pkt_seq = pkt_seq_video_non_burst_sync_events;
+	} else {
+		printf("[DSI] Command mode\n");
+		pkt_seq = pkt_seq_command_mode;
+	}
+
+	value = DSI_CONTROL_CHANNEL(0) |
+		DSI_CONTROL_FORMAT(priv->format) |
+		DSI_CONTROL_LANES(device->lanes - 1) |
+		DSI_CONTROL_SOURCE(0);
+	writel(value, &misc->dsi_ctrl);
+
+	writel(priv->video_fifo_depth, &misc->dsi_max_threshold);
+
+	value = DSI_HOST_CONTROL_HS;
+	writel(value, &misc->host_dsi_ctrl);
+
+	value = readl(&misc->dsi_ctrl);
+
+	if (mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)
+		value |= DSI_CONTROL_HS_CLK_CTRL;
+
+	value &= ~DSI_CONTROL_TX_TRIG(3);
+
+	/* enable DCS commands for command mode */
+	if (mode_flags & MIPI_DSI_MODE_VIDEO)
+		value &= ~DSI_CONTROL_DCS_ENABLE;
+	else
+		value |= DSI_CONTROL_DCS_ENABLE;
+
+	value |= DSI_CONTROL_VIDEO_ENABLE;
+	value &= ~DSI_CONTROL_HOST_ENABLE;
+	writel(value, &misc->dsi_ctrl);
+
+	for (i = 0; i < NUM_PKT_SEQ; i++)
+		writel(pkt_seq[i], &pkt->dsi_pkt_seq_0_lo + i);
+
+	if (mode_flags & MIPI_DSI_MODE_VIDEO) {
+		/* horizontal active pixels */
+		hact = timing->hactive.typ * mul / div;
+
+		/* horizontal sync width */
+		hsw = timing->hsync_len.typ * mul / div;
+
+		/* horizontal back porch */
+		hbp = timing->hback_porch.typ * mul / div;
+
+		if ((mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) == 0)
+			hbp += hsw;
+
+		/* horizontal front porch */
+		hfp = timing->hfront_porch.typ * mul / div;
+
+		/* subtract packet overhead */
+		hsw -= 10;
+		hbp -= 14;
+		hfp -= 8;
+
+		writel(hsw << 16 | 0, &len->dsi_pkt_len_0_1);
+		writel(hact << 16 | hbp, &len->dsi_pkt_len_2_3);
+		writel(hfp, &len->dsi_pkt_len_4_5);
+		writel(0x0f0f << 16, &len->dsi_pkt_len_6_7);
+	} else {
+		/* 1 byte (DCS command) + pixel data */
+		value = 1 + timing->hactive.typ * mul / div;
+
+		writel(0, &len->dsi_pkt_len_0_1);
+		writel(value << 16, &len->dsi_pkt_len_2_3);
+		writel(value << 16, &len->dsi_pkt_len_4_5);
+		writel(0, &len->dsi_pkt_len_6_7);
+
+		value = MIPI_DCS_WRITE_MEMORY_START << 8 |
+			MIPI_DCS_WRITE_MEMORY_CONTINUE;
+		writel(value, &len->dsi_dcs_cmds);
+	}
+
+	/* set SOL delay (for non-burst mode only) */
+	writel(8 * mul / div, &misc->dsi_sol_delay);
+}
+
+static int tegra_dsi_encoder_enable(struct udevice *dev)
+{
+	struct tegra_dsi_priv *priv = dev_get_priv(dev);
+	struct mipi_dsi_device *device = &priv->device;
+	struct display_timing *timing = &priv->timing;
+	struct dsi_misc_reg *misc = &priv->dsi->misc;
+	unsigned int mul, div;
+	unsigned long bclk, plld, period;
+	u32 value;
+	int ret;
+
+	/* Disable interrupt */
+	writel(0, &misc->int_enable);
+
+	tegra_dsi_pad_calibrate(&priv->dsi->pad);
+
+	tegra_dsi_get_muldiv(device->format, &mul, &div);
+
+	/* compute byte clock */
+	bclk = (timing->pixelclock.typ * mul) / (div * device->lanes);
+
+	tegra_dsi_set_timeout(&priv->dsi->timeout, bclk, 60);
+
+	/*
+	 * Compute bit clock and round up to the next MHz.
+	 */
+	plld = DIV_ROUND_UP(bclk * 8, USEC_PER_SEC) * USEC_PER_SEC;
+	period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, plld);
+
+	ret = mipi_dphy_timing_get_default(&priv->dphy_timing, period);
+	if (ret < 0) {
+		printf("%s: failed to get D-PHY timing: %d\n", __func__, ret);
+		return ret;
+	}
+
+	ret = mipi_dphy_timing_validate(&priv->dphy_timing, period);
+	if (ret < 0) {
+		printf("%s: failed to validate D-PHY timing: %d\n", __func__, ret);
+		return ret;
+	}
+
+	/*
+	 * The D-PHY timing fields are expressed in byte-clock cycles, so
+	 * multiply the period by 8.
+	 */
+	tegra_dsi_set_phy_timing(&priv->dsi->ptiming,
+				 period * 8, &priv->dphy_timing);
+
+	/* Perform panel HW setup */
+	ret = panel_enable_backlight(priv->panel);
+	if (ret)
+		return ret;
+
+	tegra_dsi_configure(dev, 0);
+
+	ret = panel_set_backlight(priv->panel, BACKLIGHT_DEFAULT);
+	if (ret)
+		return ret;
+
+	tegra_dsi_configure(dev, device->mode_flags);
+
+	tegra_dc_enable_controller(dev);
+
+	/* enable DSI controller */
+	value = readl(&misc->dsi_pwr_ctrl);
+	value |= DSI_POWER_CONTROL_ENABLE;
+	writel(value, &misc->dsi_pwr_ctrl);
+
+	return 0;
+}
+
+static int tegra_dsi_bridge_set_panel(struct udevice *dev, int percent)
+{
+	/* Is not used in tegra dc */
+	return 0;
+}
+
+static int tegra_dsi_panel_timings(struct udevice *dev,
+				   struct display_timing *timing)
+{
+	struct tegra_dsi_priv *priv = dev_get_priv(dev);
+
+	memcpy(timing, &priv->timing, sizeof(*timing));
+
+	return 0;
+}
+
+static void tegra_dsi_init_clocks(struct udevice *dev)
+{
+	struct tegra_dsi_priv *priv = dev_get_priv(dev);
+	struct mipi_dsi_device *device = &priv->device;
+	unsigned int mul, div;
+	unsigned long bclk, plld;
+
+	tegra_dsi_get_muldiv(device->format, &mul, &div);
+
+	bclk = (priv->timing.pixelclock.typ * mul) /
+					(div * device->lanes);
+
+	plld = DIV_ROUND_UP(bclk * 8, USEC_PER_SEC);
+
+	switch (clock_get_osc_freq()) {
+	case CLOCK_OSC_FREQ_12_0: /* OSC is 12Mhz */
+	case CLOCK_OSC_FREQ_48_0: /* OSC is 48Mhz */
+		clock_set_rate(CLOCK_ID_DISPLAY, plld, 12, 0, 8);
+		break;
+
+	case CLOCK_OSC_FREQ_26_0: /* OSC is 26Mhz */
+		clock_set_rate(CLOCK_ID_DISPLAY, plld, 26, 0, 8);
+		break;
+
+	case CLOCK_OSC_FREQ_13_0: /* OSC is 13Mhz */
+	case CLOCK_OSC_FREQ_16_8: /* OSC is 16.8Mhz */
+		clock_set_rate(CLOCK_ID_DISPLAY, plld, 13, 0, 8);
+		break;
+
+	case CLOCK_OSC_FREQ_19_2:
+	case CLOCK_OSC_FREQ_38_4:
+	default:
+		/*
+		 * These are not supported.
+		 */
+		break;
+	}
+
+	priv->dsi_clk = clock_decode_periph_id(dev);
+
+	clock_enable(priv->dsi_clk);
+	udelay(2);
+	reset_set_enable(priv->dsi_clk, 0);
+}
+
+static int tegra_dsi_bridge_probe(struct udevice *dev)
+{
+	struct tegra_dsi_priv *priv = dev_get_priv(dev);
+	struct mipi_dsi_device *device = &priv->device;
+	struct mipi_dsi_panel_plat *mipi_plat;
+	int ret;
+
+	priv->dsi = (struct dsi_ctlr *)dev_read_addr_ptr(dev);
+	if (!priv->dsi) {
+		printf("%s: No display controller address\n", __func__);
+		return -EINVAL;
+	}
+
+	priv->video_fifo_depth = 480;
+	priv->host_fifo_depth = 64;
+
+	ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
+					   "avdd-dsi-csi-supply", &priv->avdd);
+	if (ret)
+		debug("%s: Cannot get avdd-dsi-csi-supply: error %d\n",
+		      __func__, ret);
+
+	ret = uclass_get_device_by_phandle(UCLASS_PANEL, dev,
+					   "panel", &priv->panel);
+	if (ret) {
+		printf("%s: Cannot get panel: error %d\n", __func__, ret);
+		return log_ret(ret);
+	}
+
+	panel_get_display_timing(priv->panel, &priv->timing);
+
+	mipi_plat = dev_get_plat(priv->panel);
+	mipi_plat->device = device;
+
+	priv->host.dev = (struct device *)dev;
+	priv->host.ops = &tegra_dsi_bridge_host_ops;
+
+	device->host = &priv->host;
+	device->lanes = mipi_plat->lanes;
+	device->format = mipi_plat->format;
+	device->mode_flags = mipi_plat->mode_flags;
+
+	tegra_dsi_get_format(device->format, &priv->format);
+
+	if (priv->avdd) {
+		ret = regulator_set_enable(priv->avdd, true);
+		if (ret)
+			return ret;
+	}
+
+	tegra_dsi_init_clocks(dev);
+
+	return 0;
+}
+
+static const struct panel_ops tegra_dsi_bridge_ops = {
+	.enable_backlight	= tegra_dsi_encoder_enable,
+	.set_backlight		= tegra_dsi_bridge_set_panel,
+	.get_display_timing	= tegra_dsi_panel_timings,
+};
+
+static const struct udevice_id tegra_dsi_bridge_ids[] = {
+	{ .compatible = "nvidia,tegra30-dsi" },
+	{ }
+};
+
+U_BOOT_DRIVER(tegra_dsi) = {
+	.name		= "tegra_dsi",
+	.id		= UCLASS_PANEL,
+	.of_match	= tegra_dsi_bridge_ids,
+	.ops		= &tegra_dsi_bridge_ops,
+	.probe		= tegra_dsi_bridge_probe,
+	.plat_auto	= sizeof(struct tegra_dc_plat),
+	.priv_auto	= sizeof(struct tegra_dsi_priv),
+};
-- 
2.37.2


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

* [PATCH v3 10/11] simple_panel: add support for get_display_timing
  2023-03-27  8:11 [PATCH v3 00/11] Tegra DC improvements Svyatoslav Ryhel
                   ` (8 preceding siblings ...)
  2023-03-27  8:11 ` [PATCH v3 09/11] video: tegra20: add DSI controller driver Svyatoslav Ryhel
@ 2023-03-27  8:11 ` Svyatoslav Ryhel
  2023-03-27  8:11 ` [PATCH v3 11/11] simple_panel: support simple MIPI DSI panels Svyatoslav Ryhel
  2023-04-08 19:51 ` [PATCH v3 00/11] Tegra DC improvements Anatolij Gustschin
  11 siblings, 0 replies; 16+ messages in thread
From: Svyatoslav Ryhel @ 2023-03-27  8:11 UTC (permalink / raw)
  To: Tom Warren, Anatolij Gustschin, Simon Glass, Svyatoslav Ryhel,
	Marcel Ziswiler, Thierry Reding, Maxim Schwalm, Dmitry Osipenko,
	Nicolas Chauvet
  Cc: u-boot

Some cases may require passing display timings from
panel driver. To handle such cases support parsing
device tree panel node for timing subnode.

Tested-by: Robert Eckelmann <longnoserob@gmail.com> # ASUS TF101 T20
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # Paz00
Tested-by: Svyatoslav Ryhel <clamor95@gmail.com> # Google Nexus 7 2012
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/video/simple_panel.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/video/simple_panel.c b/drivers/video/simple_panel.c
index 91c91ee75d..5a8262669a 100644
--- a/drivers/video/simple_panel.c
+++ b/drivers/video/simple_panel.c
@@ -48,6 +48,15 @@ static int simple_panel_set_backlight(struct udevice *dev, int percent)
 	return 0;
 }
 
+static int simple_panel_get_display_timing(struct udevice *dev,
+					   struct display_timing *timings)
+{
+	const void *blob = gd->fdt_blob;
+
+	return fdtdec_decode_display_timing(blob, dev_of_offset(dev),
+					    0, timings);
+}
+
 static int simple_panel_of_to_plat(struct udevice *dev)
 {
 	struct simple_panel_priv *priv = dev_get_priv(dev);
@@ -102,6 +111,7 @@ static int simple_panel_probe(struct udevice *dev)
 static const struct panel_ops simple_panel_ops = {
 	.enable_backlight	= simple_panel_enable_backlight,
 	.set_backlight		= simple_panel_set_backlight,
+	.get_display_timing	= simple_panel_get_display_timing,
 };
 
 static const struct udevice_id simple_panel_ids[] = {
-- 
2.37.2


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

* [PATCH v3 11/11] simple_panel: support simple MIPI DSI panels
  2023-03-27  8:11 [PATCH v3 00/11] Tegra DC improvements Svyatoslav Ryhel
                   ` (9 preceding siblings ...)
  2023-03-27  8:11 ` [PATCH v3 10/11] simple_panel: add support for get_display_timing Svyatoslav Ryhel
@ 2023-03-27  8:11 ` Svyatoslav Ryhel
  2023-04-08 19:51 ` [PATCH v3 00/11] Tegra DC improvements Anatolij Gustschin
  11 siblings, 0 replies; 16+ messages in thread
From: Svyatoslav Ryhel @ 2023-03-27  8:11 UTC (permalink / raw)
  To: Tom Warren, Anatolij Gustschin, Simon Glass, Svyatoslav Ryhel,
	Marcel Ziswiler, Thierry Reding, Maxim Schwalm, Dmitry Osipenko,
	Nicolas Chauvet
  Cc: u-boot

Re-use simple panel driver for MIPI DSI panels
which do not require additional DSI commands
for setup.

Tested-by: Robert Eckelmann <longnoserob@gmail.com> # ASUS TF101 T20
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # Paz00
Tested-by: Andreas Westman Dorcsak <hedmoo@yahoo.com> # ASUS TF700T T30
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/video/simple_panel.c | 37 ++++++++++++++++++++++++++++++++----
 1 file changed, 33 insertions(+), 4 deletions(-)

diff --git a/drivers/video/simple_panel.c b/drivers/video/simple_panel.c
index 5a8262669a..81fcafb9f5 100644
--- a/drivers/video/simple_panel.c
+++ b/drivers/video/simple_panel.c
@@ -8,6 +8,7 @@
 #include <backlight.h>
 #include <dm.h>
 #include <log.h>
+#include <mipi_dsi.h>
 #include <panel.h>
 #include <asm/gpio.h>
 #include <power/regulator.h>
@@ -18,6 +19,19 @@ struct simple_panel_priv {
 	struct gpio_desc enable;
 };
 
+/* List of supported DSI panels */
+enum {
+	PANEL_NON_DSI,
+	PANASONIC_VVX10F004B00,
+};
+
+static const struct mipi_dsi_panel_plat panasonic_vvx10f004b00 = {
+	.mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+		      MIPI_DSI_CLOCK_NON_CONTINUOUS,
+	.format = MIPI_DSI_FMT_RGB888,
+	.lanes = 4,
+};
+
 static int simple_panel_enable_backlight(struct udevice *dev)
 {
 	struct simple_panel_priv *priv = dev_get_priv(dev);
@@ -96,6 +110,8 @@ static int simple_panel_of_to_plat(struct udevice *dev)
 static int simple_panel_probe(struct udevice *dev)
 {
 	struct simple_panel_priv *priv = dev_get_priv(dev);
+	struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
+	const u32 dsi_data = dev_get_driver_data(dev);
 	int ret;
 
 	if (IS_ENABLED(CONFIG_DM_REGULATOR) && priv->reg) {
@@ -105,6 +121,16 @@ static int simple_panel_probe(struct udevice *dev)
 			return ret;
 	}
 
+	switch (dsi_data) {
+	case PANASONIC_VVX10F004B00:
+		memcpy(plat, &panasonic_vvx10f004b00,
+		       sizeof(panasonic_vvx10f004b00));
+		break;
+	case PANEL_NON_DSI:
+	default:
+		break;
+	}
+
 	return 0;
 }
 
@@ -123,15 +149,18 @@ static const struct udevice_id simple_panel_ids[] = {
 	{ .compatible = "lg,lb070wv8" },
 	{ .compatible = "sharp,lq123p1jx31" },
 	{ .compatible = "boe,nv101wxmn51" },
+	{ .compatible = "panasonic,vvx10f004b00",
+	  .data = PANASONIC_VVX10F004B00 },
 	{ }
 };
 
 U_BOOT_DRIVER(simple_panel) = {
-	.name	= "simple_panel",
-	.id	= UCLASS_PANEL,
-	.of_match = simple_panel_ids,
-	.ops	= &simple_panel_ops,
+	.name		= "simple_panel",
+	.id		= UCLASS_PANEL,
+	.of_match	= simple_panel_ids,
+	.ops		= &simple_panel_ops,
 	.of_to_plat	= simple_panel_of_to_plat,
 	.probe		= simple_panel_probe,
 	.priv_auto	= sizeof(struct simple_panel_priv),
+	.plat_auto	= sizeof(struct mipi_dsi_panel_plat),
 };
-- 
2.37.2


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

* Re: [PATCH v3 00/11] Tegra DC improvements
  2023-03-27  8:11 [PATCH v3 00/11] Tegra DC improvements Svyatoslav Ryhel
                   ` (10 preceding siblings ...)
  2023-03-27  8:11 ` [PATCH v3 11/11] simple_panel: support simple MIPI DSI panels Svyatoslav Ryhel
@ 2023-04-08 19:51 ` Anatolij Gustschin
  11 siblings, 0 replies; 16+ messages in thread
From: Anatolij Gustschin @ 2023-04-08 19:51 UTC (permalink / raw)
  To: Svyatoslav Ryhel
  Cc: Tom Warren, Simon Glass, Marcel Ziswiler, Thierry Reding,
	Maxim Schwalm, Dmitry Osipenko, Nicolas Chauvet, u-boot

On Mon, 27 Mar 2023 11:11:39 +0300
Svyatoslav Ryhel clamor95@gmail.com wrote:
...
> Changes from v2:
> - resend after month
> 
> Changes from v1:
> - DSI driver headers were optimized
> - Tested on Paz00 board
> 
> ---
> 
> Marcel Ziswiler (1):
>   tegra: lcd: video: integrate display driver for t30
> 
> Svyatoslav Ryhel (10):
>   video: move tegra dc driver into own folder
>   video: tegra-dc: get clocks from device tree
>   video: tegra-dc: request timings from panel driver first
>   video: tegra-dc: assign regmap directly
>   video: tegra-dc: add 180 degree panel rotation
>   video: tegra-dc: add panel_set_backlight call
>   video: tegra-dc: pass DC regmap to internal devices
>   video: tegra20: add DSI controller driver
>   simple_panel: add support for get_display_timing
>   simple_panel: support simple MIPI DSI panels
> 
>  arch/arm/dts/tegra30-u-boot.dtsi              |   9 +
>  arch/arm/include/asm/arch-tegra/dc.h          |   8 +
>  arch/arm/include/asm/arch-tegra30/display.h   |  28 +
>  arch/arm/include/asm/arch-tegra30/dsi.h       | 217 +++++
>  arch/arm/include/asm/arch-tegra30/pwm.h       |  13 +
>  drivers/video/Kconfig                         |  11 +-
>  drivers/video/Makefile                        |   2 +-
>  drivers/video/simple_panel.c                  |  47 +-
>  drivers/video/tegra20/Kconfig                 |  17 +
>  drivers/video/tegra20/Makefile                |   4 +
>  drivers/video/tegra20/mipi-phy.c              | 134 +++
>  drivers/video/tegra20/mipi-phy.h              |  48 +
>  drivers/video/{tegra.c => tegra20/tegra-dc.c} | 123 ++-
>  drivers/video/tegra20/tegra-dsi.c             | 864 ++++++++++++++++++
>  14 files changed, 1476 insertions(+), 49 deletions(-)
>  create mode 100644 arch/arm/include/asm/arch-tegra30/display.h
>  create mode 100644 arch/arm/include/asm/arch-tegra30/dsi.h
>  create mode 100644 arch/arm/include/asm/arch-tegra30/pwm.h
>  create mode 100644 drivers/video/tegra20/Kconfig
>  create mode 100644 drivers/video/tegra20/Makefile
>  create mode 100644 drivers/video/tegra20/mipi-phy.c
>  create mode 100644 drivers/video/tegra20/mipi-phy.h
>  rename drivers/video/{tegra.c => tegra20/tegra-dc.c} (82%)
>  create mode 100644 drivers/video/tegra20/tegra-dsi.c

Series applied to u-boot-video/master, thanks! When applying,
in patch 01/11 I changed "u-boot,dm-pre-reloc" to "bootph-all"
(in master the driver model tags use this new schema).

--
Anatolij

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

* Re: [PATCH v3 06/11] video: tegra-dc: add 180 degree panel rotation
  2023-03-27  8:11 ` [PATCH v3 06/11] video: tegra-dc: add 180 degree panel rotation Svyatoslav Ryhel
@ 2023-04-16 17:29   ` Dmitry Osipenko
  2023-04-16 17:34     ` Svyatoslav Ryhel
  2023-04-16 18:44     ` Svyatoslav Ryhel
  0 siblings, 2 replies; 16+ messages in thread
From: Dmitry Osipenko @ 2023-04-16 17:29 UTC (permalink / raw)
  To: Svyatoslav Ryhel, Tom Warren, Anatolij Gustschin, Simon Glass,
	Marcel Ziswiler, Thierry Reding, Maxim Schwalm, Nicolas Chauvet
  Cc: u-boot

27.03.2023 11:11, Svyatoslav Ryhel пишет:
> @@ -370,6 +383,8 @@ static int tegra_lcd_of_to_plat(struct udevice *dev)
>  		return -EINVAL;
>  	}
>  
> +	priv->rotation = dev_read_bool(dev, "nvidia,180-rotation");

There is no "nvidia,180-rotation" in the DT binding. It should be
standard panel property "rotation = <180>".


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

* Re: [PATCH v3 06/11] video: tegra-dc: add 180 degree panel rotation
  2023-04-16 17:29   ` Dmitry Osipenko
@ 2023-04-16 17:34     ` Svyatoslav Ryhel
  2023-04-16 18:44     ` Svyatoslav Ryhel
  1 sibling, 0 replies; 16+ messages in thread
From: Svyatoslav Ryhel @ 2023-04-16 17:34 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Tom Warren, Anatolij Gustschin, Simon Glass, Marcel Ziswiler,
	Thierry Reding, Maxim Schwalm, Nicolas Chauvet, u-boot

нд, 16 квіт. 2023 р. о 20:29 Dmitry Osipenko <digetx@gmail.com> пише:
>
> 27.03.2023 11:11, Svyatoslav Ryhel пишет:
> > @@ -370,6 +383,8 @@ static int tegra_lcd_of_to_plat(struct udevice *dev)
> >               return -EINVAL;
> >       }
> >
> > +     priv->rotation = dev_read_bool(dev, "nvidia,180-rotation");
>
> There is no "nvidia,180-rotation" in the DT binding. It should be
> standard panel property "rotation = <180>".
>

I will check if with recent patches u-boot can perform this. If not, this option
should stay till software panel rotation is supported.

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

* Re: [PATCH v3 06/11] video: tegra-dc: add 180 degree panel rotation
  2023-04-16 17:29   ` Dmitry Osipenko
  2023-04-16 17:34     ` Svyatoslav Ryhel
@ 2023-04-16 18:44     ` Svyatoslav Ryhel
  1 sibling, 0 replies; 16+ messages in thread
From: Svyatoslav Ryhel @ 2023-04-16 18:44 UTC (permalink / raw)
  To: Dmitry Osipenko
  Cc: Tom Warren, Anatolij Gustschin, Simon Glass, Marcel Ziswiler,
	Thierry Reding, Maxim Schwalm, Nicolas Chauvet, u-boot

нд, 16 квіт. 2023 р. о 20:29 Dmitry Osipenko <digetx@gmail.com> пише:
>
> 27.03.2023 11:11, Svyatoslav Ryhel пишет:
> > @@ -370,6 +383,8 @@ static int tegra_lcd_of_to_plat(struct udevice *dev)
> >               return -EINVAL;
> >       }
> >
> > +     priv->rotation = dev_read_bool(dev, "nvidia,180-rotation");
>
> There is no "nvidia,180-rotation" in the DT binding. It should be
> standard panel property "rotation = <180>".
>

With the current state of the u-boot I was not able to get the correct rotation
of the grouper panel. Since you have grouper, maybe you can propose better
solution.

Best regards
Svyatoslav R.

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

end of thread, other threads:[~2023-04-16 18:44 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-03-27  8:11 [PATCH v3 00/11] Tegra DC improvements Svyatoslav Ryhel
2023-03-27  8:11 ` [PATCH v3 01/11] tegra: lcd: video: integrate display driver for t30 Svyatoslav Ryhel
2023-03-27  8:11 ` [PATCH v3 02/11] video: move tegra dc driver into own folder Svyatoslav Ryhel
2023-03-27  8:11 ` [PATCH v3 03/11] video: tegra-dc: get clocks from device tree Svyatoslav Ryhel
2023-03-27  8:11 ` [PATCH v3 04/11] video: tegra-dc: request timings from panel driver first Svyatoslav Ryhel
2023-03-27  8:11 ` [PATCH v3 05/11] video: tegra-dc: assign regmap directly Svyatoslav Ryhel
2023-03-27  8:11 ` [PATCH v3 06/11] video: tegra-dc: add 180 degree panel rotation Svyatoslav Ryhel
2023-04-16 17:29   ` Dmitry Osipenko
2023-04-16 17:34     ` Svyatoslav Ryhel
2023-04-16 18:44     ` Svyatoslav Ryhel
2023-03-27  8:11 ` [PATCH v3 07/11] video: tegra-dc: add panel_set_backlight call Svyatoslav Ryhel
2023-03-27  8:11 ` [PATCH v3 08/11] video: tegra-dc: pass DC regmap to internal devices Svyatoslav Ryhel
2023-03-27  8:11 ` [PATCH v3 09/11] video: tegra20: add DSI controller driver Svyatoslav Ryhel
2023-03-27  8:11 ` [PATCH v3 10/11] simple_panel: add support for get_display_timing Svyatoslav Ryhel
2023-03-27  8:11 ` [PATCH v3 11/11] simple_panel: support simple MIPI DSI panels Svyatoslav Ryhel
2023-04-08 19:51 ` [PATCH v3 00/11] Tegra DC improvements Anatolij Gustschin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox