[PATCH 18/26] video: bridge: Add Samsung DSIM bridge

Dario Binacchi dario.binacchi at amarulasolutions.com
Fri Sep 13 11:56:00 CEST 2024


From: Michael Trimarchi <michael at amarulasolutions.com>

Samsung MIPI DSIM controller is common DSI IP that can be used in various
SoCs like Exynos, i.MX8M Mini/Nano.

In order to access this DSI controller between various platform SoCs,
the ideal way to incorporate this in the as a bridge that can support
all different platform.

Signed-off-by: Michael Trimarchi <michael at amarulasolutions.com>
Signed-off-by: Dario Binacchi <dario.binacchi at amarulasolutions.com>
---

 drivers/video/bridge/Kconfig            |   13 +
 drivers/video/bridge/Makefile           |    1 +
 drivers/video/bridge/samsung-dsi-host.c | 1567 +++++++++++++++++++++++
 drivers/video/bridge/samsung-dsim.c     |  148 +++
 drivers/video/bridge/samsung-dsim.h     |   20 +
 5 files changed, 1749 insertions(+)
 create mode 100644 drivers/video/bridge/samsung-dsi-host.c
 create mode 100644 drivers/video/bridge/samsung-dsim.c
 create mode 100644 drivers/video/bridge/samsung-dsim.h

diff --git a/drivers/video/bridge/Kconfig b/drivers/video/bridge/Kconfig
index ab9172737201..5cb1e7af9935 100644
--- a/drivers/video/bridge/Kconfig
+++ b/drivers/video/bridge/Kconfig
@@ -44,6 +44,19 @@ config VIDEO_BRIDGE_ANALOGIX_ANX6345
 	 The Analogix ANX6345 is RGB-to-DP converter. It enables an eDP LCD
 	 panel to be connected to an parallel LCD interface.
 
+config VIDEO_BRIDGE_SAMSUNG_DSIM
+	bool "Enable IMX SEC DSI video support"
+	select MIPI_DPHY_HELPERS
+	select VIDEO_BRIDGE
+	select VIDEO_LINK
+	select VIDEO_MIPI_DSI
+	help
+	  Enables the common driver code for the Samsung
+	  MIPI DSI block found in SoCs from various vendors.
+	  As this does not provide any functionality by itself (but
+	  rather requires a SoC-specific glue driver to call it), it
+	  can not be enabled from the configuration menu.
+
 config VIDEO_BRIDGE_SOLOMON_SSD2825
 	bool "Solomon SSD2825 bridge driver"
 	depends on PANEL && DM_GPIO
diff --git a/drivers/video/bridge/Makefile b/drivers/video/bridge/Makefile
index 58697e3cbe90..8f49013299ae 100644
--- a/drivers/video/bridge/Makefile
+++ b/drivers/video/bridge/Makefile
@@ -8,5 +8,6 @@ obj-$(CONFIG_VIDEO_BRIDGE_PARADE_DP501) += dp501.o
 obj-$(CONFIG_VIDEO_BRIDGE_PARADE_PS862X) += ps862x.o
 obj-$(CONFIG_VIDEO_BRIDGE_NXP_PTN3460) += ptn3460.o
 obj-$(CONFIG_VIDEO_BRIDGE_ANALOGIX_ANX6345) += anx6345.o
+obj-$(CONFIG_VIDEO_BRIDGE_SAMSUNG_DSIM) += samsung-dsim.o samsung-dsi-host.o
 obj-$(CONFIG_VIDEO_BRIDGE_SOLOMON_SSD2825) += ssd2825.o
 obj-$(CONFIG_VIDEO_BRIDGE_TOSHIBA_TC358768) += tc358768.o
diff --git a/drivers/video/bridge/samsung-dsi-host.c b/drivers/video/bridge/samsung-dsi-host.c
new file mode 100644
index 000000000000..dd3e33c4edc7
--- /dev/null
+++ b/drivers/video/bridge/samsung-dsi-host.c
@@ -0,0 +1,1567 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2024 Amarula Solutions
+ * Copyright 2018 NXP
+ *
+ */
+
+#define LOG_CATEGORY UCLASS_DSI_HOST
+
+#include <dm.h>
+#include <clk.h>
+#include <dm/device_compat.h>
+#include <asm/io.h>
+#include <log.h>
+#include <phy-mipi-dphy.h>
+#include <linux/err.h>
+#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/log2.h>
+#include <linux/math64.h>
+#include <asm/unaligned.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/imx-regs.h>
+#include <asm/arch/sys_proto.h>
+#include <div64.h>
+#include <video_bridge.h>
+#include <panel.h>
+#include <dsi_host.h>
+#include <asm/arch/gpio.h>
+#include <dm/device-internal.h>
+#include "samsung-dsim.h"
+
+#define MIPI_FIFO_TIMEOUT		250000 /* 250ms */
+
+#define DRIVER_NAME "samsung_dsi"
+
+/* reg bit manipulation */
+#define REG_MASK(e, s) (((1 << ((e) - (s) + 1)) - 1) << (s))
+#define REG_PUT(x, e, s) (((x) << (s)) & REG_MASK(e, s))
+#define REG_GET(x, e, s) (((x) & REG_MASK(e, s)) >> (s))
+
+#define RGB_STATUS_CMDMODE_INSEL	BIT(31)
+#define RGB_STATUS_GET_RGBSTATE(x)	REG_GET(x, 12,  0)
+
+#define CLKCTRL_DPHY_SEL_1P5G		(0x0 << 29)
+#define CLKCTRL_PLLBYPASS		BIT(29)
+#define CLKCTRL_BYTECLKSRC_DPHY_PLL	REG_PUT(0, 26, 25)
+#define CLKCTRL_SET_LANEESCCLKEN(x)	REG_PUT(x, 23, 19)
+#define CLKCTRL_SET_ESCPRESCALER(x)	REG_PUT(x, 15,  0)
+
+#define ESCMODE_SET_STOPSTATE_CNT(X)	REG_PUT(x, 31, 21)
+#define ESCMODE_FORCESTOPSTATE		BIT(20)
+#define ESCMODE_FORCEBTA		BIT(16)
+#define ESCMODE_CMDLPDT			BIT(7)
+#define ESCMODE_TXLPDT			BIT(6)
+#define ESCMODE_TXTRIGGERRST		BIT(5)
+
+#define MVPORCH_SET_CMDALLOW(x)		REG_PUT(x, 31, 28)
+#define MVPORCH_SET_STABLEVFP(x)	REG_PUT(x, 26, 16)
+#define MVPORCH_SET_MAINVBP(x)		REG_PUT(x, 10,  0)
+
+#define MHPORCH_SET_MAINHFP(x)		REG_PUT(x, 31, 16)
+#define MHPORCH_SET_MAINHBP(x)		REG_PUT(x, 15,  0)
+
+#define MSYNC_SET_MAINVSA(x)		REG_PUT(x, 31, 22)
+#define MSYNC_SET_MAINHSA(x)		REG_PUT(x, 15,  0)
+
+#define INTSRC_PLLSTABLE		BIT(31)
+#define INTSRC_SWRSTRELEASE		BIT(30)
+#define INTSRC_SFRPLFIFOEMPTY		BIT(29)
+#define INTSRC_SFRPHFIFOEMPTY		BIT(28)
+#define INTSRC_FRAMEDONE		BIT(24)
+#define INTSRC_LPDRTOUT			BIT(21)
+#define INTSRC_TATOUT			BIT(20)
+#define INTSRC_RXDATDONE		BIT(18)
+#define INTSRC_MASK			(INTSRC_PLLSTABLE	|	\
+					 INTSRC_SWRSTRELEASE	|	\
+					 INTSRC_SFRPLFIFOEMPTY	|	\
+					 INTSRC_SFRPHFIFOEMPTY	|	\
+					 INTSRC_FRAMEDONE	|	\
+					 INTSRC_LPDRTOUT	|	\
+					 INTSRC_TATOUT		|	\
+					 INTSRC_RXDATDONE)
+
+#define INTMSK_MSKPLLSTABLE		BIT(31)
+#define INTMSK_MSKSWRELEASE		BIT(30)
+#define INTMSK_MSKSFRPLFIFOEMPTY	BIT(29)
+#define INTMSK_MSKSFRPHFIFOEMPTY	BIT(28)
+#define INTMSK_MSKFRAMEDONE		BIT(24)
+#define INTMSK_MSKLPDRTOUT		BIT(21)
+#define INTMSK_MSKTATOUT		BIT(20)
+#define INTMSK_MSKRXDATDONE		BIT(18)
+
+#define PKTHDR_SET_DATA1(x)		REG_PUT(x, 23, 16)
+#define PKTHDR_GET_DATA1(x)		REG_GET(x, 23, 16)
+#define PKTHDR_SET_DATA0(x)		REG_PUT(x, 15,  8)
+#define PKTHDR_GET_DATA0(x)		REG_GET(x, 15,  8)
+#define PKTHDR_GET_WC(x)		REG_GET(x, 23,  8)
+#define PKTHDR_SET_DI(x)		REG_PUT(x,  7,  0)
+#define PKTHDR_GET_DI(x)		REG_GET(x,  7,  0)
+#define PKTHDR_SET_DT(x)		REG_PUT(x,  5,  0)
+#define PKTHDR_GET_DT(x)		REG_GET(x,  5,  0)
+#define PKTHDR_SET_VC(x)		REG_PUT(x,  7,  6)
+#define PKTHDR_GET_VC(x)		REG_GET(x,  7,  6)
+
+#define FIFOCTRL_FULLRX			BIT(25)
+#define FIFOCTRL_EMPTYRX		BIT(24)
+#define FIFOCTRL_FULLHSFR		BIT(23)
+#define FIFOCTRL_EMPTYHSFR		BIT(22)
+#define FIFOCTRL_FULLLSFR		BIT(21)
+#define FIFOCTRL_EMPTYLSFR		BIT(20)
+#define FIFOCTRL_FULLHMAIN		BIT(11)
+#define FIFOCTRL_EMPTYHMAIN		BIT(10)
+#define FIFOCTRL_FULLLMAIN		BIT(9)
+#define FIFOCTRL_EMPTYLMAIN		BIT(8)
+#define FIFOCTRL_NINITRX		BIT(4)
+#define FIFOCTRL_NINITSFR		BIT(3)
+#define FIFOCTRL_NINITI80		BIT(2)
+#define FIFOCTRL_NINITSUB		BIT(1)
+#define FIFOCTRL_NINITMAIN		BIT(0)
+
+#define PLLCTRL_DPDNSWAP_CLK		BIT(25)
+#define PLLCTRL_DPDNSWAP_DAT		BIT(24)
+#define PLLCTRL_PLLEN			BIT(23)
+#define PLLCTRL_SET_PMS(x)		REG_PUT(x, 19,  1)
+#define PLLCTRL_SET_P(x)		REG_PUT(x, 18, 13)
+#define PLLCTRL_SET_M(x)		REG_PUT(x, 12,  3)
+#define PLLCTRL_SET_S(x)		REG_PUT(x,  2,  0)
+
+#define MAX_MAIN_HRESOL		2047
+#define MAX_MAIN_VRESOL		2047
+#define MAX_SUB_HRESOL		1024
+#define MAX_SUB_VRESOL		1024
+
+/* in KHZ */
+#define MAX_ESC_CLK_FREQ	20000
+
+/* dsim all irqs index */
+#define PLLSTABLE		1
+#define SWRSTRELEASE		2
+#define SFRPLFIFOEMPTY		3
+#define SFRPHFIFOEMPTY		4
+#define SYNCOVERRIDE		5
+#define BUSTURNOVER		6
+#define FRAMEDONE		7
+#define LPDRTOUT		8
+#define TATOUT			9
+#define RXDATDONE		10
+#define RXTE			11
+#define RXACK			12
+#define ERRRXECC		13
+#define ERRRXCRC		14
+#define ERRESC3			15
+#define ERRESC2			16
+#define ERRESC1			17
+#define ERRESC0			18
+#define ERRSYNC3		19
+#define ERRSYNC2		20
+#define ERRSYNC1		21
+#define ERRSYNC0		22
+#define ERRCONTROL3		23
+#define ERRCONTROL2		24
+#define ERRCONTROL1		25
+#define ERRCONTROL0		26
+
+#define PS_TO_CYCLE(ps, hz) DIV64_U64_ROUND_CLOSEST(((ps) * (hz)), 1000000000000ULL)
+
+#define MIPI_HFP_PKT_OVERHEAD	6
+#define MIPI_HBP_PKT_OVERHEAD	6
+#define MIPI_HSA_PKT_OVERHEAD	6
+
+/* DSIM_STATUS */
+#define DSIM_STOP_STATE_DAT(x)		(((x) & 0xf) << 0)
+#define DSIM_STOP_STATE_CLK		BIT(8)
+#define DSIM_TX_READY_HS_CLK		BIT(10)
+#define DSIM_PLL_STABLE			BIT(31)
+
+/* DSIM_SWRST */
+#define DSIM_FUNCRST			BIT(16)
+#define DSIM_SWRST			BIT(0)
+
+/* DSIM_TIMEOUT */
+#define DSIM_LPDR_TIMEOUT(x)		((x) << 0)
+#define DSIM_BTA_TIMEOUT(x)		((x) << 16)
+
+/* DSIM_CLKCTRL */
+#define DSIM_ESC_PRESCALER(x)		(((x) & 0xffff) << 0)
+#define DSIM_ESC_PRESCALER_MASK		(0xffff << 0)
+#define DSIM_LANE_ESC_CLK_EN_CLK	BIT(19)
+#define DSIM_LANE_ESC_CLK_EN_DATA(x)	(((x) & 0xf) << 20)
+#define DSIM_LANE_ESC_CLK_EN_DATA_MASK	(0xf << 20)
+#define DSIM_BYTE_CLKEN			BIT(24)
+#define DSIM_BYTE_CLK_SRC(x)		(((x) & 0x3) << 25)
+#define DSIM_BYTE_CLK_SRC_MASK		(0x3 << 25)
+#define DSIM_PLL_BYPASS			BIT(27)
+#define DSIM_ESC_CLKEN			BIT(28)
+#define DSIM_TX_REQUEST_HSCLK		BIT(31)
+
+/* DSIM_CONFIG */
+#define DSIM_LANE_EN_CLK		BIT(0)
+#define DSIM_LANE_EN(x)			(((x) & 0xf) << 1)
+#define DSIM_NUM_OF_DATA_LANE(x)	(((x) & 0x3) << 5)
+#define DSIM_SUB_PIX_FORMAT(x)		(((x) & 0x7) << 8)
+#define DSIM_MAIN_PIX_FORMAT_MASK	(0x7 << 12)
+#define DSIM_MAIN_PIX_FORMAT_RGB888	(0x7 << 12)
+#define DSIM_MAIN_PIX_FORMAT_RGB666	(0x6 << 12)
+#define DSIM_MAIN_PIX_FORMAT_RGB666_P	(0x5 << 12)
+#define DSIM_MAIN_PIX_FORMAT_RGB565	(0x4 << 12)
+#define DSIM_SUB_VC(x)			(((x) & 0x3) << 16)
+#define DSIM_MAIN_VC(x)			(((x) & 0x3) << 18)
+#define DSIM_HSA_DISABLE_MODE		BIT(20)
+#define DSIM_HBP_DISABLE_MODE		BIT(21)
+#define DSIM_HFP_DISABLE_MODE		BIT(22)
+/*
+ * The i.MX 8M Mini Applications Processor Reference Manual,
+ * Rev. 3, 11/2020 Page 4091
+ * The i.MX 8M Nano Applications Processor Reference Manual,
+ * Rev. 2, 07/2022 Page 3058
+ * The i.MX 8M Plus Applications Processor Reference Manual,
+ * Rev. 1, 06/2021 Page 5436
+ * all claims this bit is 'HseDisableMode' with the definition
+ * 0 = Disables transfer
+ * 1 = Enables transfer
+ *
+ * This clearly states that HSE is not a disabled bit.
+ *
+ * The naming convention follows as per the manual and the
+ * driver logic is based on the MIPI_DSI_MODE_VIDEO_HSE flag.
+ */
+#define DSIM_HSE_DISABLE_MODE		BIT(23)
+#define DSIM_AUTO_MODE			BIT(24)
+#define DSIM_VIDEO_MODE			BIT(25)
+#define DSIM_BURST_MODE			BIT(26)
+#define DSIM_SYNC_INFORM		BIT(27)
+#define DSIM_EOT_DISABLE		BIT(28)
+#define DSIM_MFLUSH_VS			BIT(29)
+/* This flag is valid only for exynos3250/3472/5260/5430 */
+#define DSIM_CLKLANE_STOP		BIT(30)
+#define DSIM_NON_CONTINUOUS_CLKLANE	BIT(31)
+
+/* DSIM_ESCMODE */
+#define DSIM_TX_TRIGGER_RST		BIT(4)
+#define DSIM_TX_LPDT_LP			BIT(6)
+#define DSIM_CMD_LPDT_LP		BIT(7)
+#define DSIM_FORCE_BTA			BIT(16)
+#define DSIM_FORCE_STOP_STATE		BIT(20)
+#define DSIM_STOP_STATE_CNT(x)		(((x) & 0x7ff) << 21)
+#define DSIM_STOP_STATE_CNT_MASK	(0x7ff << 21)
+
+/* DSIM_MDRESOL */
+#define DSIM_MAIN_STAND_BY		BIT(31)
+#define DSIM_MAIN_VRESOL(x, num_bits)	(((x) & ((1 << (num_bits)) - 1)) << 16)
+#define DSIM_MAIN_HRESOL(x, num_bits)	(((x) & ((1 << (num_bits)) - 1)) << 0)
+
+/* DSIM_MVPORCH */
+#define DSIM_CMD_ALLOW(x)		((x) << 28)
+#define DSIM_STABLE_VFP(x)		((x) << 16)
+#define DSIM_MAIN_VBP(x)		((x) << 0)
+#define DSIM_CMD_ALLOW_MASK		(0xf << 28)
+#define DSIM_STABLE_VFP_MASK		(0x7ff << 16)
+#define DSIM_MAIN_VBP_MASK		(0x7ff << 0)
+
+/* DSIM_MHPORCH */
+#define DSIM_MAIN_HFP(x)		((x) << 16)
+#define DSIM_MAIN_HBP(x)		((x) << 0)
+#define DSIM_MAIN_HFP_MASK		((0xffff) << 16)
+#define DSIM_MAIN_HBP_MASK		((0xffff) << 0)
+
+/* DSIM_MSYNC */
+#define DSIM_MAIN_VSA(x)		((x) << 22)
+#define DSIM_MAIN_HSA(x)		((x) << 0)
+#define DSIM_MAIN_VSA_MASK		((0x3ff) << 22)
+#define DSIM_MAIN_HSA_MASK		((0xffff) << 0)
+
+/* DSIM_SDRESOL */
+#define DSIM_SUB_STANDY(x)		((x) << 31)
+#define DSIM_SUB_VRESOL(x)		((x) << 16)
+#define DSIM_SUB_HRESOL(x)		((x) << 0)
+#define DSIM_SUB_STANDY_MASK		((0x1) << 31)
+#define DSIM_SUB_VRESOL_MASK		((0x7ff) << 16)
+#define DSIM_SUB_HRESOL_MASK		((0x7ff) << 0)
+
+/* DSIM_INTSRC */
+#define DSIM_INT_PLL_STABLE		BIT(31)
+#define DSIM_INT_SW_RST_RELEASE		BIT(30)
+#define DSIM_INT_SFR_FIFO_EMPTY		BIT(29)
+#define DSIM_INT_SFR_HDR_FIFO_EMPTY	BIT(28)
+#define DSIM_INT_BTA			BIT(25)
+#define DSIM_INT_FRAME_DONE		BIT(24)
+#define DSIM_INT_RX_TIMEOUT		BIT(21)
+#define DSIM_INT_BTA_TIMEOUT		BIT(20)
+#define DSIM_INT_RX_DONE		BIT(18)
+#define DSIM_INT_RX_TE			BIT(17)
+#define DSIM_INT_RX_ACK			BIT(16)
+#define DSIM_INT_RX_ECC_ERR		BIT(15)
+#define DSIM_INT_RX_CRC_ERR		BIT(14)
+
+/* DSIM_FIFOCTRL */
+#define DSIM_RX_DATA_FULL		BIT(25)
+#define DSIM_RX_DATA_EMPTY		BIT(24)
+#define DSIM_SFR_HEADER_FULL		BIT(23)
+#define DSIM_SFR_HEADER_EMPTY		BIT(22)
+#define DSIM_SFR_PAYLOAD_FULL		BIT(21)
+#define DSIM_SFR_PAYLOAD_EMPTY		BIT(20)
+#define DSIM_I80_HEADER_FULL		BIT(19)
+#define DSIM_I80_HEADER_EMPTY		BIT(18)
+#define DSIM_I80_PAYLOAD_FULL		BIT(17)
+#define DSIM_I80_PAYLOAD_EMPTY		BIT(16)
+#define DSIM_SD_HEADER_FULL		BIT(15)
+#define DSIM_SD_HEADER_EMPTY		BIT(14)
+#define DSIM_SD_PAYLOAD_FULL		BIT(13)
+#define DSIM_SD_PAYLOAD_EMPTY		BIT(12)
+#define DSIM_MD_HEADER_FULL		BIT(11)
+#define DSIM_MD_HEADER_EMPTY		BIT(10)
+#define DSIM_MD_PAYLOAD_FULL		BIT(9)
+#define DSIM_MD_PAYLOAD_EMPTY		BIT(8)
+#define DSIM_RX_FIFO			BIT(4)
+#define DSIM_SFR_FIFO			BIT(3)
+#define DSIM_I80_FIFO			BIT(2)
+#define DSIM_SD_FIFO			BIT(1)
+#define DSIM_MD_FIFO			BIT(0)
+
+/* DSIM_PHYACCHR */
+#define DSIM_AFC_EN			BIT(14)
+#define DSIM_AFC_CTL(x)			(((x) & 0x7) << 5)
+
+/* DSIM_PLLCTRL */
+#define DSIM_FREQ_BAND(x)		((x) << 24)
+#define DSIM_PLL_EN			BIT(23)
+#define DSIM_PLL_P(x, offset)		((x) << (offset))
+#define DSIM_PLL_M(x)			((x) << 4)
+#define DSIM_PLL_S(x)			((x) << 1)
+
+/* DSIM_PHYCTRL */
+#define DSIM_PHYCTRL_ULPS_EXIT(x)	(((x) & 0x1ff) << 0)
+#define DSIM_PHYCTRL_B_DPHYCTL_VREG_LP	BIT(30)
+#define DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP	BIT(14)
+
+/* DSIM_PHYTIMING */
+#define DSIM_PHYTIMING_LPX(x)		((x) << 8)
+#define DSIM_PHYTIMING_HS_EXIT(x)	((x) << 0)
+
+/* DSIM_PHYTIMING1 */
+#define DSIM_PHYTIMING1_CLK_PREPARE(x)	((x) << 24)
+#define DSIM_PHYTIMING1_CLK_ZERO(x)	((x) << 16)
+#define DSIM_PHYTIMING1_CLK_POST(x)	((x) << 8)
+#define DSIM_PHYTIMING1_CLK_TRAIL(x)	((x) << 0)
+
+/* DSIM_PHYTIMING2 */
+#define DSIM_PHYTIMING2_HS_PREPARE(x)	((x) << 16)
+#define DSIM_PHYTIMING2_HS_ZERO(x)	((x) << 8)
+#define DSIM_PHYTIMING2_HS_TRAIL(x)	((x) << 0)
+
+#define DSI_MAX_BUS_WIDTH		4
+#define DSI_NUM_VIRTUAL_CHANNELS	4
+#define DSI_TX_FIFO_SIZE		2048
+#define DSI_RX_FIFO_SIZE		256
+#define DSI_XFER_TIMEOUT_MS		100
+#define DSI_RX_FIFO_EMPTY		0x30800002
+
+struct samsung_dsim_driver_data {
+	const unsigned int *reg_ofs;
+	unsigned int plltmr_reg;
+	unsigned int has_freqband:1;
+	unsigned int has_clklane_stop:1;
+	unsigned int has_broken_fifoctrl_emptyhdr:1;
+	unsigned int num_clks;
+	unsigned int min_freq;
+	unsigned int max_freq;
+	unsigned int wait_for_reset;
+	unsigned int num_bits_resol;
+	unsigned int pll_p_offset;
+	const unsigned int *reg_values;
+	unsigned int pll_fin_min;
+	unsigned int pll_fin_max;
+	u16 m_min;
+	u16 m_max;
+};
+
+enum reg_idx {
+	DSIM_STATUS_REG,	/* Status register */
+	DSIM_RGB_STATUS_REG,	/* RGB status register */
+	DSIM_SWRST_REG,		/* Software reset register */
+	DSIM_CLKCTRL_REG,	/* Clock control register */
+	DSIM_TIMEOUT_REG,	/* Time out register */
+	DSIM_CONFIG_REG,	/* Configuration register */
+	DSIM_ESCMODE_REG,	/* Escape mode register */
+	DSIM_MDRESOL_REG,
+	DSIM_MVPORCH_REG,	/* Main display Vporch register */
+	DSIM_MHPORCH_REG,	/* Main display Hporch register */
+	DSIM_MSYNC_REG,		/* Main display sync area register */
+	DSIM_INTSRC_REG,	/* Interrupt source register */
+	DSIM_INTMSK_REG,	/* Interrupt mask register */
+	DSIM_PKTHDR_REG,	/* Packet Header FIFO register */
+	DSIM_PAYLOAD_REG,	/* Payload FIFO register */
+	DSIM_RXFIFO_REG,	/* Read FIFO register */
+	DSIM_FIFOCTRL_REG,	/* FIFO status and control register */
+	DSIM_PLLCTRL_REG,	/* PLL control register */
+	DSIM_PHYCTRL_REG,
+	DSIM_PHYTIMING_REG,
+	DSIM_PHYTIMING1_REG,
+	DSIM_PHYTIMING2_REG,
+	NUM_REGS
+};
+
+static const unsigned int exynos_reg_ofs[] = {
+	[DSIM_STATUS_REG] =  0x00,
+	[DSIM_SWRST_REG] =  0x04,
+	[DSIM_CLKCTRL_REG] =  0x08,
+	[DSIM_TIMEOUT_REG] =  0x0c,
+	[DSIM_CONFIG_REG] =  0x10,
+	[DSIM_ESCMODE_REG] =  0x14,
+	[DSIM_MDRESOL_REG] =  0x18,
+	[DSIM_MVPORCH_REG] =  0x1c,
+	[DSIM_MHPORCH_REG] =  0x20,
+	[DSIM_MSYNC_REG] =  0x24,
+	[DSIM_INTSRC_REG] =  0x2c,
+	[DSIM_INTMSK_REG] =  0x30,
+	[DSIM_PKTHDR_REG] =  0x34,
+	[DSIM_PAYLOAD_REG] =  0x38,
+	[DSIM_RXFIFO_REG] =  0x3c,
+	[DSIM_FIFOCTRL_REG] =  0x44,
+	[DSIM_PLLCTRL_REG] =  0x4c,
+	[DSIM_PHYCTRL_REG] =  0x5c,
+	[DSIM_PHYTIMING_REG] =  0x64,
+	[DSIM_PHYTIMING1_REG] =  0x68,
+	[DSIM_PHYTIMING2_REG] =  0x6c,
+};
+
+static const unsigned int exynos5433_reg_ofs[] = {
+	[DSIM_STATUS_REG] = 0x04,
+	[DSIM_RGB_STATUS_REG] = 0x08,
+	[DSIM_SWRST_REG] = 0x0C,
+	[DSIM_CLKCTRL_REG] = 0x10,
+	[DSIM_TIMEOUT_REG] = 0x14,
+	[DSIM_CONFIG_REG] = 0x18,
+	[DSIM_ESCMODE_REG] = 0x1C,
+	[DSIM_MDRESOL_REG] = 0x20,
+	[DSIM_MVPORCH_REG] = 0x24,
+	[DSIM_MHPORCH_REG] = 0x28,
+	[DSIM_MSYNC_REG] = 0x2C,
+	[DSIM_INTSRC_REG] = 0x34,
+	[DSIM_INTMSK_REG] = 0x38,
+	[DSIM_PKTHDR_REG] = 0x3C,
+	[DSIM_PAYLOAD_REG] = 0x40,
+	[DSIM_RXFIFO_REG] = 0x44,
+	[DSIM_FIFOCTRL_REG] = 0x4C,
+	[DSIM_PLLCTRL_REG] = 0x94,
+	[DSIM_PHYCTRL_REG] = 0xA4,
+	[DSIM_PHYTIMING_REG] = 0xB4,
+	[DSIM_PHYTIMING1_REG] = 0xB8,
+	[DSIM_PHYTIMING2_REG] = 0xBC,
+};
+
+enum reg_value_idx {
+	RESET_TYPE,
+	PLL_TIMER,
+	STOP_STATE_CNT,
+	PHYCTRL_ULPS_EXIT,
+	PHYCTRL_VREG_LP,
+	PHYCTRL_SLEW_UP,
+	PHYTIMING_LPX,
+	PHYTIMING_HS_EXIT,
+	PHYTIMING_CLK_PREPARE,
+	PHYTIMING_CLK_ZERO,
+	PHYTIMING_CLK_POST,
+	PHYTIMING_CLK_TRAIL,
+	PHYTIMING_HS_PREPARE,
+	PHYTIMING_HS_ZERO,
+	PHYTIMING_HS_TRAIL
+};
+
+static const unsigned int reg_values[] = {
+	[RESET_TYPE] = DSIM_SWRST,
+	[PLL_TIMER] = 500,
+	[STOP_STATE_CNT] = 0xf,
+	[PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0x0af),
+	[PHYCTRL_VREG_LP] = 0,
+	[PHYCTRL_SLEW_UP] = 0,
+	[PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x06),
+	[PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0b),
+	[PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x07),
+	[PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x27),
+	[PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0d),
+	[PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x08),
+	[PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x09),
+	[PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x0d),
+	[PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0b),
+};
+
+static const unsigned int exynos5422_reg_values[] = {
+	[RESET_TYPE] = DSIM_SWRST,
+	[PLL_TIMER] = 500,
+	[STOP_STATE_CNT] = 0xf,
+	[PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0xaf),
+	[PHYCTRL_VREG_LP] = 0,
+	[PHYCTRL_SLEW_UP] = 0,
+	[PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x08),
+	[PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0d),
+	[PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x09),
+	[PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x30),
+	[PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0e),
+	[PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x0a),
+	[PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x0c),
+	[PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x11),
+	[PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0d),
+};
+
+static const unsigned int exynos5433_reg_values[] = {
+	[RESET_TYPE] = DSIM_FUNCRST,
+	[PLL_TIMER] = 22200,
+	[STOP_STATE_CNT] = 0xa,
+	[PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0x190),
+	[PHYCTRL_VREG_LP] = DSIM_PHYCTRL_B_DPHYCTL_VREG_LP,
+	[PHYCTRL_SLEW_UP] = DSIM_PHYCTRL_B_DPHYCTL_SLEW_UP,
+	[PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x07),
+	[PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0c),
+	[PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x09),
+	[PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x2d),
+	[PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0e),
+	[PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x09),
+	[PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x0b),
+	[PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x10),
+	[PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0c),
+};
+
+static const unsigned int imx8mm_dsim_reg_values[] = {
+	[RESET_TYPE] = DSIM_SWRST,
+	[PLL_TIMER] = 500,
+	[STOP_STATE_CNT] = 0xf,
+	[PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0xaf),
+	[PHYCTRL_VREG_LP] = 0,
+	[PHYCTRL_SLEW_UP] = 0,
+	[PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x06),
+	[PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0b),
+	[PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x07),
+	[PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x26),
+	[PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0d),
+	[PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x08),
+	[PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x08),
+	[PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x0d),
+	[PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0b),
+};
+
+static const struct samsung_dsim_driver_data exynos3_dsi_driver_data = {
+	.reg_ofs = exynos_reg_ofs,
+	.plltmr_reg = 0x50,
+	.has_freqband = 1,
+	.has_clklane_stop = 1,
+	.num_clks = 2,
+	.max_freq = 1000,
+	.wait_for_reset = 1,
+	.num_bits_resol = 11,
+	.pll_p_offset = 13,
+	.reg_values = reg_values,
+	.pll_fin_min = 6,
+	.pll_fin_max = 12,
+	.m_min = 41,
+	.m_max = 125,
+	.min_freq = 500,
+	.has_broken_fifoctrl_emptyhdr = 1,
+};
+
+static const struct samsung_dsim_driver_data exynos4_dsi_driver_data = {
+	.reg_ofs = exynos_reg_ofs,
+	.plltmr_reg = 0x50,
+	.has_freqband = 1,
+	.has_clklane_stop = 1,
+	.num_clks = 2,
+	.max_freq = 1000,
+	.wait_for_reset = 1,
+	.num_bits_resol = 11,
+	.pll_p_offset = 13,
+	.reg_values = reg_values,
+	.pll_fin_min = 6,
+	.pll_fin_max = 12,
+	.m_min = 41,
+	.m_max = 125,
+	.min_freq = 500,
+	.has_broken_fifoctrl_emptyhdr = 1,
+};
+
+static const struct samsung_dsim_driver_data exynos5_dsi_driver_data = {
+	.reg_ofs = exynos_reg_ofs,
+	.plltmr_reg = 0x58,
+	.num_clks = 2,
+	.max_freq = 1000,
+	.wait_for_reset = 1,
+	.num_bits_resol = 11,
+	.pll_p_offset = 13,
+	.reg_values = reg_values,
+	.pll_fin_min = 6,
+	.pll_fin_max = 12,
+	.m_min = 41,
+	.m_max = 125,
+	.min_freq = 500,
+};
+
+static const struct samsung_dsim_driver_data exynos5433_dsi_driver_data = {
+	.reg_ofs = exynos5433_reg_ofs,
+	.plltmr_reg = 0xa0,
+	.has_clklane_stop = 1,
+	.num_clks = 5,
+	.max_freq = 1500,
+	.wait_for_reset = 0,
+	.num_bits_resol = 12,
+	.pll_p_offset = 13,
+	.reg_values = exynos5433_reg_values,
+	.pll_fin_min = 6,
+	.pll_fin_max = 12,
+	.m_min = 41,
+	.m_max = 125,
+	.min_freq = 500,
+};
+
+static const struct samsung_dsim_driver_data exynos5422_dsi_driver_data = {
+	.reg_ofs = exynos5433_reg_ofs,
+	.plltmr_reg = 0xa0,
+	.has_clklane_stop = 1,
+	.num_clks = 2,
+	.max_freq = 1500,
+	.wait_for_reset = 1,
+	.num_bits_resol = 12,
+	.pll_p_offset = 13,
+	.reg_values = exynos5422_reg_values,
+	.pll_fin_min = 6,
+	.pll_fin_max = 12,
+	.m_min = 41,
+	.m_max = 125,
+	.min_freq = 500,
+};
+
+static const struct samsung_dsim_driver_data imx8mm_dsi_driver_data = {
+	.reg_ofs = exynos5433_reg_ofs,
+	.plltmr_reg = 0xa0,
+	.has_clklane_stop = 1,
+	.num_clks = 2,
+	.max_freq = 2100,
+	.wait_for_reset = 0,
+	.num_bits_resol = 12,
+	/*
+	 * Unlike Exynos, PLL_P(PMS_P) offset 14 is used in i.MX8M Mini/Nano/Plus
+	 * downstream driver - drivers/gpu/drm/bridge/sec-dsim.c
+	 */
+	.pll_p_offset = 14,
+	.reg_values = imx8mm_dsim_reg_values,
+	.pll_fin_min = 2,
+	.pll_fin_max = 30,
+	.m_min = 64,
+	.m_max = 1023,
+	.min_freq = 1050,
+};
+
+static const struct samsung_dsim_driver_data *
+samsung_dsim_types[DSIM_TYPE_COUNT] = {
+	[DSIM_TYPE_EXYNOS3250] = &exynos3_dsi_driver_data,
+	[DSIM_TYPE_EXYNOS4210] = &exynos4_dsi_driver_data,
+	[DSIM_TYPE_EXYNOS5410] = &exynos5_dsi_driver_data,
+	[DSIM_TYPE_EXYNOS5422] = &exynos5422_dsi_driver_data,
+	[DSIM_TYPE_EXYNOS5433] = &exynos5433_dsi_driver_data,
+	[DSIM_TYPE_IMX8MM] = &imx8mm_dsi_driver_data,
+	[DSIM_TYPE_IMX8MP] = &imx8mm_dsi_driver_data,
+};
+
+/* DSIM PLL configuration from spec:
+ *
+ * Fout(DDR) = (M * Fin) / (P * 2^S), so Fout / Fin = M / (P * 2^S)
+ * Fin_pll   = Fin / P     (6 ~ 12 MHz)
+ * S: [2:0], M: [12:3], P: [18:13], so
+ * TODO: 'S' is in [0 ~ 3], 'M' is in, 'P' is in [1 ~ 33]
+ *
+ */
+
+struct samsung_dsi {
+	void __iomem *reg_base;
+	struct clk sclk_mipi;
+	const struct samsung_dsim_driver_data *driver_data;
+
+	/* kHz clocks */
+	u64 pix_clk;
+	u64 bit_clk;
+	u64 hs_clock;
+
+	unsigned int lanes;
+	unsigned int channel;			/* virtual channel */
+	enum mipi_dsi_pixel_format format;
+	unsigned long mode_flags;
+	unsigned int pms;
+
+	struct mipi_dsi_device *device;
+	u32 max_data_lanes;
+
+	struct mipi_dsi_host dsi_host;
+	struct display_timing timings;
+};
+
+static inline void samsung_dsim_write(struct samsung_dsi *dsi,
+				      enum reg_idx idx, u32 val)
+{
+	writel(val, dsi->reg_base + dsi->driver_data->reg_ofs[idx]);
+}
+
+static inline u32 samsung_dsim_read(struct samsung_dsi *dsi, enum reg_idx idx)
+{
+	return readl(dsi->reg_base + dsi->driver_data->reg_ofs[idx]);
+}
+
+static int samsung_dsi_wait_for_pkt_done(struct samsung_dsi *dsim, unsigned long timeout)
+{
+	u32 intsrc;
+
+	do {
+		intsrc = samsung_dsim_read(dsim, DSIM_INTSRC_REG);
+		if (intsrc & INTSRC_SFRPLFIFOEMPTY) {
+			samsung_dsim_write(dsim, DSIM_INTSRC_REG, INTSRC_SFRPLFIFOEMPTY);
+			return 0;
+		}
+
+		udelay(1);
+	} while (--timeout);
+
+	return -ETIMEDOUT;
+}
+
+static int samsung_dsi_wait_for_hdr_done(struct samsung_dsi *dsim, unsigned long timeout)
+{
+	u32 intsrc;
+
+	do {
+		intsrc = samsung_dsim_read(dsim, DSIM_INTSRC_REG);
+		if (intsrc & INTSRC_SFRPHFIFOEMPTY) {
+			samsung_dsim_write(dsim, DSIM_INTSRC_REG, INTSRC_SFRPHFIFOEMPTY);
+			return 0;
+		}
+
+		udelay(1);
+	} while (--timeout);
+
+	return -ETIMEDOUT;
+}
+
+static int samsung_dsi_wait_for_rx_done(struct samsung_dsi *dsim,
+					unsigned long timeout)
+{
+	u32 intsrc;
+
+	do {
+		intsrc = samsung_dsim_read(dsim, DSIM_INTSRC_REG);
+		if (intsrc & INTSRC_RXDATDONE) {
+			samsung_dsim_write(dsim, DSIM_INTSRC_REG, INTSRC_RXDATDONE);
+			return 0;
+		}
+
+		udelay(1);
+	} while (--timeout);
+
+	return -ETIMEDOUT;
+}
+
+static int samsung_dsi_wait_pll_stable(struct samsung_dsi *dsim)
+{
+	u32 status;
+	ulong start;
+
+	start = get_timer(0);	/* Get current timestamp */
+
+	do {
+		status = samsung_dsim_read(dsim, DSIM_STATUS_REG);
+		if (status & DSIM_PLL_STABLE)
+			return 0;
+	} while (get_timer(0) < (start + 100)); /* Wait 100ms */
+
+	return -ETIMEDOUT;
+}
+
+static unsigned long samsung_dsim_pll_find_pms(struct samsung_dsi *dsi,
+					       unsigned long fin,
+					       unsigned long fout,
+					       u8 *p, u16 *m, u8 *s)
+{
+	const struct samsung_dsim_driver_data *driver_data = dsi->driver_data;
+	unsigned long best_freq = 0;
+	u32 min_delta = 0xffffffff;
+	u8 p_min, p_max;
+	u8 _p, best_p;
+	u16 _m, best_m;
+	u8 _s, best_s;
+
+	p_min = DIV_ROUND_UP(fin, (MHZ(12)));
+	p_max = fin / (MHZ(6));
+
+	for (_p = p_min; _p <= p_max; ++_p) {
+		for (_s = 0; _s <= 5; ++_s) {
+			u64 tmp;
+			u32 delta;
+
+			tmp = (u64)fout * (_p << _s);
+			do_div(tmp, fin);
+			_m = tmp;
+			if (_m < driver_data->m_min || _m > driver_data->m_max)
+				continue;
+
+			tmp = (u64)_m * fin;
+			do_div(tmp, _p);
+			if (tmp < driver_data->min_freq  * MHZ(1) ||
+			    tmp > driver_data->max_freq * MHZ(1))
+				continue;
+
+			tmp = (u64)_m * fin;
+			do_div(tmp, _p << _s);
+
+			delta = abs(fout - tmp);
+			if (delta < min_delta) {
+				best_p = _p;
+				best_m = _m;
+				best_s = _s;
+				min_delta = delta;
+				best_freq = tmp;
+			}
+		}
+	}
+
+	if (best_freq) {
+		*p = best_p;
+		*m = best_m;
+		*s = best_s;
+	}
+
+	return best_freq;
+}
+
+static int samsung_dsi_config_pll(struct samsung_dsi *dsim)
+{
+	int ret;
+	u32 pllctrl = 0, status, data_lanes_en, stop;
+
+	writel(dsim->driver_data->reg_values[PLL_TIMER],
+	       dsim->reg_base + dsim->driver_data->plltmr_reg);
+
+	/* TODO: config dp/dn swap if requires */
+
+	pllctrl |= PLLCTRL_SET_PMS(dsim->pms) | PLLCTRL_PLLEN;
+	samsung_dsim_write(dsim, DSIM_PLLCTRL_REG, pllctrl);
+
+	ret = samsung_dsi_wait_pll_stable(dsim);
+	if (ret) {
+		log_err("wait for pll stable time out\n");
+		return ret;
+	}
+
+	/* wait for clk & data lanes to go to stop state */
+	mdelay(1);
+
+	data_lanes_en = (0x1 << dsim->lanes) - 1;
+	status = samsung_dsim_read(dsim, DSIM_STATUS_REG);
+	if (!(status & DSIM_STOP_STATE_CLK)) {
+		log_err("clock is not in stop state\n");
+		return -EBUSY;
+	}
+
+	stop = DSIM_STOP_STATE_DAT(status);
+	if ((stop & data_lanes_en) != data_lanes_en) {
+		log_err("one or more data lanes is not in stop state\n");
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static void samsung_dsi_set_main_mode(struct samsung_dsi *dsim)
+{
+	u32 bpp, hfp_wc, hbp_wc, hsa_wc, wc;
+	u32 mdresol = 0, mvporch = 0, mhporch = 0, msync = 0;
+	struct display_timing *timings = &dsim->timings;
+	unsigned int num_bits_resol = dsim->driver_data->num_bits_resol;
+
+	mdresol |= DSIM_MAIN_VRESOL(timings->vactive.typ, num_bits_resol) |
+		   DSIM_MAIN_HRESOL(timings->hactive.typ, num_bits_resol);
+	samsung_dsim_write(dsim, DSIM_MDRESOL_REG, mdresol);
+
+	mvporch |= MVPORCH_SET_MAINVBP(timings->vback_porch.typ)    |
+		   MVPORCH_SET_STABLEVFP(timings->vfront_porch.typ) |
+		   MVPORCH_SET_CMDALLOW(0x0);
+	samsung_dsim_write(dsim, DSIM_MVPORCH_REG, mvporch);
+
+	bpp = mipi_dsi_pixel_format_to_bpp(dsim->format);
+
+	wc = DIV_ROUND_UP(timings->hfront_porch.typ * (bpp >> 3), dsim->lanes);
+	hfp_wc = wc > MIPI_HFP_PKT_OVERHEAD ?
+		wc - MIPI_HFP_PKT_OVERHEAD : timings->hfront_porch.typ;
+	wc = DIV_ROUND_UP(timings->hback_porch.typ * (bpp >> 3), dsim->lanes);
+	hbp_wc = wc > MIPI_HBP_PKT_OVERHEAD ?
+		wc - MIPI_HBP_PKT_OVERHEAD : timings->hback_porch.typ;
+
+	mhporch |= MHPORCH_SET_MAINHFP(hfp_wc) |
+		   MHPORCH_SET_MAINHBP(hbp_wc);
+
+	samsung_dsim_write(dsim, DSIM_MHPORCH_REG, mhporch);
+
+	wc = DIV_ROUND_UP(timings->hsync_len.typ * (bpp >> 3), dsim->lanes);
+	hsa_wc = wc > MIPI_HSA_PKT_OVERHEAD ?
+		wc - MIPI_HSA_PKT_OVERHEAD : timings->hsync_len.typ;
+
+	msync |= MSYNC_SET_MAINVSA(timings->vsync_len.typ) |
+		 MSYNC_SET_MAINHSA(hsa_wc);
+
+	debug("hfp_wc %u hbp_wc %u hsa_wc %u\n", hfp_wc, hbp_wc, hsa_wc);
+
+	samsung_dsim_write(dsim, DSIM_MSYNC_REG, msync);
+}
+
+static void samsung_dsi_config_dpi(struct samsung_dsi *dsim)
+{
+	u32 config = 0, rgb_status = 0, data_lanes_en;
+
+	if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO)
+		rgb_status &= ~RGB_STATUS_CMDMODE_INSEL;
+	else
+		rgb_status |= RGB_STATUS_CMDMODE_INSEL;
+
+	samsung_dsim_write(dsim, DSIM_RGB_STATUS_REG, rgb_status);
+
+	if (dsim->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
+		config |= DSIM_CLKLANE_STOP;
+		config |= DSIM_NON_CONTINUOUS_CLKLANE;
+	}
+
+	if (dsim->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH)
+		config |= DSIM_MFLUSH_VS;
+
+	/* disable EoT packets in HS mode */
+	if (dsim->mode_flags & MIPI_DSI_MODE_EOT_PACKET)
+		config |= DSIM_EOT_DISABLE;
+
+	if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO) {
+		config |= DSIM_VIDEO_MODE;
+
+		if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+			config |= DSIM_BURST_MODE;
+
+		else if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+			config |= DSIM_SYNC_INFORM;
+
+		if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT)
+			config |= DSIM_AUTO_MODE;
+
+		if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HSE)
+			config |= DSIM_HSE_DISABLE_MODE;
+
+		if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HFP)
+			config |= DSIM_HFP_DISABLE_MODE;
+
+		if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HBP)
+			config |= DSIM_HBP_DISABLE_MODE;
+
+		if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HSA)
+			config |= DSIM_HSA_DISABLE_MODE;
+	}
+
+	config |= DSIM_MAIN_VC(dsim->channel);
+
+	if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO) {
+		switch (dsim->format) {
+		case MIPI_DSI_FMT_RGB888:
+			config |= DSIM_MAIN_PIX_FORMAT_RGB888;
+			break;
+		case MIPI_DSI_FMT_RGB666:
+			config |= DSIM_MAIN_PIX_FORMAT_RGB666;
+			break;
+		case MIPI_DSI_FMT_RGB666_PACKED:
+			config |= DSIM_MAIN_PIX_FORMAT_RGB666_P;
+			break;
+		case MIPI_DSI_FMT_RGB565:
+			config |= DSIM_MAIN_PIX_FORMAT_RGB565;
+			break;
+		default:
+			log_err("invalid pixel format\n");
+			break;
+		}
+	}
+
+	/* config data lanes number and enable lanes */
+	data_lanes_en = BIT(dsim->lanes) - 1;
+	config |= (DSIM_NUM_OF_DATA_LANE(dsim->lanes - 1) | DSIM_LANE_EN_CLK |
+			DSIM_LANE_EN(data_lanes_en));
+
+	debug("DSIM config 0x%x\n", config);
+
+	samsung_dsim_write(dsim, DSIM_CONFIG_REG, config);
+}
+
+static void samsung_dsi_config_cmd_lpm(struct samsung_dsi *dsim, bool enable)
+{
+	u32 escmode;
+
+	escmode = samsung_dsim_read(dsim, DSIM_ESCMODE_REG);
+
+	if (enable)
+		escmode |= ESCMODE_CMDLPDT;
+	else
+		escmode &= ~ESCMODE_CMDLPDT;
+
+	samsung_dsim_write(dsim, DSIM_ESCMODE_REG, escmode);
+}
+
+static void samsung_dsi_config_dphy(struct samsung_dsi *dsim)
+{
+	const struct samsung_dsim_driver_data *driver_data = dsim->driver_data;
+	const unsigned int *reg_values = driver_data->reg_values;
+	u32 reg;
+	struct phy_configure_opts_mipi_dphy cfg;
+	int clk_prepare, lpx, clk_zero, clk_post, clk_trail;
+	int hs_exit, hs_prepare, hs_zero, hs_trail;
+	unsigned long long byte_clock = dsim->hs_clock / 8;
+
+	if (driver_data->has_freqband)
+		return;
+
+	phy_mipi_dphy_get_default_config_for_hsclk(dsim->hs_clock, dsim->lanes, &cfg);
+
+	/*
+	 * TODO:
+	 * The tech Applications Processor manuals for i.MX8M Mini, Nano,
+	 * and Plus don't state what the definition of the PHYTIMING
+	 * bits are beyond their address and bit position.
+	 * After reviewing NXP's downstream code, it appears
+	 * that the various PHYTIMING registers take the number
+	 * of cycles and use various dividers on them.  This
+	 * calculation does not result in an exact match to the
+	 * downstream code, but it is very close to the values
+	 * generated by their lookup table, and it appears
+	 * to sync at a variety of resolutions. If someone
+	 * can get a more accurate mathematical equation needed
+	 * for these registers, this should be updated.
+	 */
+
+	lpx = PS_TO_CYCLE(cfg.lpx, byte_clock);
+	hs_exit = PS_TO_CYCLE(cfg.hs_exit, byte_clock);
+	clk_prepare = PS_TO_CYCLE(cfg.clk_prepare, byte_clock);
+	clk_zero = PS_TO_CYCLE(cfg.clk_zero, byte_clock);
+	clk_post = PS_TO_CYCLE(cfg.clk_post, byte_clock);
+	clk_trail = PS_TO_CYCLE(cfg.clk_trail, byte_clock);
+	hs_prepare = PS_TO_CYCLE(cfg.hs_prepare, byte_clock);
+	hs_zero = PS_TO_CYCLE(cfg.hs_zero, byte_clock);
+	hs_trail = PS_TO_CYCLE(cfg.hs_trail, byte_clock);
+
+	/* B D-PHY: D-PHY Master & Slave Analog Block control */
+	reg = reg_values[PHYCTRL_ULPS_EXIT] | reg_values[PHYCTRL_VREG_LP] |
+		reg_values[PHYCTRL_SLEW_UP];
+
+	samsung_dsim_write(dsim, DSIM_PHYCTRL_REG, reg);
+
+	/*
+	 * T LPX: Transmitted length of any Low-Power state period
+	 * T HS-EXIT: Time that the transmitter drives LP-11 following a HS
+	 *	burst
+	 */
+
+	reg = DSIM_PHYTIMING_LPX(lpx) | DSIM_PHYTIMING_HS_EXIT(hs_exit);
+	samsung_dsim_write(dsim, DSIM_PHYTIMING_REG, reg);
+
+	/*
+	 * T CLK-PREPARE: Time that the transmitter drives the Clock Lane LP-00
+	 *	Line state immediately before the HS-0 Line state starting the
+	 *	HS transmission
+	 * T CLK-ZERO: Time that the transmitter drives the HS-0 state prior to
+	 *	transmitting the Clock.
+	 * T CLK_POST: Time that the transmitter continues to send HS clock
+	 *	after the last associated Data Lane has transitioned to LP Mode
+	 *	Interval is defined as the period from the end of T HS-TRAIL to
+	 *	the beginning of T CLK-TRAIL
+	 * T CLK-TRAIL: Time that the transmitter drives the HS-0 state after
+	 *	the last payload clock bit of a HS transmission burst
+	 */
+
+	reg = DSIM_PHYTIMING1_CLK_PREPARE(clk_prepare) |
+	      DSIM_PHYTIMING1_CLK_ZERO(clk_zero) |
+	      DSIM_PHYTIMING1_CLK_POST(clk_post) |
+	      DSIM_PHYTIMING1_CLK_TRAIL(clk_trail);
+
+	samsung_dsim_write(dsim, DSIM_PHYTIMING1_REG, reg);
+
+	/*
+	 * T HS-PREPARE: Time that the transmitter drives the Data Lane LP-00
+	 *	Line state immediately before the HS-0 Line state starting the
+	 *	HS transmission
+	 * T HS-ZERO: Time that the transmitter drives the HS-0 state prior to
+	 *	transmitting the Sync sequence.
+	 * T HS-TRAIL: Time that the transmitter drives the flipped differential
+	 *	state after last payload data bit of a HS transmission burst
+	 */
+
+	reg = DSIM_PHYTIMING2_HS_PREPARE(hs_prepare) |
+	      DSIM_PHYTIMING2_HS_ZERO(hs_zero) |
+	      DSIM_PHYTIMING2_HS_TRAIL(hs_trail);
+
+	samsung_dsim_write(dsim, DSIM_PHYTIMING2_REG, reg);
+
+	reg = DSIM_BTA_TIMEOUT(0xff) | DSIM_LPDR_TIMEOUT(0xffff);
+
+	samsung_dsim_write(dsim, DSIM_TIMEOUT_REG, reg);
+}
+
+static void samsung_dsim_write_pl_to_sfr_fifo(struct samsung_dsi *dsim,
+					      const void *payload,
+					      size_t length)
+{
+	u32 pl_data;
+
+	if (!length)
+		return;
+
+	while (length >= 4) {
+		pl_data = get_unaligned_le32(payload);
+		samsung_dsim_write(dsim, DSIM_PAYLOAD_REG, pl_data);
+		payload += 4;
+		length -= 4;
+	}
+
+	pl_data = 0;
+	switch (length) {
+	case 3:
+		pl_data |= ((u8 *)payload)[2] << 16;
+	case 2:
+		pl_data |= ((u8 *)payload)[1] << 8;
+	case 1:
+		pl_data |= ((u8 *)payload)[0];
+		samsung_dsim_write(dsim, DSIM_PAYLOAD_REG, pl_data);
+		break;
+	}
+}
+
+static void samsung_dsim_write_ph_to_sfr_fifo(struct samsung_dsi *dsim,
+					      void *header, bool use_lpm)
+{
+	u32 pkthdr;
+
+	pkthdr = PKTHDR_SET_DATA1(((u8 *)header)[2])	| /* WC MSB  */
+		 PKTHDR_SET_DATA0(((u8 *)header)[1])	| /* WC LSB  */
+		 PKTHDR_SET_DI(((u8 *)header)[0]);	  /* Data ID */
+
+	samsung_dsim_write(dsim, DSIM_PKTHDR_REG, pkthdr);
+}
+
+static int samsung_dsim_read_pl_from_sfr_fifo(struct samsung_dsi *dsim,
+					      void *payload, size_t length)
+{
+	u8 data_type;
+	u16 word_count = 0;
+	u32 fifoctrl, ph, pl;
+
+	fifoctrl = samsung_dsim_read(dsim, DSIM_FIFOCTRL_REG);
+
+	if (WARN_ON(fifoctrl & FIFOCTRL_EMPTYRX))
+		return -EINVAL;
+
+	ph = samsung_dsim_read(dsim, DSIM_RXFIFO_REG);
+	data_type = PKTHDR_GET_DT(ph);
+	switch (data_type) {
+	case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
+		dev_err(dsim->device->dev, "peripheral report error: (0-7)%x, (8-15)%x\n",
+			PKTHDR_GET_DATA0(ph), PKTHDR_GET_DATA1(ph));
+		return -EPROTO;
+	case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
+	case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
+		if (!WARN_ON(length < 2)) {
+			((u8 *)payload)[1] = PKTHDR_GET_DATA1(ph);
+			word_count++;
+		}
+		fallthrough;
+	case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
+	case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
+		((u8 *)payload)[0] = PKTHDR_GET_DATA0(ph);
+		word_count++;
+		length = word_count;
+		break;
+	case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE:
+	case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE:
+		word_count = PKTHDR_GET_WC(ph);
+		if (word_count > length) {
+			dev_err(dsim->device->dev, "invalid receive buffer length\n");
+			return -EINVAL;
+		}
+
+		length = word_count;
+
+		while (word_count >= 4) {
+			pl = samsung_dsim_read(dsim, DSIM_RXFIFO_REG);
+			((u8 *)payload)[0] = pl & 0xff;
+			((u8 *)payload)[1] = (pl >> 8)  & 0xff;
+			((u8 *)payload)[2] = (pl >> 16) & 0xff;
+			((u8 *)payload)[3] = (pl >> 24) & 0xff;
+			payload += 4;
+			word_count -= 4;
+		}
+
+		if (word_count > 0) {
+			pl = samsung_dsim_read(dsim, DSIM_RXFIFO_REG);
+
+			switch (word_count) {
+			case 3:
+				((u8 *)payload)[2] = (pl >> 16) & 0xff;
+			case 2:
+				((u8 *)payload)[1] = (pl >> 8) & 0xff;
+			case 1:
+				((u8 *)payload)[0] = pl & 0xff;
+				break;
+			}
+		}
+
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return length;
+}
+
+static void samsung_dsi_init_fifo_pointers(struct samsung_dsi *dsim)
+{
+	u32 fifoctrl, fifo_ptrs;
+
+	fifoctrl = samsung_dsim_read(dsim, DSIM_FIFOCTRL_REG);
+
+	fifo_ptrs = FIFOCTRL_NINITRX	|
+		    FIFOCTRL_NINITSFR	|
+		    FIFOCTRL_NINITI80	|
+		    FIFOCTRL_NINITSUB	|
+		    FIFOCTRL_NINITMAIN;
+
+	fifoctrl &= ~fifo_ptrs;
+	samsung_dsim_write(dsim, DSIM_FIFOCTRL_REG, fifoctrl);
+	udelay(500);
+
+	fifoctrl |= fifo_ptrs;
+	samsung_dsim_write(dsim, DSIM_FIFOCTRL_REG, fifoctrl);
+	udelay(500);
+}
+
+static void samsung_dsi_config_clkctrl(struct samsung_dsi *dsim)
+{
+	u32 clkctrl = 0, data_lanes_en;
+	u64 byte_clk, esc_prescaler;
+
+	clkctrl |= DSIM_TX_REQUEST_HSCLK;
+
+	/* using 1.5Gbps PHY */
+	clkctrl |= CLKCTRL_DPHY_SEL_1P5G;
+	clkctrl |= DSIM_ESC_CLKEN;
+	clkctrl &= ~CLKCTRL_PLLBYPASS;
+	clkctrl |= CLKCTRL_BYTECLKSRC_DPHY_PLL;
+	clkctrl |= DSIM_BYTE_CLKEN;
+
+	data_lanes_en = (0x1 << dsim->lanes) - 1;
+	clkctrl |= CLKCTRL_SET_LANEESCCLKEN(0x1 | data_lanes_en << 1);
+
+	/* calculate esc prescaler from byte clock:
+	 * EscClk = ByteClk / EscPrescaler;
+	 */
+	byte_clk = dsim->bit_clk >> 3;
+	esc_prescaler = DIV_ROUND_UP_ULL(byte_clk, MAX_ESC_CLK_FREQ);
+
+	clkctrl |= CLKCTRL_SET_ESCPRESCALER(esc_prescaler);
+
+	debug("DSIM clkctrl 0x%x\n", clkctrl);
+
+	samsung_dsim_write(dsim, DSIM_CLKCTRL_REG, clkctrl);
+}
+
+static void samsung_dsi_set_standby(struct samsung_dsi *dsim, bool standby)
+{
+	u32 mdresol = 0;
+
+	mdresol = samsung_dsim_read(dsim, DSIM_MDRESOL_REG);
+
+	if (standby)
+		mdresol |= DSIM_MAIN_STAND_BY;
+	else
+		mdresol &= ~DSIM_MAIN_STAND_BY;
+
+	samsung_dsim_write(dsim, DSIM_MDRESOL_REG, mdresol);
+}
+
+static void samsung_dsi_disable_clock(struct samsung_dsi *dsim)
+{
+	u32 reg;
+
+	reg = samsung_dsim_read(dsim, DSIM_CLKCTRL_REG);
+	reg &= ~(DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK |
+		 DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN);
+	samsung_dsim_write(dsim, DSIM_CLKCTRL_REG, reg);
+
+	reg = samsung_dsim_read(dsim, DSIM_PLLCTRL_REG);
+	reg &= ~DSIM_PLL_EN;
+	samsung_dsim_write(dsim, DSIM_PLLCTRL_REG, reg);
+}
+
+static inline struct samsung_dsi *host_to_dsi(struct mipi_dsi_host *host)
+{
+	return container_of(host, struct samsung_dsi, dsi_host);
+}
+
+static int samsung_dsi_bridge_clk_set(struct samsung_dsi *dsim_host)
+{
+	int bpp;
+	unsigned long pix_clk, bit_clk;
+	unsigned long fin, fout;
+	u8 p, s;
+	u16 m;
+
+	bpp = mipi_dsi_pixel_format_to_bpp(dsim_host->format);
+	if (bpp < 0)
+		return -EINVAL;
+
+	pix_clk = dsim_host->timings.pixelclock.typ;
+	bit_clk = DIV_ROUND_UP_ULL(pix_clk * bpp, dsim_host->lanes);
+
+	dsim_host->pix_clk = DIV_ROUND_UP_ULL(pix_clk, 1000);
+	dsim_host->bit_clk = DIV_ROUND_UP_ULL(bit_clk, 1000);
+
+	fout = dsim_host->bit_clk;
+	fin  = clk_get_rate(&dsim_host->sclk_mipi);
+	if (fin == 0) {
+		log_err("Error: DSI PHY reference clock is disabled\n");
+		return -EINVAL;
+	}
+
+	fout = samsung_dsim_pll_find_pms(dsim_host, fin, bit_clk, &p, &m, &s);
+	if (!fout) {
+		log_err("failed to find PLL PMS for requested frequency\n");
+		return -EINVAL;
+	}
+	dsim_host->pms = PLLCTRL_SET_P(p) | PLLCTRL_SET_M(m) |
+			 PLLCTRL_SET_S(s);
+	dsim_host->hs_clock = fout;
+
+	debug("%s: bitclk %llu pixclk %llu pms 0x%x\n", __func__,
+	      dsim_host->bit_clk, dsim_host->pix_clk, dsim_host->pms);
+
+	return 0;
+}
+
+static int samsung_dsi_bridge_prepare(struct samsung_dsi *dsim_host)
+{
+	int ret;
+
+	/* At this moment, the dsim bridge's preceding encoder has
+	 * already been enabled. So the dsim can be configed here
+	 */
+
+	/* config main display mode */
+	samsung_dsi_set_main_mode(dsim_host);
+
+	/* config dsim dpi */
+	samsung_dsi_config_dpi(dsim_host);
+
+	/* config dsim pll */
+	ret = samsung_dsi_config_pll(dsim_host);
+	if (ret) {
+		log_err("dsim pll config failed: %d\n", ret);
+		return ret;
+	}
+
+	/* config dphy timings */
+	samsung_dsi_config_dphy(dsim_host);
+
+	samsung_dsi_init_fifo_pointers(dsim_host);
+
+	/* config esc clock, byte clock and etc */
+	samsung_dsi_config_clkctrl(dsim_host);
+
+	return 0;
+}
+
+static int samsung_dsi_host_attach(struct mipi_dsi_host *host,
+				   struct mipi_dsi_device *device)
+{
+	struct samsung_dsi *dsi = host_to_dsi(host);
+
+	if (!device->lanes || device->lanes > dsi->max_data_lanes) {
+		log_err("invalid data lanes number\n");
+		return -EINVAL;
+	}
+
+	if (!(device->mode_flags & MIPI_DSI_MODE_VIDEO)		||
+	    !((device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)	||
+	      (device->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE))) {
+		log_err("unsupported dsi mode\n");
+		return -EINVAL;
+	}
+
+	if (device->format != MIPI_DSI_FMT_RGB888 &&
+	    device->format != MIPI_DSI_FMT_RGB565 &&
+	    device->format != MIPI_DSI_FMT_RGB666 &&
+	    device->format != MIPI_DSI_FMT_RGB666_PACKED) {
+		log_err("unsupported pixel format: %#x\n", device->format);
+		return -EINVAL;
+	}
+
+	dsi->lanes	 = device->lanes;
+	dsi->channel	 = device->channel;
+	dsi->format	 = device->format;
+	dsi->mode_flags = device->mode_flags;
+
+	debug("lanes %u, channel %u, format 0x%x, mode_flags 0x%lx\n", dsi->lanes,
+	      dsi->channel, dsi->format, dsi->mode_flags);
+
+	samsung_dsi_bridge_clk_set(dsi);
+	samsung_dsi_bridge_prepare(dsi);
+
+	return 0;
+}
+
+static ssize_t samsung_dsi_host_transfer(struct mipi_dsi_host *host,
+					 const struct mipi_dsi_msg *msg)
+{
+	struct samsung_dsi *dsim = host_to_dsi(host);
+	int ret, nb_bytes;
+	bool use_lpm;
+	struct mipi_dsi_packet packet;
+
+	ret = mipi_dsi_create_packet(&packet, msg);
+	if (ret) {
+		dev_err(dsim->device->dev, "failed to create dsi packet: %d\n", ret);
+		return ret;
+	}
+
+	/* config LPM for CMD TX */
+	use_lpm = msg->flags & MIPI_DSI_MSG_USE_LPM ? true : false;
+	samsung_dsi_config_cmd_lpm(dsim, use_lpm);
+
+	if (packet.payload_length) {		/* Long Packet case */
+		/* write packet payload */
+		samsung_dsim_write_pl_to_sfr_fifo(dsim, packet.payload,
+						  packet.payload_length);
+
+		/* write packet header */
+		samsung_dsim_write_ph_to_sfr_fifo(dsim, packet.header, use_lpm);
+
+		ret = samsung_dsi_wait_for_pkt_done(dsim, MIPI_FIFO_TIMEOUT);
+		if (ret) {
+			dev_err(dsim->device->dev, "wait tx done timeout!\n");
+			return -EBUSY;
+		}
+	} else {
+		/* write packet header */
+		samsung_dsim_write_ph_to_sfr_fifo(dsim, packet.header, use_lpm);
+
+		ret = samsung_dsi_wait_for_hdr_done(dsim, MIPI_FIFO_TIMEOUT);
+		if (ret) {
+			dev_err(dsim->device->dev, "wait pkthdr tx done time out\n");
+			return -EBUSY;
+		}
+	}
+
+	/* read packet payload */
+	if (unlikely(msg->rx_buf)) {
+		ret = samsung_dsi_wait_for_rx_done(dsim, MIPI_FIFO_TIMEOUT);
+		if (ret) {
+			dev_err(dsim->device->dev, "wait rx done time out\n");
+			return -EBUSY;
+		}
+
+		ret = samsung_dsim_read_pl_from_sfr_fifo(dsim, msg->rx_buf,
+							 msg->rx_len);
+		if (ret < 0)
+			return ret;
+		nb_bytes = msg->rx_len;
+	} else {
+		nb_bytes = packet.size;
+	}
+
+	return nb_bytes;
+}
+
+static const struct mipi_dsi_host_ops samsung_dsi_host_ops = {
+	.attach = samsung_dsi_host_attach,
+	.transfer = samsung_dsi_host_transfer,
+};
+
+static int samsung_dsi_init(struct udevice *dev,
+			    struct mipi_dsi_device *device,
+			    struct display_timing *timings,
+			    unsigned int max_data_lanes,
+			    const struct mipi_dsi_phy_ops *phy_ops)
+{
+	struct samsung_dsi *dsi = dev_get_priv(dev);
+	struct udevice *dsi_bridge = device->dev;
+	enum samsung_dsim_type hw_type = (enum samsung_dsim_type)dev_get_driver_data(dsi_bridge);
+
+	dsi->max_data_lanes = max_data_lanes;
+	dsi->device = device;
+	dsi->dsi_host.ops = &samsung_dsi_host_ops;
+	dsi->driver_data = samsung_dsim_types[hw_type];
+	device->host = &dsi->dsi_host;
+
+	dsi->reg_base = (void *)dev_read_addr(device->dev);
+	if ((fdt_addr_t)dsi->reg_base == FDT_ADDR_T_NONE) {
+		dev_err(device->dev, "dsi dt register address error\n");
+		return -EINVAL;
+	}
+
+	dsi->timings = *timings;
+
+	return 0;
+}
+
+static int samsung_dsi_enable(struct udevice *dev)
+{
+	struct samsung_dsi *dsim_host = dev_get_priv(dev);
+
+	/* enable data transfer of dsim */
+	samsung_dsi_set_standby(dsim_host, true);
+
+	return 0;
+}
+
+static int samsung_dsi_disable(struct udevice *dev)
+{
+	u32 intsrc;
+	struct samsung_dsi *dsim_host = dev_get_priv(dev);
+
+	/* disable data transfer of dsim */
+	samsung_dsi_set_standby(dsim_host, false);
+
+	/* disable esc clock & byte clock & dsim pll */
+	samsung_dsi_disable_clock(dsim_host);
+
+	/* Clear all intsrc */
+	intsrc = samsung_dsim_read(dsim_host, DSIM_INTSRC_REG);
+	samsung_dsim_write(dsim_host, DSIM_INTSRC_REG, intsrc);
+
+	return 0;
+}
+
+struct dsi_host_ops samsung_dsi_ops = {
+	.init = samsung_dsi_init,
+	.enable = samsung_dsi_enable,
+	.disable = samsung_dsi_disable,
+};
+
+static int samsung_dsi_probe(struct udevice *dev)
+{
+	struct samsung_dsi *dsim_host = dev_get_priv(dev);
+	int ret;
+
+	ret = clk_get_by_name(dev, "sclk_mipi", &dsim_host->sclk_mipi);
+	if (ret)
+		debug("Failed to get sclk_mipi clock\n");
+
+	return ret;
+}
+
+static const struct udevice_id samsung_dsi_ids[] = {
+	{ .compatible = "samsung,sec-mipi-dsi" },
+	{ }
+};
+
+U_BOOT_DRIVER(samsung_dsi) = {
+	.name			= "samsung_dsi",
+	.id			= UCLASS_DSI_HOST,
+	.of_match		= samsung_dsi_ids,
+	.probe			= samsung_dsi_probe,
+	.remove			= samsung_dsi_disable,
+	.ops			= &samsung_dsi_ops,
+	.priv_auto		= sizeof(struct samsung_dsi),
+};
diff --git a/drivers/video/bridge/samsung-dsim.c b/drivers/video/bridge/samsung-dsim.c
new file mode 100644
index 000000000000..986f1d830844
--- /dev/null
+++ b/drivers/video/bridge/samsung-dsim.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2024 Amarula Solutions
+ * Copyright 2019 NXP
+ *
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dsi_host.h>
+#include <mipi_dsi.h>
+#include <panel.h>
+#include <video.h>
+#include <video_bridge.h>
+#include <video_link.h>
+#include <asm/io.h>
+#include <asm/arch/gpio.h>
+#include <dm/device-internal.h>
+#include <linux/iopoll.h>
+#include <linux/err.h>
+#include <power/regulator.h>
+#include "samsung-dsim.h"
+
+struct samsung_dsim_priv {
+	struct mipi_dsi_device device;
+	void __iomem *base;
+	struct udevice *panel;
+	struct udevice *dsi_host;
+};
+
+static int samsung_dsim_attach(struct udevice *dev)
+{
+	struct samsung_dsim_priv *priv = dev_get_priv(dev);
+	struct mipi_dsi_device *device = &priv->device;
+	struct mipi_dsi_panel_plat *mplat;
+	struct display_timing timings;
+	int ret;
+
+	priv->panel = video_link_get_next_device(dev);
+	if (!priv->panel || device_get_uclass_id(priv->panel) != UCLASS_PANEL) {
+		dev_err(dev, "get panel device error\n");
+		return -ENODEV;
+	}
+
+	mplat = dev_get_plat(priv->panel);
+	mplat->device = &priv->device;
+
+	ret = video_link_get_display_timings(&timings);
+	if (ret) {
+		dev_err(dev, "decode display timing error %d\n", ret);
+		return ret;
+	}
+
+	ret = uclass_get_device(UCLASS_DSI_HOST, 0, &priv->dsi_host);
+	if (ret) {
+		dev_err(dev, "No video dsi host detected %d\n", ret);
+		return ret;
+	}
+
+	/* allow to use the compatible */
+	device->dev = dev;
+	ret = dsi_host_init(priv->dsi_host, device, &timings, 4,
+			    NULL);
+	if (ret) {
+		dev_err(dev, "failed to initialize mipi dsi host\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int samsung_dsim_set_backlight(struct udevice *dev, int percent)
+{
+	struct samsung_dsim_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = panel_enable_backlight(priv->panel);
+	if (ret) {
+		dev_err(dev, "panel %s enable backlight error %d\n",
+			priv->panel->name, ret);
+		return ret;
+	}
+
+	ret = dsi_host_enable(priv->dsi_host);
+	if (ret) {
+		dev_err(dev, "failed to enable mipi dsi host\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int samsung_dsim_probe(struct udevice *dev)
+{
+	struct samsung_dsim_priv *priv = dev_get_priv(dev);
+	struct mipi_dsi_device *device = &priv->device;
+
+	device->dev = dev;
+
+	return 0;
+}
+
+static int samsung_dsim_remove(struct udevice *dev)
+{
+	struct samsung_dsim_priv *priv = dev_get_priv(dev);
+	int ret;
+
+	if (priv->panel)
+		device_remove(priv->panel, DM_REMOVE_NORMAL);
+
+	ret = dsi_host_disable(priv->dsi_host);
+	if (ret) {
+		dev_err(dev, "failed to enable mipi dsi host\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int samsung_dsim_check_timing(struct udevice *dev, struct display_timing *timings)
+{
+	timings->flags &= ~DISPLAY_FLAGS_DE_HIGH;
+	return 0;
+}
+
+struct video_bridge_ops samsung_dsim_ops = {
+	.attach = samsung_dsim_attach,
+	.set_backlight = samsung_dsim_set_backlight,
+	.check_timing = samsung_dsim_check_timing,
+};
+
+static const struct udevice_id samsung_dsim_ids[] = {
+	{ .compatible = "fsl,imx8mm-mipi-dsim", .data = DSIM_TYPE_IMX8MM },
+	{ .compatible = "fsl,imx8mn-mipi-dsim", .data = DSIM_TYPE_IMX8MM },
+	{ }
+};
+
+U_BOOT_DRIVER(samsung_dsim) = {
+	.name				= "samsung_dsim",
+	.id				= UCLASS_VIDEO_BRIDGE,
+	.of_match			= samsung_dsim_ids,
+	.bind				= dm_scan_fdt_dev,
+	.remove				= samsung_dsim_remove,
+	.probe				= samsung_dsim_probe,
+	.ops				= &samsung_dsim_ops,
+	.priv_auto		= sizeof(struct samsung_dsim_priv),
+};
diff --git a/drivers/video/bridge/samsung-dsim.h b/drivers/video/bridge/samsung-dsim.h
new file mode 100644
index 000000000000..9bb2a379589b
--- /dev/null
+++ b/drivers/video/bridge/samsung-dsim.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2024 Amarula Solutions
+ */
+
+#ifndef SAMSUNG_DSIM_H
+#define SAMSUNG_DSIM_H
+
+enum samsung_dsim_type {
+	DSIM_TYPE_EXYNOS3250,
+	DSIM_TYPE_EXYNOS4210,
+	DSIM_TYPE_EXYNOS5410,
+	DSIM_TYPE_EXYNOS5422,
+	DSIM_TYPE_EXYNOS5433,
+	DSIM_TYPE_IMX8MM,
+	DSIM_TYPE_IMX8MP,
+	DSIM_TYPE_COUNT,
+};
+
+#endif
-- 
2.43.0



More information about the U-Boot mailing list