diff --git a/Makefile b/Makefile
index 28dd25f4c..1dcdc0da1 100644
--- a/Makefile
+++ b/Makefile
@@ -493,14 +493,16 @@ LINUXINCLUDE    := \
 		-I$(objtree)/arch/$(SRCARCH)/include/generated \
 		$(if $(building_out_of_srctree),-I$(srctree)/include) \
 		-I$(objtree)/include \
-		$(USERINCLUDE)
+		$(USERINCLUDE) \
+		-I$(srctree)/vendor/include \
+		-I$(srctree)/vendor/include/linux
 
 KBUILD_AFLAGS   := -D__ASSEMBLY__ -fno-PIE
 KBUILD_CFLAGS   := -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs \
 		   -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE \
 		   -Werror=implicit-function-declaration -Werror=implicit-int \
 		   -Werror=return-type -Wno-format-security \
-		   -std=gnu89
+		   -std=gnu99
 KBUILD_CPPFLAGS := -D__KERNEL__
 KBUILD_AFLAGS_KERNEL :=
 KBUILD_CFLAGS_KERNEL :=
@@ -956,9 +958,6 @@ endif
 # arch Makefile may override CC so keep this after arch Makefile is included
 NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include)
 
-# warn about C99 declaration after statement
-KBUILD_CFLAGS += -Wdeclaration-after-statement
-
 # Variable Length Arrays (VLAs) should not be used anywhere in the kernel
 KBUILD_CFLAGS += -Wvla
 
@@ -1396,7 +1395,7 @@ kselftest-merge:
 # Devicetree files
 
 ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/boot/dts/),)
-dtstree := arch/$(SRCARCH)/boot/dts
+dtstree := vendor/arch/$(SRCARCH)/boot/dts
 endif
 
 ifneq ($(dtstree),)
diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
index 485b7dbd4..5e039976c 100644
--- a/arch/arm64/Makefile
+++ b/arch/arm64/Makefile
@@ -44,8 +44,7 @@ ifeq ($(CONFIG_BROKEN_GAS_INST),y)
 $(warning Detected assembler with broken .inst; disassembly will be unreliable)
 endif
 
-KBUILD_CFLAGS	+= -mgeneral-regs-only	\
-		   $(compat_vdso) $(cc_has_k_constraint)
+KBUILD_CFLAGS	+= $(compat_vdso) $(cc_has_k_constraint)
 KBUILD_CFLAGS	+= $(call cc-disable-warning, psabi)
 KBUILD_AFLAGS	+= $(compat_vdso)
 
@@ -198,3 +197,16 @@ define archhelp
   echo  '                  (distribution) /sbin/installkernel or'
   echo  '                  install to $$(INSTALL_PATH) and run lilo'
 endef
+
+MAKE_MODULES ?= y
+
+%.img:
+ifeq ("$(CONFIG_MODULES)$(MAKE_MODULES)$(srctree)","yy$(objtree)")
+	$(Q)$(MAKE) rockchip/$*.dtb Image.lz4 modules
+else
+	$(Q)$(MAKE) rockchip/$*.dtb Image.lz4
+endif
+	$(Q)$(srctree)/vendor/scripts/mkimg --dtb $*.dtb
+
+CLEAN_DIRS += out
+CLEAN_FILES += boot.img kernel.img resource.img zboot.img
diff --git a/arch/arm64/boot/Makefile b/arch/arm64/boot/Makefile
index cd3414898..7469148c3 100644
--- a/arch/arm64/boot/Makefile
+++ b/arch/arm64/boot/Makefile
@@ -28,7 +28,7 @@ $(obj)/Image.gz: $(obj)/Image FORCE
 	$(call if_changed,gzip)
 
 $(obj)/Image.lz4: $(obj)/Image FORCE
-	$(call if_changed,lz4)
+	$(call if_changed,lz4c)
 
 $(obj)/Image.lzma: $(obj)/Image FORCE
 	$(call if_changed,lzma)
diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c
index 4c0e72781..2094dce73 100644
--- a/arch/arm64/kernel/cpuinfo.c
+++ b/arch/arm64/kernel/cpuinfo.c
@@ -25,6 +25,13 @@
 #include <linux/smp.h>
 #include <linux/delay.h>
 
+#ifdef CONFIG_ARCH_ROCKCHIP
+unsigned int system_serial_low;
+EXPORT_SYMBOL(system_serial_low);
+
+unsigned int system_serial_high;
+EXPORT_SYMBOL(system_serial_high);
+#endif
 /*
  * In case the boot CPU is hotpluggable, we record its initial state and
  * current state separately. Certain system registers may contain different
@@ -200,6 +207,10 @@ static int c_show(struct seq_file *m, void *v)
 		seq_printf(m, "CPU revision\t: %d\n\n", MIDR_REVISION(midr));
 	}
 
+#ifdef CONFIG_ARCH_ROCKCHIP
+	seq_printf(m, "Serial\t\t: %08x%08x\n",
+		   system_serial_high, system_serial_low);
+#endif
 	return 0;
 }
 
diff --git a/drivers/clk/rockchip/Kconfig b/drivers/clk/rockchip/Kconfig
index 47cd6c5de..99e66263a 100644
--- a/drivers/clk/rockchip/Kconfig
+++ b/drivers/clk/rockchip/Kconfig
@@ -2,7 +2,7 @@
 # common clock support for ROCKCHIP SoC family.
 
 config COMMON_CLK_ROCKCHIP
-	bool "Rockchip clock controller common support"
+	tristate "Rockchip clock controller common support"
 	depends on ARCH_ROCKCHIP
 	default ARCH_ROCKCHIP
 	help
@@ -10,69 +10,79 @@ config COMMON_CLK_ROCKCHIP
 
 if COMMON_CLK_ROCKCHIP
 config CLK_PX30
-	bool "Rockchip PX30 clock controller support"
-	default y
+	tristate "Rockchip PX30 clock controller support"
+	depends on ARM64 || COMPILE_TEST
+	default n
 	help
 	  Build the driver for PX30 Clock Driver.
 
 config CLK_RV110X
-	bool "Rockchip RV110x clock controller support"
-	default y
+	tristate "Rockchip RV110x clock controller support"
+	depends on ARM || COMPILE_TEST
+	default n
 	help
 	  Build the driver for RV110x Clock Driver.
 
 config CLK_RK3036
-	bool "Rockchip RK3036 clock controller support"
-	default y
+	tristate "Rockchip RK3036 clock controller support"
+	depends on ARM || COMPILE_TEST
+	default n
 	help
 	  Build the driver for RK3036 Clock Driver.
 
 config CLK_RK312X
-	bool "Rockchip RK312x clock controller support"
-	default y
+	tristate "Rockchip RK312x clock controller support"
+	depends on ARM || COMPILE_TEST
+	default n
 	help
 	  Build the driver for RK312x Clock Driver.
 
 config CLK_RK3188
-	bool "Rockchip RK3188 clock controller support"
-	default y
+	tristate "Rockchip RK3188 clock controller support"
+	depends on ARM || COMPILE_TEST
+	default n
 	help
 	  Build the driver for RK3188 Clock Driver.
 
 config CLK_RK322X
-	bool "Rockchip RK322x clock controller support"
-	default y
+	tristate "Rockchip RK322x clock controller support"
+	depends on ARM || COMPILE_TEST
+	default n
 	help
 	  Build the driver for RK322x Clock Driver.
 
 config CLK_RK3288
-	bool "Rockchip RK3288 clock controller support"
-	depends on ARM
-	default y
+	tristate "Rockchip RK3288 clock controller support"
+	depends on ARM || COMPILE_TEST
+	default n
 	help
 	  Build the driver for RK3288 Clock Driver.
 
 config CLK_RK3308
-	bool "Rockchip RK3308 clock controller support"
-	default y
+	tristate "Rockchip RK3308 clock controller support"
+	depends on ARM64 || COMPILE_TEST
+	default n
 	help
 	  Build the driver for RK3308 Clock Driver.
 
 config CLK_RK3328
-	bool "Rockchip RK3328 clock controller support"
-	default y
+	tristate "Rockchip RK3328 clock controller support"
+	depends on ARM64 || COMPILE_TEST
+	default n
 	help
 	  Build the driver for RK3328 Clock Driver.
 
 config CLK_RK3368
-	bool "Rockchip RK3368 clock controller support"
-	default y
+	tristate "Rockchip RK3368 clock controller support"
+	depends on ARM64 || COMPILE_TEST
+	default n
 	help
 	  Build the driver for RK3368 Clock Driver.
 
 config CLK_RK3399
 	tristate "Rockchip RK3399 clock controller support"
-	default y
+	depends on ARM64 || COMPILE_TEST
+	default n
 	help
 	  Build the driver for RK3399 Clock Driver.
 endif
diff --git a/drivers/clk/rockchip/clk-cpu.c b/drivers/clk/rockchip/clk-cpu.c
index 0dc478a19..3293174a6 100644
--- a/drivers/clk/rockchip/clk-cpu.c
+++ b/drivers/clk/rockchip/clk-cpu.c
@@ -51,6 +51,7 @@
  */
 struct rockchip_cpuclk {
 	struct clk_hw				hw;
+	struct clk_hw				*pll_hw;
 
 	struct clk_mux				cpu_mux;
 	const struct clk_ops			*cpu_mux_ops;
@@ -88,10 +89,10 @@ static unsigned long rockchip_cpuclk_recalc_rate(struct clk_hw *hw,
 {
 	struct rockchip_cpuclk *cpuclk = to_rockchip_cpuclk_hw(hw);
 	const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data;
-	u32 clksel0 = readl_relaxed(cpuclk->reg_base + reg_data->core_reg);
+	u32 clksel0 = readl_relaxed(cpuclk->reg_base + reg_data->core_reg[0]);
 
-	clksel0 >>= reg_data->div_core_shift;
-	clksel0 &= reg_data->div_core_mask;
+	clksel0 >>= reg_data->div_core_shift[0];
+	clksel0 &= reg_data->div_core_mask[0];
 	return parent_rate / (clksel0 + 1);
 }
 
@@ -117,6 +118,42 @@ static void rockchip_cpuclk_set_dividers(struct rockchip_cpuclk *cpuclk,
 	}
 }
 
+static void rockchip_cpuclk_set_pre_muxs(struct rockchip_cpuclk *cpuclk,
+					 const struct rockchip_cpuclk_rate_table *rate)
+{
+	int i;
+
+	/* alternate parent is active now. set the pre_muxs */
+	for (i = 0; i < ARRAY_SIZE(rate->pre_muxs); i++) {
+		const struct rockchip_cpuclk_clksel *clksel = &rate->pre_muxs[i];
+
+		if (!clksel->reg)
+			break;
+
+		pr_debug("%s: setting reg 0x%x to 0x%x\n",
+			 __func__, clksel->reg, clksel->val);
+		writel(clksel->val, cpuclk->reg_base + clksel->reg);
+	}
+}
+
+static void rockchip_cpuclk_set_post_muxs(struct rockchip_cpuclk *cpuclk,
+					  const struct rockchip_cpuclk_rate_table *rate)
+{
+	int i;
+
+	/* alternate parent is active now. set the muxs */
+	for (i = 0; i < ARRAY_SIZE(rate->post_muxs); i++) {
+		const struct rockchip_cpuclk_clksel *clksel = &rate->post_muxs[i];
+
+		if (!clksel->reg)
+			break;
+
+		pr_debug("%s: setting reg 0x%x to 0x%x\n",
+			 __func__, clksel->reg, clksel->val);
+		writel(clksel->val, cpuclk->reg_base + clksel->reg);
+	}
+}
+
 static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk,
 					   struct clk_notifier_data *ndata)
 {
@@ -124,6 +161,7 @@ static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk,
 	const struct rockchip_cpuclk_rate_table *rate;
 	unsigned long alt_prate, alt_div;
 	unsigned long flags;
+	int i = 0;
 
 	/* check validity of the new rate */
 	rate = rockchip_get_cpuclk_settings(cpuclk, ndata->new_rate);
@@ -133,6 +171,8 @@ static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk,
 		return -EINVAL;
 	}
 
+	rockchip_boost_enable_recovery_sw_low(cpuclk->pll_hw);
+
 	alt_prate = clk_get_rate(cpuclk->alt_parent);
 
 	spin_lock_irqsave(cpuclk->lock, flags);
@@ -146,10 +186,10 @@ static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk,
 	if (alt_prate > ndata->old_rate) {
 		/* calculate dividers */
 		alt_div =  DIV_ROUND_UP(alt_prate, ndata->old_rate) - 1;
-		if (alt_div > reg_data->div_core_mask) {
+		if (alt_div > reg_data->div_core_mask[0]) {
 			pr_warn("%s: limiting alt-divider %lu to %d\n",
-				__func__, alt_div, reg_data->div_core_mask);
-			alt_div = reg_data->div_core_mask;
+				__func__, alt_div, reg_data->div_core_mask[0]);
+			alt_div = reg_data->div_core_mask[0];
 		}
 
 		/*
@@ -162,19 +202,28 @@ static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk,
 		pr_debug("%s: setting div %lu as alt-rate %lu > old-rate %lu\n",
 			 __func__, alt_div, alt_prate, ndata->old_rate);
 
-		writel(HIWORD_UPDATE(alt_div, reg_data->div_core_mask,
-					      reg_data->div_core_shift) |
-		       HIWORD_UPDATE(reg_data->mux_core_alt,
+		for (i = 0; i < reg_data->num_cores; i++) {
+			writel(HIWORD_UPDATE(alt_div, reg_data->div_core_mask[i],
+					     reg_data->div_core_shift[i]),
+			       cpuclk->reg_base + reg_data->core_reg[i]);
+		}
+	}
+
+	rockchip_boost_add_core_div(cpuclk->pll_hw, alt_prate);
+
+	rockchip_cpuclk_set_pre_muxs(cpuclk, rate);
+
+	/* select alternate parent */
+	if (reg_data->mux_core_reg)
+		writel(HIWORD_UPDATE(reg_data->mux_core_alt,
 				     reg_data->mux_core_mask,
 				     reg_data->mux_core_shift),
-		       cpuclk->reg_base + reg_data->core_reg);
-	} else {
-		/* select alternate parent */
+		       cpuclk->reg_base + reg_data->mux_core_reg);
+	else
 		writel(HIWORD_UPDATE(reg_data->mux_core_alt,
 				     reg_data->mux_core_mask,
 				     reg_data->mux_core_shift),
-		       cpuclk->reg_base + reg_data->core_reg);
-	}
+		       cpuclk->reg_base + reg_data->core_reg[0]);
 
 	spin_unlock_irqrestore(cpuclk->lock, flags);
 	return 0;
@@ -186,6 +235,7 @@ static int rockchip_cpuclk_post_rate_change(struct rockchip_cpuclk *cpuclk,
 	const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data;
 	const struct rockchip_cpuclk_rate_table *rate;
 	unsigned long flags;
+	int i = 0;
 
 	rate = rockchip_get_cpuclk_settings(cpuclk, ndata->new_rate);
 	if (!rate) {
@@ -206,16 +256,31 @@ static int rockchip_cpuclk_post_rate_change(struct rockchip_cpuclk *cpuclk,
 	 * primary parent by the extra dividers that were needed for the alt.
 	 */
 
-	writel(HIWORD_UPDATE(0, reg_data->div_core_mask,
-				reg_data->div_core_shift) |
-	       HIWORD_UPDATE(reg_data->mux_core_main,
-				reg_data->mux_core_mask,
-				reg_data->mux_core_shift),
-	       cpuclk->reg_base + reg_data->core_reg);
+	if (reg_data->mux_core_reg)
+		writel(HIWORD_UPDATE(reg_data->mux_core_main,
+				     reg_data->mux_core_mask,
+				     reg_data->mux_core_shift),
+		       cpuclk->reg_base + reg_data->mux_core_reg);
+	else
+		writel(HIWORD_UPDATE(reg_data->mux_core_main,
+				     reg_data->mux_core_mask,
+				     reg_data->mux_core_shift),
+		       cpuclk->reg_base + reg_data->core_reg[0]);
+
+	rockchip_cpuclk_set_post_muxs(cpuclk, rate);
+
+	/* remove dividers */
+	for (i = 0; i < reg_data->num_cores; i++) {
+		writel(HIWORD_UPDATE(0, reg_data->div_core_mask[i],
+				     reg_data->div_core_shift[i]),
+		       cpuclk->reg_base + reg_data->core_reg[i]);
+	}
 
 	if (ndata->old_rate > ndata->new_rate)
 		rockchip_cpuclk_set_dividers(cpuclk, rate);
 
+	rockchip_boost_disable_recovery_sw(cpuclk->pll_hw);
+
 	spin_unlock_irqrestore(cpuclk->lock, flags);
 	return 0;
 }
@@ -244,14 +309,16 @@ static int rockchip_cpuclk_notifier_cb(struct notifier_block *nb,
 }
 
 struct clk *rockchip_clk_register_cpuclk(const char *name,
-			const char *const *parent_names, u8 num_parents,
+			u8 num_parents,
+			struct clk *parent, struct clk *alt_parent,
 			const struct rockchip_cpuclk_reg_data *reg_data,
 			const struct rockchip_cpuclk_rate_table *rates,
 			int nrates, void __iomem *reg_base, spinlock_t *lock)
 {
 	struct rockchip_cpuclk *cpuclk;
 	struct clk_init_data init;
-	struct clk *clk, *cclk;
+	struct clk *clk, *cclk, *pll_clk;
+	const char *parent_name;
 	int ret;
 
 	if (num_parents < 2) {
@@ -259,12 +326,18 @@ struct clk *rockchip_clk_register_cpuclk(const char *name,
 		return ERR_PTR(-EINVAL);
 	}
 
+	if (IS_ERR(parent) || IS_ERR(alt_parent)) {
+		pr_err("%s: invalid parent clock(s)\n", __func__);
+		return ERR_PTR(-EINVAL);
+	}
+
 	cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
 	if (!cpuclk)
 		return ERR_PTR(-ENOMEM);
 
+	parent_name = clk_hw_get_name(__clk_get_hw(parent));
 	init.name = name;
-	init.parent_names = &parent_names[reg_data->mux_core_main];
+	init.parent_names = &parent_name;
 	init.num_parents = 1;
 	init.ops = &rockchip_cpuclk_ops;
 
@@ -281,8 +354,19 @@ struct clk *rockchip_clk_register_cpuclk(const char *name,
 	cpuclk->reg_data = reg_data;
 	cpuclk->clk_nb.notifier_call = rockchip_cpuclk_notifier_cb;
 	cpuclk->hw.init = &init;
+	if (reg_data->pll_name) {
+		pll_clk = clk_get_parent(parent);
+		if (!pll_clk) {
+			pr_err("%s: could not lookup pll clock: (%s)\n",
+			       __func__, reg_data->pll_name);
+			ret = -EINVAL;
+			goto free_cpuclk;
+		}
+		cpuclk->pll_hw = __clk_get_hw(pll_clk);
+		rockchip_boost_init(cpuclk->pll_hw);
+	}
 
-	cpuclk->alt_parent = __clk_lookup(parent_names[reg_data->mux_core_alt]);
+	cpuclk->alt_parent = alt_parent;
 	if (!cpuclk->alt_parent) {
 		pr_err("%s: could not lookup alternate parent: (%d)\n",
 		       __func__, reg_data->mux_core_alt);
@@ -297,11 +381,11 @@ struct clk *rockchip_clk_register_cpuclk(const char *name,
 		goto free_cpuclk;
 	}
 
-	clk = __clk_lookup(parent_names[reg_data->mux_core_main]);
+	clk = parent;
 	if (!clk) {
 		pr_err("%s: could not lookup parent clock: (%d) %s\n",
 		       __func__, reg_data->mux_core_main,
-		       parent_names[reg_data->mux_core_main]);
+		       parent_name);
 		ret = -EINVAL;
 		goto free_alt_parent;
 	}
diff --git a/drivers/clk/rockchip/clk-ddr.c b/drivers/clk/rockchip/clk-ddr.c
index 86718c54e..3c8bcbee2 100644
--- a/drivers/clk/rockchip/clk-ddr.c
+++ b/drivers/clk/rockchip/clk-ddr.c
@@ -8,10 +8,20 @@
 #include <linux/clk.h>
 #include <linux/clk-provider.h>
 #include <linux/io.h>
+#include <linux/of.h>
+#include <linux/rockchip/rockchip_sip.h>
 #include <linux/slab.h>
 #include <soc/rockchip/rockchip_sip.h>
+#include <soc/rockchip/scpi.h>
+#include <uapi/drm/drm_mode.h>
+#ifdef CONFIG_ARM
+#include <asm/psci.h>
+#endif
+
 #include "clk.h"
 
+#define MHZ		(1000000)
+
 struct rockchip_ddrclk {
 	struct clk_hw	hw;
 	void __iomem	*reg_base;
@@ -21,25 +31,47 @@ struct rockchip_ddrclk {
 	int		div_shift;
 	int		div_width;
 	int		ddr_flag;
-	spinlock_t	*lock;
 };
 
 #define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw)
 
+struct share_params_ddrclk {
+	u32 hz;
+	u32 lcdc_type;
+};
+
+struct rockchip_ddrclk_data {
+	void __iomem *params;
+	int (*dmcfreq_wait_complete)(void);
+};
+
+static struct rockchip_ddrclk_data ddr_data = {NULL, NULL};
+
+void rockchip_set_ddrclk_params(void __iomem *params)
+{
+	ddr_data.params = params;
+}
+EXPORT_SYMBOL(rockchip_set_ddrclk_params);
+
+void rockchip_set_ddrclk_dmcfreq_wait_complete(int (*func)(void))
+{
+	ddr_data.dmcfreq_wait_complete = func;
+}
+EXPORT_SYMBOL(rockchip_set_ddrclk_dmcfreq_wait_complete);
+
 static int rockchip_ddrclk_sip_set_rate(struct clk_hw *hw, unsigned long drate,
 					unsigned long prate)
 {
-	struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw);
-	unsigned long flags;
 	struct arm_smccc_res res;
 
-	spin_lock_irqsave(ddrclk->lock, flags);
 	arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, drate, 0,
 		      ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE,
 		      0, 0, 0, 0, &res);
-	spin_unlock_irqrestore(ddrclk->lock, flags);
 
-	return res.a0;
+	if (res.a0)
+		return 0;
+	else
+		return -EPERM;
 }
 
 static unsigned long
@@ -87,18 +119,134 @@ static const struct clk_ops rockchip_ddrclk_sip_ops = {
 	.get_parent = rockchip_ddrclk_get_parent,
 };
 
+static u32 ddr_clk_cached;
+
+static int rockchip_ddrclk_scpi_set_rate(struct clk_hw *hw, unsigned long drate,
+					 unsigned long prate)
+{
+	u32 ret;
+	u32 lcdc_type = 0;
+	struct share_params_ddrclk *p;
+
+	p = (struct share_params_ddrclk *)ddr_data.params;
+	if (p)
+		lcdc_type = p->lcdc_type;
+
+	ret = scpi_ddr_set_clk_rate(drate / MHZ, lcdc_type);
+	if (ret) {
+		ddr_clk_cached = ret;
+		ret = 0;
+	} else {
+		ddr_clk_cached = 0;
+		ret = -1;
+	}
+
+	return ret;
+}
+
+static unsigned long rockchip_ddrclk_scpi_recalc_rate(struct clk_hw *hw,
+						      unsigned long parent_rate)
+{
+	if (ddr_clk_cached)
+		return (MHZ * ddr_clk_cached);
+	else
+		return (MHZ * scpi_ddr_get_clk_rate());
+}
+
+static long rockchip_ddrclk_scpi_round_rate(struct clk_hw *hw,
+					    unsigned long rate,
+					    unsigned long *prate)
+{
+	rate = rate / MHZ;
+	rate = (rate / 12) * 12;
+
+	return (rate * MHZ);
+}
+
+static const struct clk_ops rockchip_ddrclk_scpi_ops = {
+	.recalc_rate = rockchip_ddrclk_scpi_recalc_rate,
+	.set_rate = rockchip_ddrclk_scpi_set_rate,
+	.round_rate = rockchip_ddrclk_scpi_round_rate,
+	.get_parent = rockchip_ddrclk_get_parent,
+};
+
+static int rockchip_ddrclk_sip_set_rate_v2(struct clk_hw *hw,
+					   unsigned long drate,
+					   unsigned long prate)
+{
+	struct share_params_ddrclk *p;
+	struct arm_smccc_res res;
+
+	p = (struct share_params_ddrclk *)ddr_data.params;
+	if (p)
+		p->hz = drate;
+
+	res = sip_smc_dram(SHARE_PAGE_TYPE_DDR, 0,
+			   ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE);
+
+	if ((int)res.a1 == SIP_RET_SET_RATE_TIMEOUT) {
+		if (ddr_data.dmcfreq_wait_complete)
+			ddr_data.dmcfreq_wait_complete();
+	}
+
+	return res.a0;
+}
+
+static unsigned long rockchip_ddrclk_sip_recalc_rate_v2
+			(struct clk_hw *hw, unsigned long parent_rate)
+{
+	struct arm_smccc_res res;
+
+	res = sip_smc_dram(SHARE_PAGE_TYPE_DDR, 0,
+			   ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE);
+	if (!res.a0)
+		return res.a1;
+	else
+		return 0;
+}
+
+static long rockchip_ddrclk_sip_round_rate_v2(struct clk_hw *hw,
+					      unsigned long rate,
+					      unsigned long *prate)
+{
+	struct share_params_ddrclk *p;
+	struct arm_smccc_res res;
+
+	p = (struct share_params_ddrclk *)ddr_data.params;
+	if (p)
+		p->hz = rate;
+
+	res = sip_smc_dram(SHARE_PAGE_TYPE_DDR, 0,
+			   ROCKCHIP_SIP_CONFIG_DRAM_ROUND_RATE);
+	if (!res.a0)
+		return res.a1;
+	else
+		return 0;
+}
+
+static const struct clk_ops rockchip_ddrclk_sip_ops_v2 = {
+	.recalc_rate = rockchip_ddrclk_sip_recalc_rate_v2,
+	.set_rate = rockchip_ddrclk_sip_set_rate_v2,
+	.round_rate = rockchip_ddrclk_sip_round_rate_v2,
+	.get_parent = rockchip_ddrclk_get_parent,
+};
+
 struct clk *rockchip_clk_register_ddrclk(const char *name, int flags,
 					 const char *const *parent_names,
 					 u8 num_parents, int mux_offset,
 					 int mux_shift, int mux_width,
 					 int div_shift, int div_width,
-					 int ddr_flag, void __iomem *reg_base,
-					 spinlock_t *lock)
+					 int ddr_flag, void __iomem *reg_base)
 {
 	struct rockchip_ddrclk *ddrclk;
 	struct clk_init_data init;
 	struct clk *clk;
 
+#ifdef CONFIG_ARM
+	if (!psci_smp_available())
+		return NULL;
+#endif
+
 	ddrclk = kzalloc(sizeof(*ddrclk), GFP_KERNEL);
 	if (!ddrclk)
 		return ERR_PTR(-ENOMEM);
@@ -114,6 +262,12 @@ struct clk *rockchip_clk_register_ddrclk(const char *name, int flags,
 	case ROCKCHIP_DDRCLK_SIP:
 		init.ops = &rockchip_ddrclk_sip_ops;
 		break;
+	case ROCKCHIP_DDRCLK_SCPI:
+		init.ops = &rockchip_ddrclk_scpi_ops;
+		break;
+	case ROCKCHIP_DDRCLK_SIP_V2:
+		init.ops = &rockchip_ddrclk_sip_ops_v2;
+		break;
 	default:
 		pr_err("%s: unsupported ddrclk type %d\n", __func__, ddr_flag);
 		kfree(ddrclk);
@@ -121,7 +275,6 @@ struct clk *rockchip_clk_register_ddrclk(const char *name, int flags,
 	}
 
 	ddrclk->reg_base = reg_base;
-	ddrclk->lock = lock;
 	ddrclk->hw.init = &init;
 	ddrclk->mux_offset = mux_offset;
 	ddrclk->mux_shift = mux_shift;
diff --git a/drivers/clk/rockchip/clk-half-divider.c b/drivers/clk/rockchip/clk-half-divider.c
index ccd5c270c..b978af08d 100644
--- a/drivers/clk/rockchip/clk-half-divider.c
+++ b/drivers/clk/rockchip/clk-half-divider.c
@@ -14,9 +14,9 @@ static bool _is_best_half_div(unsigned long rate, unsigned long now,
 			      unsigned long best, unsigned long flags)
 {
 	if (flags & CLK_DIVIDER_ROUND_CLOSEST)
-		return abs(rate - now) < abs(rate - best);
+		return abs(rate - now) <= abs(rate - best);
 
-	return now <= rate && now > best;
+	return now <= rate && now >= best;
 }
 
 static unsigned long clk_half_divider_recalc_rate(struct clk_hw *hw,
@@ -38,7 +38,7 @@ static int clk_half_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
 {
 	unsigned int i, bestdiv = 0;
 	unsigned long parent_rate, best = 0, now, maxdiv;
-	unsigned long parent_rate_saved = *best_parent_rate;
+	bool is_bestdiv = false;
 
 	if (!rate)
 		rate = 1;
@@ -51,7 +51,7 @@ static int clk_half_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
 		if (bestdiv < 3)
 			bestdiv = 0;
 		else
-			bestdiv = (bestdiv - 3) / 2;
+			bestdiv = DIV_ROUND_UP(bestdiv - 3, 2);
 		bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
 		return bestdiv;
 	}
@@ -63,28 +63,20 @@ static int clk_half_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
 	maxdiv = min(ULONG_MAX / rate, maxdiv);
 
 	for (i = 0; i <= maxdiv; i++) {
-		if (((u64)rate * (i * 2 + 3)) == ((u64)parent_rate_saved * 2)) {
-			/*
-			 * It's the most ideal case if the requested rate can be
-			 * divided from parent clock without needing to change
-			 * parent rate, so return the divider immediately.
-			 */
-			*best_parent_rate = parent_rate_saved;
-			return i;
-		}
 		parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw),
 						((u64)rate * (i * 2 + 3)) / 2);
 		now = DIV_ROUND_UP_ULL(((u64)parent_rate * 2),
 				       (i * 2 + 3));
 
 		if (_is_best_half_div(rate, now, best, flags)) {
+			is_bestdiv = true;
 			bestdiv = i;
 			best = now;
 			*best_parent_rate = parent_rate;
 		}
 	}
 
-	if (!bestdiv) {
+	if (!is_bestdiv) {
 		bestdiv = div_mask(width);
 		*best_parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), 1);
 	}
@@ -114,7 +106,7 @@ static int clk_half_divider_set_rate(struct clk_hw *hw, unsigned long rate,
 	u32 val;
 
 	value = DIV_ROUND_UP_ULL(((u64)parent_rate * 2), rate);
-	value = (value - 3) / 2;
+	value = DIV_ROUND_UP(value - 3, 2);
 	value =  min_t(unsigned int, value, div_mask(divider->width));
 
 	if (divider->lock)
@@ -160,10 +152,10 @@ struct clk *rockchip_clk_register_halfdiv(const char *name,
 					  u8 num_parents, void __iomem *base,
 					  int muxdiv_offset, u8 mux_shift,
 					  u8 mux_width, u8 mux_flags,
-					  u8 div_shift, u8 div_width,
-					  u8 div_flags, int gate_offset,
-					  u8 gate_shift, u8 gate_flags,
-					  unsigned long flags,
+					  int div_offset, u8 div_shift,
+					  u8 div_width, u8 div_flags,
+					  int gate_offset, u8 gate_shift,
+					  u8 gate_flags, unsigned long flags,
 					  spinlock_t *lock)
 {
 	struct clk_hw *hw = ERR_PTR(-ENOMEM);
@@ -205,7 +197,10 @@ struct clk *rockchip_clk_register_halfdiv(const char *name,
 			goto err_div;
 
 		div->flags = div_flags;
-		div->reg = base + muxdiv_offset;
+		if (div_offset)
+			div->reg = base + div_offset;
+		else
+			div->reg = base + muxdiv_offset;
 		div->shift = div_shift;
 		div->width = div_width;
 		div->lock = lock;
diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c
index d0bd513ff..5687b5d8f 100644
--- a/drivers/clk/rockchip/clk-pll.c
+++ b/drivers/clk/rockchip/clk-pll.c
@@ -15,6 +15,9 @@
 #include <linux/iopoll.h>
 #include <linux/regmap.h>
 #include <linux/clk.h>
+#include <linux/gcd.h>
+#include <linux/clk/rockchip.h>
+#include <linux/mfd/syscon.h>
 #include "clk.h"
 
 #define PLL_MODE_MASK		0x3
@@ -38,15 +41,352 @@ struct rockchip_clk_pll {
 	u8			flags;
 	const struct rockchip_pll_rate_table *rate_table;
 	unsigned int		rate_count;
+	int			sel;
+	unsigned long		scaling;
 	spinlock_t		*lock;
 
 	struct rockchip_clk_provider *ctx;
+
+	bool			boost_enabled;
+	u32			boost_backup_pll_usage;
+	unsigned long		boost_backup_pll_rate;
+	unsigned long		boost_low_rate;
+	unsigned long		boost_high_rate;
+	struct regmap		*boost;
+#ifdef CONFIG_DEBUG_FS
+	struct hlist_node	debug_node;
+#endif
 };
 
 #define to_rockchip_clk_pll(_hw) container_of(_hw, struct rockchip_clk_pll, hw)
 #define to_rockchip_clk_pll_nb(nb) \
 			container_of(nb, struct rockchip_clk_pll, clk_nb)
 
+static void rockchip_boost_disable_low(struct rockchip_clk_pll *pll);
+
+#define MHZ			(1000UL * 1000UL)
+#define KHZ			(1000UL)
+
+/* CLK_PLL_TYPE_RK3066_AUTO type ops */
+#define PLL_FREF_MIN		(269 * KHZ)
+#define PLL_FREF_MAX		(2200 * MHZ)
+
+#define PLL_FVCO_MIN		(440 * MHZ)
+#define PLL_FVCO_MAX		(2200 * MHZ)
+
+#define PLL_FOUT_MIN		(27500 * KHZ)
+#define PLL_FOUT_MAX		(2200 * MHZ)
+
+#define PLL_NF_MAX		(4096)
+#define PLL_NR_MAX		(64)
+#define PLL_NO_MAX		(16)
+
+/* CLK_PLL_TYPE_RK3036/3366/3399_AUTO type ops */
+#define MIN_FOUTVCO_FREQ	(800 * MHZ)
+#define MAX_FOUTVCO_FREQ	(2000 * MHZ)
+
+static struct rockchip_pll_rate_table auto_table;
+#ifdef CONFIG_DEBUG_FS
+static HLIST_HEAD(clk_boost_list);
+static DEFINE_MUTEX(clk_boost_lock);
+#endif
+
+int rockchip_pll_clk_adaptive_scaling(struct clk *clk, int sel)
+{
+	struct clk *parent = clk_get_parent(clk);
+	struct rockchip_clk_pll *pll;
+
+	if (IS_ERR_OR_NULL(parent))
+		return -EINVAL;
+
+	pll = to_rockchip_clk_pll(__clk_get_hw(parent));
+	if (!pll)
+		return -EINVAL;
+
+	pll->sel = sel;
+
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_pll_clk_adaptive_scaling);
+
+int rockchip_pll_clk_rate_to_scale(struct clk *clk, unsigned long rate)
+{
+	const struct rockchip_pll_rate_table *rate_table;
+	struct clk *parent = clk_get_parent(clk);
+	struct rockchip_clk_pll *pll;
+	unsigned int i;
+
+	if (IS_ERR_OR_NULL(parent))
+		return -EINVAL;
+
+	pll = to_rockchip_clk_pll(__clk_get_hw(parent));
+	if (!pll)
+		return -EINVAL;
+
+	rate_table = pll->rate_table;
+	for (i = 0; i < pll->rate_count; i++) {
+		if (rate >= rate_table[i].rate)
+			return i;
+	}
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(rockchip_pll_clk_rate_to_scale);
+
+int rockchip_pll_clk_scale_to_rate(struct clk *clk, unsigned int scale)
+{
+	const struct rockchip_pll_rate_table *rate_table;
+	struct clk *parent = clk_get_parent(clk);
+	struct rockchip_clk_pll *pll;
+	unsigned int i;
+
+	if (IS_ERR_OR_NULL(parent))
+		return -EINVAL;
+
+	pll = to_rockchip_clk_pll(__clk_get_hw(parent));
+	if (!pll)
+		return -EINVAL;
+
+	rate_table = pll->rate_table;
+	for (i = 0; i < pll->rate_count; i++) {
+		if (i == scale)
+			return rate_table[i].rate;
+	}
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(rockchip_pll_clk_scale_to_rate);
+
+static struct rockchip_pll_rate_table *rk_pll_rate_table_get(void)
+{
+	return &auto_table;
+}
+
+static int rockchip_pll_clk_set_postdiv(unsigned long fout_hz,
+					u32 *postdiv1,
+					u32 *postdiv2,
+					u32 *foutvco)
+{
+	unsigned long freq;
+
+	if (fout_hz < MIN_FOUTVCO_FREQ) {
+		for (*postdiv1 = 1; *postdiv1 <= 7; (*postdiv1)++) {
+			for (*postdiv2 = 1; *postdiv2 <= 7; (*postdiv2)++) {
+				freq = fout_hz * (*postdiv1) * (*postdiv2);
+				if (freq >= MIN_FOUTVCO_FREQ &&
+				    freq <= MAX_FOUTVCO_FREQ) {
+					*foutvco = freq;
+					return 0;
+				}
+			}
+		}
+		pr_err("CANNOT FIND postdiv1/2 to make fout in range from 800M to 2000M,fout = %lu\n",
+		       fout_hz);
+	} else {
+		*postdiv1 = 1;
+		*postdiv2 = 1;
+	}
+	return 0;
+}
+
+static struct rockchip_pll_rate_table *
+rockchip_pll_clk_set_by_auto(struct rockchip_clk_pll *pll,
+			     unsigned long fin_hz,
+			     unsigned long fout_hz)
+{
+	struct rockchip_pll_rate_table *rate_table = rk_pll_rate_table_get();
+	/* FIXME set postdiv1/2 always 1*/
+	u32 foutvco = fout_hz;
+	u64 fin_64, frac_64;
+	u32 f_frac, postdiv1, postdiv2;
+	unsigned long clk_gcd = 0;
+
+	if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz)
+		return NULL;
+
+	rockchip_pll_clk_set_postdiv(fout_hz, &postdiv1, &postdiv2, &foutvco);
+	rate_table->postdiv1 = postdiv1;
+	rate_table->postdiv2 = postdiv2;
+	rate_table->dsmpd = 1;
+
+	if (fin_hz / MHZ * MHZ == fin_hz && fout_hz / MHZ * MHZ == fout_hz) {
+		fin_hz /= MHZ;
+		foutvco /= MHZ;
+		clk_gcd = gcd(fin_hz, foutvco);
+		rate_table->refdiv = fin_hz / clk_gcd;
+		rate_table->fbdiv = foutvco / clk_gcd;
+
+		rate_table->frac = 0;
+
+		pr_debug("fin = %lu, fout = %lu, clk_gcd = %lu, refdiv = %u, fbdiv = %u, postdiv1 = %u, postdiv2 = %u, frac = %u\n",
+			 fin_hz, fout_hz, clk_gcd, rate_table->refdiv,
+			 rate_table->fbdiv, rate_table->postdiv1,
+			 rate_table->postdiv2, rate_table->frac);
+	} else {
+		pr_debug("frac div running, fin_hz = %lu, fout_hz = %lu, fin_INT_mhz = %lu, fout_INT_mhz = %lu\n",
+			 fin_hz, fout_hz,
+			 fin_hz / MHZ * MHZ,
+			 fout_hz / MHZ * MHZ);
+		pr_debug("frac get postdiv1 = %u,  postdiv2 = %u, foutvco = %u\n",
+			 rate_table->postdiv1, rate_table->postdiv2, foutvco);
+		clk_gcd = gcd(fin_hz / MHZ, foutvco / MHZ);
+		rate_table->refdiv = fin_hz / MHZ / clk_gcd;
+		rate_table->fbdiv = foutvco / MHZ / clk_gcd;
+		pr_debug("frac get refdiv = %u,  fbdiv = %u\n",
+			 rate_table->refdiv, rate_table->fbdiv);
+
+		rate_table->frac = 0;
+
+		f_frac = (foutvco % MHZ);
+		fin_64 = fin_hz;
+		do_div(fin_64, (u64)rate_table->refdiv);
+		frac_64 = (u64)f_frac << 24;
+		do_div(frac_64, fin_64);
+		rate_table->frac = (u32)frac_64;
+		if (rate_table->frac > 0)
+			rate_table->dsmpd = 0;
+		pr_debug("frac = %x\n", rate_table->frac);
+	}
+	return rate_table;
+}
+
+static struct rockchip_pll_rate_table *
+rockchip_rk3066_pll_clk_set_by_auto(struct rockchip_clk_pll *pll,
+				    unsigned long fin_hz,
+				    unsigned long fout_hz)
+{
+	struct rockchip_pll_rate_table *rate_table = rk_pll_rate_table_get();
+	u32 nr, nf, no, nonr;
+	u32 nr_out, nf_out, no_out;
+	u32 n;
+	u32 numerator, denominator;
+	u64 fref, fvco, fout;
+	unsigned long clk_gcd = 0;
+
+	nr_out = PLL_NR_MAX + 1;
+	no_out = 0;
+	nf_out = 0;
+
+	if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz)
+		return NULL;
+
+	clk_gcd = gcd(fin_hz, fout_hz);
+
+	numerator = fout_hz / clk_gcd;
+	denominator = fin_hz / clk_gcd;
+
+	for (n = 1;; n++) {
+		nf = numerator * n;
+		nonr = denominator * n;
+		if (nf > PLL_NF_MAX || nonr > (PLL_NO_MAX * PLL_NR_MAX))
+			break;
+
+		for (no = 1; no <= PLL_NO_MAX; no++) {
+			if (!(no == 1 || !(no % 2)))
+				continue;
+
+			if (nonr % no)
+				continue;
+			nr = nonr / no;
+
+			if (nr > PLL_NR_MAX)
+				continue;
+
+			fref = fin_hz / nr;
+			if (fref < PLL_FREF_MIN || fref > PLL_FREF_MAX)
+				continue;
+
+			fvco = fref * nf;
+			if (fvco < PLL_FVCO_MIN || fvco > PLL_FVCO_MAX)
+				continue;
+
+			fout = fvco / no;
+			if (fout < PLL_FOUT_MIN || fout > PLL_FOUT_MAX)
+				continue;
+
+			/* select the best from all available PLL settings */
+			if ((no > no_out) ||
+			    ((no == no_out) && (nr < nr_out))) {
+				nr_out = nr;
+				nf_out = nf;
+				no_out = no;
+			}
+		}
+	}
+
+	/* output the best PLL setting */
+	if ((nr_out <= PLL_NR_MAX) && (no_out > 0)) {
+		rate_table->nr = nr_out;
+		rate_table->nf = nf_out;
+		rate_table->no = no_out;
+	} else {
+		return NULL;
+	}
+
+	return rate_table;
+}
+
+static struct rockchip_pll_rate_table *
+rockchip_rk3588_pll_clk_set_by_auto(struct rockchip_clk_pll *pll,
+				    unsigned long fin_hz,
+				    unsigned long fout_hz)
+{
+	struct rockchip_pll_rate_table *rate_table = rk_pll_rate_table_get();
+	u64 fvco_min = 2250 * MHZ, fvco_max = 4500 * MHZ;
+	u64 fout_min = 37 * MHZ, fout_max = 4500 * MHZ;
+	u32 p, m, s;
+	u64 fvco, fref, fout, ffrac;
+
+	if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz)
+		return NULL;
+
+	if (fout_hz > fout_max || fout_hz < fout_min)
+		return NULL;
+
+	if (fin_hz / MHZ * MHZ == fin_hz && fout_hz / MHZ * MHZ == fout_hz) {
+		for (s = 0; s <= 6; s++) {
+			fvco = fout_hz << s;
+			if (fvco < fvco_min || fvco > fvco_max)
+				continue;
+			for (p = 2; p <= 4; p++) {
+				for (m = 64; m <= 1023; m++) {
+					if (fvco == m * fin_hz / p) {
+						rate_table->p = p;
+						rate_table->m = m;
+						rate_table->s = s;
+						rate_table->k = 0;
+						return rate_table;
+					}
+				}
+			}
+		}
+		pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz);
+	} else {
+		fout = (fout_hz / MHZ) * MHZ;
+		ffrac = (fout_hz % MHZ);
+		for (s = 0; s <= 6; s++) {
+			fvco = fout << s;
+			if (fvco < fvco_min || fvco > fvco_max)
+				continue;
+			for (p = 1; p <= 4; p++) {
+				for (m = 64; m <= 1023; m++) {
+					if (fvco == m * fin_hz / p) {
+						rate_table->p = p;
+						rate_table->m = m;
+						rate_table->s = s;
+						fref = fin_hz / p;
+						fout = (ffrac << s) * 65535;
+						rate_table->k = fout / fref;
+						return rate_table;
+					}
+				}
+			}
+		}
+		pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz);
+	}
+	return NULL;
+}
+
 static const struct rockchip_pll_rate_table *rockchip_get_pll_settings(
 			    struct rockchip_clk_pll *pll, unsigned long rate)
 {
@@ -54,28 +394,29 @@ static const struct rockchip_pll_rate_table *rockchip_get_pll_settings(
 	int i;
 
 	for (i = 0; i < pll->rate_count; i++) {
-		if (rate == rate_table[i].rate)
+		if (rate == rate_table[i].rate) {
+			if (i < pll->sel) {
+				pll->scaling = rate;
+				return &rate_table[pll->sel];
+			}
+			pll->scaling = 0;
 			return &rate_table[i];
+		}
 	}
+	pll->scaling = 0;
 
-	return NULL;
+	if (pll->type == pll_rk3066)
+		return rockchip_rk3066_pll_clk_set_by_auto(pll, 24 * MHZ, rate);
+	else if (pll->type == pll_rk3588 || pll->type == pll_rk3588_core)
+		return rockchip_rk3588_pll_clk_set_by_auto(pll, 24 * MHZ, rate);
+	else
+		return rockchip_pll_clk_set_by_auto(pll, 24 * MHZ, rate);
 }
 
 static long rockchip_pll_round_rate(struct clk_hw *hw,
 			    unsigned long drate, unsigned long *prate)
 {
-	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
-	const struct rockchip_pll_rate_table *rate_table = pll->rate_table;
-	int i;
-
-	/* Assumming rate_table is in descending order */
-	for (i = 0; i < pll->rate_count; i++) {
-		if (drate >= rate_table[i].rate)
-			return rate_table[i].rate;
-	}
-
-	/* return minimum supported value */
-	return rate_table[i - 1].rate;
+	return drate;
 }
 
 /*
@@ -136,6 +477,30 @@ static int rockchip_rk3036_pll_wait_lock(struct rockchip_clk_pll *pll)
 	return ret;
 }
 
+static unsigned long
+rockchip_rk3036_pll_con_to_rate(struct rockchip_clk_pll *pll,
+				u32 con0, u32 con1)
+{
+	unsigned int fbdiv, postdiv1, refdiv, postdiv2;
+	u64 rate64 = 24000000;
+
+	fbdiv = ((con0 >> RK3036_PLLCON0_FBDIV_SHIFT) &
+		  RK3036_PLLCON0_FBDIV_MASK);
+	postdiv1 = ((con0 >> RK3036_PLLCON0_POSTDIV1_SHIFT) &
+		     RK3036_PLLCON0_POSTDIV1_MASK);
+	refdiv = ((con1 >> RK3036_PLLCON1_REFDIV_SHIFT) &
+		   RK3036_PLLCON1_REFDIV_MASK);
+	postdiv2 = ((con1 >> RK3036_PLLCON1_POSTDIV2_SHIFT) &
+		     RK3036_PLLCON1_POSTDIV2_MASK);
+
+	rate64 *= fbdiv;
+	do_div(rate64, refdiv);
+	do_div(rate64, postdiv1);
+	do_div(rate64, postdiv2);
+
+	return (unsigned long)rate64;
+}
+
 static void rockchip_rk3036_pll_get_params(struct rockchip_clk_pll *pll,
 					struct rockchip_pll_rate_table *rate)
 {
@@ -165,7 +530,10 @@ static unsigned long rockchip_rk3036_pll_recalc_rate(struct clk_hw *hw,
 {
 	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
 	struct rockchip_pll_rate_table cur;
-	u64 rate64 = prate;
+	u64 rate64 = prate, frac_rate64 = prate;
+
+	if (pll->sel && pll->scaling)
+		return pll->scaling;
 
 	rockchip_rk3036_pll_get_params(pll, &cur);
 
@@ -174,7 +542,7 @@ static unsigned long rockchip_rk3036_pll_recalc_rate(struct clk_hw *hw,
 
 	if (cur.dsmpd == 0) {
 		/* fractional mode */
-		u64 frac_rate64 = prate * cur.frac;
+		frac_rate64 *= cur.frac;
 
 		do_div(frac_rate64, cur.refdiv);
 		rate64 += frac_rate64 >> 24;
@@ -231,6 +599,8 @@ static int rockchip_rk3036_pll_set_params(struct rockchip_clk_pll *pll,
 	pllcon |= rate->frac << RK3036_PLLCON2_FRAC_SHIFT;
 	writel_relaxed(pllcon, pll->reg_base + RK3036_PLLCON(2));
 
+	rockchip_boost_disable_low(pll);
+
 	/* wait for the pll to lock */
 	ret = rockchip_rk3036_pll_wait_lock(pll);
 	if (ret) {
@@ -412,6 +782,9 @@ static unsigned long rockchip_rk3066_pll_recalc_rate(struct clk_hw *hw,
 		return prate;
 	}
 
+	if (pll->sel && pll->scaling)
+		return pll->scaling;
+
 	rockchip_rk3066_pll_get_params(pll, &cur);
 
 	rate64 *= cur.nf;
@@ -485,9 +858,18 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate,
 {
 	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
 	const struct rockchip_pll_rate_table *rate;
+	unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate);
+	struct regmap *grf = pll->ctx->grf;
+	int ret;
 
-	pr_debug("%s: changing %s to %lu with a parent rate of %lu\n",
-		 __func__, clk_hw_get_name(hw), drate, prate);
+	if (IS_ERR(grf)) {
+		pr_debug("%s: grf regmap not available, aborting rate change\n",
+			 __func__);
+		return PTR_ERR(grf);
+	}
+
+	pr_debug("%s: changing %s from %lu to %lu with a parent rate of %lu\n",
+		 __func__, clk_hw_get_name(hw), old_rate, drate, prate);
 
 	/* Get required rate settings from table */
 	rate = rockchip_get_pll_settings(pll, drate);
@@ -497,7 +879,11 @@ static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate,
 		return -EINVAL;
 	}
 
-	return rockchip_rk3066_pll_set_params(pll, rate);
+	ret = rockchip_rk3066_pll_set_params(pll, rate);
+	if (ret)
+		pll->scaling = 0;
+
+	return ret;
 }
 
 static int rockchip_rk3066_pll_enable(struct clk_hw *hw)
@@ -649,6 +1035,9 @@ static unsigned long rockchip_rk3399_pll_recalc_rate(struct clk_hw *hw,
 	struct rockchip_pll_rate_table cur;
 	u64 rate64 = prate;
 
+	if (pll->sel && pll->scaling)
+		return pll->scaling;
+
 	rockchip_rk3399_pll_get_params(pll, &cur);
 
 	rate64 *= cur.fbdiv;
@@ -692,6 +1081,11 @@ static int rockchip_rk3399_pll_set_params(struct rockchip_clk_pll *pll,
 		rate_change_remuxed = 1;
 	}
 
+	/* set pll power down */
+	writel(HIWORD_UPDATE(RK3399_PLLCON3_PWRDOWN,
+			     RK3399_PLLCON3_PWRDOWN, 0),
+	       pll->reg_base + RK3399_PLLCON(3));
+
 	/* update pll values */
 	writel_relaxed(HIWORD_UPDATE(rate->fbdiv, RK3399_PLLCON0_FBDIV_MASK,
 						  RK3399_PLLCON0_FBDIV_SHIFT),
@@ -715,6 +1109,11 @@ static int rockchip_rk3399_pll_set_params(struct rockchip_clk_pll *pll,
 					    RK3399_PLLCON3_DSMPD_SHIFT),
 		       pll->reg_base + RK3399_PLLCON(3));
 
+	/* set pll power up */
+	writel(HIWORD_UPDATE(0,
+			     RK3399_PLLCON3_PWRDOWN, 0),
+	       pll->reg_base + RK3399_PLLCON(3));
+
 	/* wait for the pll to lock */
 	ret = rockchip_rk3399_pll_wait_lock(pll);
 	if (ret) {
@@ -734,9 +1133,11 @@ static int rockchip_rk3399_pll_set_rate(struct clk_hw *hw, unsigned long drate,
 {
 	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
 	const struct rockchip_pll_rate_table *rate;
+	unsigned long old_rate = rockchip_rk3399_pll_recalc_rate(hw, prate);
+	int ret;
 
-	pr_debug("%s: changing %s to %lu with a parent rate of %lu\n",
-		 __func__, __clk_get_name(hw->clk), drate, prate);
+	pr_debug("%s: changing %s from %lu to %lu with a parent rate of %lu\n",
+		 __func__, __clk_get_name(hw->clk), old_rate, drate, prate);
 
 	/* Get required rate settings from table */
 	rate = rockchip_get_pll_settings(pll, drate);
@@ -746,7 +1147,11 @@ static int rockchip_rk3399_pll_set_rate(struct clk_hw *hw, unsigned long drate,
 		return -EINVAL;
 	}
 
-	return rockchip_rk3399_pll_set_params(pll, rate);
+	ret = rockchip_rk3399_pll_set_params(pll, rate);
+	if (ret)
+		pll->scaling = 0;
+
+	return ret;
 }
 
 static int rockchip_rk3399_pll_enable(struct clk_hw *hw)
@@ -842,6 +1247,307 @@ static const struct clk_ops rockchip_rk3399_pll_clk_ops = {
 	.init = rockchip_rk3399_pll_init,
 };
 
+/**
+ * PLL used in RK3588
+ */
+
+#define RK3588_PLLCON(i)		(i * 0x4)
+#define RK3588_PLLCON0_M_MASK		0x3ff
+#define RK3588_PLLCON0_M_SHIFT		0
+#define RK3588_PLLCON1_P_MASK		0x3f
+#define RK3588_PLLCON1_P_SHIFT		0
+#define RK3588_PLLCON1_S_MASK		0x7
+#define RK3588_PLLCON1_S_SHIFT		6
+#define RK3588_PLLCON2_K_MASK		0xffff
+#define RK3588_PLLCON2_K_SHIFT		0
+#define RK3588_PLLCON1_PWRDOWN		BIT(13)
+#define RK3588_PLLCON6_LOCK_STATUS	BIT(15)
+
+static int rockchip_rk3588_pll_wait_lock(struct rockchip_clk_pll *pll)
+{
+	u32 pllcon;
+	int ret;
+
+	/*
+	 * Lock time typical 250, max 500 input clock cycles @24MHz
+	 * So define a very safe maximum of 1000us, meaning 24000 cycles.
+	 */
+	ret = readl_relaxed_poll_timeout(pll->reg_base + RK3588_PLLCON(6),
+					 pllcon,
+					 pllcon & RK3588_PLLCON6_LOCK_STATUS,
+					 0, 1000);
+	if (ret)
+		pr_err("%s: timeout waiting for pll to lock\n", __func__);
+
+	return ret;
+}
+
+static void rockchip_rk3588_pll_get_params(struct rockchip_clk_pll *pll,
+					struct rockchip_pll_rate_table *rate)
+{
+	u32 pllcon;
+
+	pllcon = readl_relaxed(pll->reg_base + RK3588_PLLCON(0));
+	rate->m = ((pllcon >> RK3588_PLLCON0_M_SHIFT)
+				& RK3588_PLLCON0_M_MASK);
+
+	pllcon = readl_relaxed(pll->reg_base + RK3588_PLLCON(1));
+	rate->p = ((pllcon >> RK3588_PLLCON1_P_SHIFT)
+				& RK3588_PLLCON1_P_MASK);
+	rate->s = ((pllcon >> RK3588_PLLCON1_S_SHIFT)
+				& RK3588_PLLCON1_S_MASK);
+
+	pllcon = readl_relaxed(pll->reg_base + RK3588_PLLCON(2));
+	rate->k = ((pllcon >> RK3588_PLLCON2_K_SHIFT)
+				& RK3588_PLLCON2_K_MASK);
+}
+
+static unsigned long rockchip_rk3588_pll_recalc_rate(struct clk_hw *hw,
+						     unsigned long prate)
+{
+	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+	struct rockchip_pll_rate_table cur;
+	u64 rate64 = prate, postdiv;
+
+	if (pll->sel && pll->scaling)
+		return pll->scaling;
+
+	rockchip_rk3588_pll_get_params(pll, &cur);
+
+	rate64 *= cur.m;
+	do_div(rate64, cur.p);
+
+	if (cur.k) {
+		/* fractional mode */
+		u64 frac_rate64 = prate * cur.k;
+
+		postdiv = cur.p * 65535;
+		do_div(frac_rate64, postdiv);
+		rate64 += frac_rate64;
+	}
+	rate64 = rate64 >> cur.s;
+
+	return (unsigned long)rate64;
+}
+
+static int rockchip_rk3588_pll_set_params(struct rockchip_clk_pll *pll,
+				const struct rockchip_pll_rate_table *rate)
+{
+	const struct clk_ops *pll_mux_ops = pll->pll_mux_ops;
+	struct clk_mux *pll_mux = &pll->pll_mux;
+	struct rockchip_pll_rate_table cur;
+	int rate_change_remuxed = 0;
+	int cur_parent;
+	int ret;
+
+	pr_debug("%s: rate settings for %lu p: %d, m: %d, s: %d, k: %d\n",
+		__func__, rate->rate, rate->p, rate->m, rate->s, rate->k);
+
+	rockchip_rk3588_pll_get_params(pll, &cur);
+	cur.rate = 0;
+
+	if (pll->type == pll_rk3588) {
+		cur_parent = pll_mux_ops->get_parent(&pll_mux->hw);
+		if (cur_parent == PLL_MODE_NORM) {
+			pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW);
+			rate_change_remuxed = 1;
+		}
+	}
+
+	/* set pll power down */
+	writel(HIWORD_UPDATE(RK3588_PLLCON1_PWRDOWN,
+			     RK3588_PLLCON1_PWRDOWN, 0),
+	       pll->reg_base + RK3399_PLLCON(1));
+
+	/* update pll values */
+	writel_relaxed(HIWORD_UPDATE(rate->m, RK3588_PLLCON0_M_MASK,
+						  RK3588_PLLCON0_M_SHIFT),
+		       pll->reg_base + RK3399_PLLCON(0));
+
+	writel_relaxed(HIWORD_UPDATE(rate->p, RK3588_PLLCON1_P_MASK,
+						   RK3588_PLLCON1_P_SHIFT) |
+		       HIWORD_UPDATE(rate->s, RK3588_PLLCON1_S_MASK,
+						     RK3588_PLLCON1_S_SHIFT),
+		       pll->reg_base + RK3399_PLLCON(1));
+
+	writel_relaxed(HIWORD_UPDATE(rate->k, RK3588_PLLCON2_K_MASK,
+				     RK3588_PLLCON2_K_SHIFT),
+		       pll->reg_base + RK3399_PLLCON(2));
+
+	/* set pll power up */
+	writel(HIWORD_UPDATE(0,
+			     RK3588_PLLCON1_PWRDOWN, 0),
+	       pll->reg_base + RK3588_PLLCON(1));
+
+	/* wait for the pll to lock */
+	ret = rockchip_rk3588_pll_wait_lock(pll);
+	if (ret) {
+		pr_warn("%s: pll update unsuccessful, trying to restore old params\n",
+			__func__);
+		rockchip_rk3588_pll_set_params(pll, &cur);
+	}
+
+	if ((pll->type == pll_rk3588) && rate_change_remuxed)
+		pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM);
+
+	return ret;
+}
+
+static int rockchip_rk3588_pll_set_rate(struct clk_hw *hw, unsigned long drate,
+					unsigned long prate)
+{
+	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+	const struct rockchip_pll_rate_table *rate;
+	unsigned long old_rate = rockchip_rk3588_pll_recalc_rate(hw, prate);
+	int ret;
+
+	pr_debug("%s: changing %s from %lu to %lu with a parent rate of %lu\n",
+		 __func__, __clk_get_name(hw->clk), old_rate, drate, prate);
+
+	/* Get required rate settings from table */
+	rate = rockchip_get_pll_settings(pll, drate);
+	if (!rate) {
+		pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
+			drate, __clk_get_name(hw->clk));
+		return -EINVAL;
+	}
+
+	ret = rockchip_rk3588_pll_set_params(pll, rate);
+	if (ret)
+		pll->scaling = 0;
+
+	return ret;
+}
+
+static int rockchip_rk3588_pll_enable(struct clk_hw *hw)
+{
+	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+
+	writel(HIWORD_UPDATE(0, RK3588_PLLCON1_PWRDOWN, 0),
+	       pll->reg_base + RK3588_PLLCON(1));
+	rockchip_rk3588_pll_wait_lock(pll);
+
+	return 0;
+}
+
+static void rockchip_rk3588_pll_disable(struct clk_hw *hw)
+{
+	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+
+	writel(HIWORD_UPDATE(RK3588_PLLCON1_PWRDOWN,
+			     RK3588_PLLCON1_PWRDOWN, 0),
+	       pll->reg_base + RK3588_PLLCON(1));
+}
+
+static int rockchip_rk3588_pll_is_enabled(struct clk_hw *hw)
+{
+	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+	u32 pllcon = readl(pll->reg_base + RK3588_PLLCON(1));
+
+	return !(pllcon & RK3588_PLLCON1_PWRDOWN);
+}
+
+static int rockchip_rk3588_pll_init(struct clk_hw *hw)
+{
+	struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+
+	if (!(pll->flags & ROCKCHIP_PLL_SYNC_RATE))
+		return 0;
+
+	return 0;
+}
+
+static const struct clk_ops rockchip_rk3588_pll_clk_norate_ops = {
+	.recalc_rate = rockchip_rk3588_pll_recalc_rate,
+	.enable = rockchip_rk3588_pll_enable,
+	.disable = rockchip_rk3588_pll_disable,
+	.is_enabled = rockchip_rk3588_pll_is_enabled,
+};
+
+static const struct clk_ops rockchip_rk3588_pll_clk_ops = {
+	.recalc_rate = rockchip_rk3588_pll_recalc_rate,
+	.round_rate = rockchip_pll_round_rate,
+	.set_rate = rockchip_rk3588_pll_set_rate,
+	.enable = rockchip_rk3588_pll_enable,
+	.disable = rockchip_rk3588_pll_disable,
+	.is_enabled = rockchip_rk3588_pll_is_enabled,
+	.init = rockchip_rk3588_pll_init,
+};
+
+#ifdef CONFIG_ROCKCHIP_CLK_COMPENSATION
+int rockchip_pll_clk_compensation(struct clk *clk, int ppm)
+{
+	struct clk *parent = clk_get_parent(clk);
+	struct rockchip_clk_pll *pll;
+	static u32 frac, fbdiv;
+	bool negative;
+	u32 pllcon, pllcon0, pllcon2, fbdiv_mask, frac_mask, frac_shift;
+	u64 fracdiv, m, n;
+
+	if ((ppm > 1000) || (ppm < -1000))
+		return -EINVAL;
+
+	if (IS_ERR_OR_NULL(parent))
+		return -EINVAL;
+
+	pll = to_rockchip_clk_pll(__clk_get_hw(parent));
+	if (!pll)
+		return -EINVAL;
+
+	switch (pll->type) {
+	case pll_rk3036:
+	case pll_rk3328:
+		pllcon0 = RK3036_PLLCON(0);
+		pllcon2 = RK3036_PLLCON(2);
+		fbdiv_mask = RK3036_PLLCON0_FBDIV_MASK;
+		frac_mask = RK3036_PLLCON2_FRAC_MASK;
+		frac_shift = RK3036_PLLCON2_FRAC_SHIFT;
+		break;
+	case pll_rk3066:
+		return -EINVAL;
+	case pll_rk3399:
+		pllcon0 = RK3399_PLLCON(0);
+		pllcon2 = RK3399_PLLCON(2);
+		fbdiv_mask = RK3399_PLLCON0_FBDIV_MASK;
+		frac_mask = RK3399_PLLCON2_FRAC_MASK;
+		frac_shift = RK3399_PLLCON2_FRAC_SHIFT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	negative = !!(ppm & BIT(31));
+	ppm = negative ? ~ppm + 1 : ppm;
+
+	if (!frac) {
+		frac = readl_relaxed(pll->reg_base + pllcon2) & frac_mask;
+		fbdiv = readl_relaxed(pll->reg_base + pllcon0) & fbdiv_mask;
+	}
+
+	/*
+	 *   delta frac                 frac          ppm
+	 * -------------- = (fbdiv + ----------) * ---------
+	 *    1 << 24                 1 << 24       1000000
+	 *
+	 */
+	m = div64_u64((uint64_t)frac * ppm, 1000000);
+	n = div64_u64((uint64_t)ppm << 24, 1000000) * fbdiv;
+
+	fracdiv = negative ? frac - (m + n) : frac + (m + n);
+
+	if (!frac || fracdiv > frac_mask)
+		return -EINVAL;
+
+	pllcon = readl_relaxed(pll->reg_base + pllcon2);
+	pllcon &= ~(frac_mask << frac_shift);
+	pllcon |= fracdiv << frac_shift;
+	writel_relaxed(pllcon, pll->reg_base + pllcon2);
+
+	return  0;
+}
+EXPORT_SYMBOL(rockchip_pll_clk_compensation);
+#endif
+
 /*
  * Common registering of pll clocks
  */
@@ -890,7 +1596,8 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
 	if (pll_type == pll_rk3036 ||
 	    pll_type == pll_rk3066 ||
 	    pll_type == pll_rk3328 ||
-	    pll_type == pll_rk3399)
+	    pll_type == pll_rk3399 ||
+	    pll_type == pll_rk3588)
 		pll_mux->flags |= CLK_MUX_HIWORD_MASK;
 
 	/* the actual muxing is xin24m, pll-output, xin32k */
@@ -914,8 +1621,12 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
 	/* now create the actual pll */
 	init.name = pll_name;
 
+#ifndef CONFIG_ROCKCHIP_LOW_PERFORMANCE
 	/* keep all plls untouched for now */
 	init.flags = flags | CLK_IGNORE_UNUSED;
+#else
+	init.flags = flags;
+#endif
 
 	init.parent_names = &parent_names[0];
 	init.num_parents = 1;
@@ -940,7 +1651,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
 	switch (pll_type) {
 	case pll_rk3036:
 	case pll_rk3328:
-		if (!pll->rate_table)
+		if (!pll->rate_table || IS_ERR(ctx->grf))
 			init.ops = &rockchip_rk3036_pll_clk_norate_ops;
 		else
 			init.ops = &rockchip_rk3036_pll_clk_ops;
@@ -957,6 +1668,14 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
 		else
 			init.ops = &rockchip_rk3399_pll_clk_ops;
 		break;
+	case pll_rk3588:
+	case pll_rk3588_core:
+		if (!pll->rate_table)
+			init.ops = &rockchip_rk3588_pll_clk_norate_ops;
+		else
+			init.ops = &rockchip_rk3588_pll_clk_ops;
+		init.flags = flags;
+		break;
 	default:
 		pr_warn("%s: Unknown pll type for pll clk %s\n",
 			__func__, name);
@@ -988,3 +1707,316 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
 	kfree(pll);
 	return mux_clk;
 }
+
+static unsigned long rockchip_pll_con_to_rate(struct rockchip_clk_pll *pll,
+					      u32 con0, u32 con1)
+{
+	switch (pll->type) {
+	case pll_rk3036:
+	case pll_rk3328:
+		return rockchip_rk3036_pll_con_to_rate(pll, con0, con1);
+	case pll_rk3066:
+		break;
+	case pll_rk3399:
+		break;
+	default:
+		pr_warn("%s: Unknown pll type\n", __func__);
+	}
+
+	return 0;
+}
+
+void rockchip_boost_init(struct clk_hw *hw)
+{
+	struct rockchip_clk_pll *pll;
+	struct device_node *np;
+	u32 value, con0, con1;
+
+	if (!hw)
+		return;
+	pll = to_rockchip_clk_pll(hw);
+	np = of_parse_phandle(pll->ctx->cru_node, "rockchip,boost", 0);
+	if (!np) {
+		pr_debug("%s: failed to get boost np\n", __func__);
+		return;
+	}
+	pll->boost = syscon_node_to_regmap(np);
+	if (IS_ERR(pll->boost)) {
+		pr_debug("%s: failed to get boost regmap\n", __func__);
+		return;
+	}
+
+	if (!of_property_read_u32(np, "rockchip,boost-low-con0", &con0) &&
+	    !of_property_read_u32(np, "rockchip,boost-low-con1", &con1)) {
+		pr_debug("boost-low-con=0x%x 0x%x\n", con0, con1);
+		regmap_write(pll->boost, BOOST_PLL_L_CON(0),
+			     HIWORD_UPDATE(con0, BOOST_PLL_CON_MASK, 0));
+		regmap_write(pll->boost, BOOST_PLL_L_CON(1),
+			     HIWORD_UPDATE(con1, BOOST_PLL_CON_MASK, 0));
+		pll->boost_low_rate = rockchip_pll_con_to_rate(pll, con0,
+							       con1);
+		pr_debug("boost-low-rate=%lu\n", pll->boost_low_rate);
+	}
+	if (!of_property_read_u32(np, "rockchip,boost-high-con0", &con0) &&
+	    !of_property_read_u32(np, "rockchip,boost-high-con1", &con1)) {
+		pr_debug("boost-high-con=0x%x 0x%x\n", con0, con1);
+		regmap_write(pll->boost, BOOST_PLL_H_CON(0),
+			     HIWORD_UPDATE(con0, BOOST_PLL_CON_MASK, 0));
+		regmap_write(pll->boost, BOOST_PLL_H_CON(1),
+			     HIWORD_UPDATE(con1, BOOST_PLL_CON_MASK, 0));
+		pll->boost_high_rate = rockchip_pll_con_to_rate(pll, con0,
+								con1);
+		pr_debug("boost-high-rate=%lu\n", pll->boost_high_rate);
+	}
+	if (!of_property_read_u32(np, "rockchip,boost-backup-pll", &value)) {
+		pr_debug("boost-backup-pll=0x%x\n", value);
+		regmap_write(pll->boost, BOOST_CLK_CON,
+			     HIWORD_UPDATE(value, BOOST_BACKUP_PLL_MASK,
+					   BOOST_BACKUP_PLL_SHIFT));
+	}
+	if (!of_property_read_u32(np, "rockchip,boost-backup-pll-usage",
+				  &pll->boost_backup_pll_usage)) {
+		pr_debug("boost-backup-pll-usage=0x%x\n",
+			 pll->boost_backup_pll_usage);
+		regmap_write(pll->boost, BOOST_CLK_CON,
+			     HIWORD_UPDATE(pll->boost_backup_pll_usage,
+					   BOOST_BACKUP_PLL_USAGE_MASK,
+					   BOOST_BACKUP_PLL_USAGE_SHIFT));
+	}
+	if (!of_property_read_u32(np, "rockchip,boost-switch-threshold",
+				  &value)) {
+		pr_debug("boost-switch-threshold=0x%x\n", value);
+		regmap_write(pll->boost, BOOST_SWITCH_THRESHOLD, value);
+	}
+	if (!of_property_read_u32(np, "rockchip,boost-statis-threshold",
+				  &value)) {
+		pr_debug("boost-statis-threshold=0x%x\n", value);
+		regmap_write(pll->boost, BOOST_STATIS_THRESHOLD, value);
+	}
+	if (!of_property_read_u32(np, "rockchip,boost-statis-enable",
+				  &value)) {
+		pr_debug("boost-statis-enable=0x%x\n", value);
+		regmap_write(pll->boost, BOOST_BOOST_CON,
+			     HIWORD_UPDATE(value, BOOST_STATIS_ENABLE_MASK,
+					   BOOST_STATIS_ENABLE_SHIFT));
+	}
+	if (!of_property_read_u32(np, "rockchip,boost-enable", &value)) {
+		pr_debug("boost-enable=0x%x\n", value);
+		regmap_write(pll->boost, BOOST_BOOST_CON,
+			     HIWORD_UPDATE(value, BOOST_ENABLE_MASK,
+					   BOOST_ENABLE_SHIFT));
+		if (value)
+			pll->boost_enabled = true;
+	}
+#ifdef CONFIG_DEBUG_FS
+	if (pll->boost_enabled) {
+		mutex_lock(&clk_boost_lock);
+		hlist_add_head(&pll->debug_node, &clk_boost_list);
+		mutex_unlock(&clk_boost_lock);
+	}
+#endif
+}
+
+void rockchip_boost_enable_recovery_sw_low(struct clk_hw *hw)
+{
+	struct rockchip_clk_pll *pll;
+	unsigned int val;
+
+	if (!hw)
+		return;
+	pll = to_rockchip_clk_pll(hw);
+	if (!pll->boost_enabled)
+		return;
+
+	regmap_write(pll->boost, BOOST_BOOST_CON,
+		     HIWORD_UPDATE(1, BOOST_RECOVERY_MASK,
+				   BOOST_RECOVERY_SHIFT));
+	do {
+		regmap_read(pll->boost, BOOST_FSM_STATUS, &val);
+	} while (!(val & BOOST_BUSY_STATE));
+
+	regmap_write(pll->boost, BOOST_BOOST_CON,
+		     HIWORD_UPDATE(1, BOOST_SW_CTRL_MASK,
+				   BOOST_SW_CTRL_SHIFT) |
+		     HIWORD_UPDATE(1, BOOST_LOW_FREQ_EN_MASK,
+				   BOOST_LOW_FREQ_EN_SHIFT));
+}
+
+static void rockchip_boost_disable_low(struct rockchip_clk_pll *pll)
+{
+	if (!pll->boost_enabled)
+		return;
+
+	regmap_write(pll->boost, BOOST_BOOST_CON,
+		     HIWORD_UPDATE(0, BOOST_LOW_FREQ_EN_MASK,
+				   BOOST_LOW_FREQ_EN_SHIFT));
+}
+
+void rockchip_boost_disable_recovery_sw(struct clk_hw *hw)
+{
+	struct rockchip_clk_pll *pll;
+
+	if (!hw)
+		return;
+	pll = to_rockchip_clk_pll(hw);
+	if (!pll->boost_enabled)
+		return;
+
+	regmap_write(pll->boost, BOOST_BOOST_CON,
+		     HIWORD_UPDATE(0, BOOST_RECOVERY_MASK,
+				   BOOST_RECOVERY_SHIFT));
+	regmap_write(pll->boost, BOOST_BOOST_CON,
+		     HIWORD_UPDATE(0, BOOST_SW_CTRL_MASK,
+				   BOOST_SW_CTRL_SHIFT));
+}
+
+void rockchip_boost_add_core_div(struct clk_hw *hw, unsigned long prate)
+{
+	struct rockchip_clk_pll *pll;
+	unsigned int div;
+
+	if (!hw)
+		return;
+	pll = to_rockchip_clk_pll(hw);
+	if (!pll->boost_enabled || pll->boost_backup_pll_rate == prate)
+		return;
+
+	/* todo */
+	if (pll->boost_backup_pll_usage == BOOST_BACKUP_PLL_USAGE_TARGET)
+		return;
+	/*
+	 * cpu clock rate should be less than or equal to
+	 * low rate when change pll rate in boost module
+	 */
+	if (pll->boost_low_rate && prate > pll->boost_low_rate) {
+		div =  DIV_ROUND_UP(prate, pll->boost_low_rate) - 1;
+		regmap_write(pll->boost, BOOST_CLK_CON,
+			     HIWORD_UPDATE(div, BOOST_CORE_DIV_MASK,
+					   BOOST_CORE_DIV_SHIFT));
+		pll->boost_backup_pll_rate = prate;
+	}
+}
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+
+#ifndef MODULE
+static int boost_summary_show(struct seq_file *s, void *data)
+{
+	struct rockchip_clk_pll *pll = (struct rockchip_clk_pll *)s->private;
+	u32 boost_count = 0;
+	u32 freq_cnt0 = 0, freq_cnt1 = 0;
+	u64 freq_cnt = 0, high_freq_time = 0;
+	u32 short_count = 0, short_threshold = 0;
+	u32 interval_time = 0;
+
+	seq_puts(s, " device    boost_count   high_freq_count  high_freq_time  short_count  short_threshold  interval_count\n");
+	seq_puts(s, "------------------------------------------------------------------------------------------------------\n");
+	seq_printf(s, " %s\n", clk_hw_get_name(&pll->hw));
+
+	regmap_read(pll->boost, BOOST_SWITCH_CNT, &boost_count);
+
+	regmap_read(pll->boost, BOOST_HIGH_PERF_CNT0, &freq_cnt0);
+	regmap_read(pll->boost, BOOST_HIGH_PERF_CNT1, &freq_cnt1);
+	freq_cnt = ((u64)freq_cnt1 << 32) + (u64)freq_cnt0;
+	high_freq_time = freq_cnt;
+	do_div(high_freq_time, 24);
+
+	regmap_read(pll->boost, BOOST_SHORT_SWITCH_CNT, &short_count);
+	regmap_read(pll->boost, BOOST_STATIS_THRESHOLD, &short_threshold);
+	regmap_read(pll->boost, BOOST_SWITCH_THRESHOLD, &interval_time);
+
+	seq_printf(s, "%22u %17llu %15llu %12u %16u %15u\n",
+		   boost_count, freq_cnt, high_freq_time, short_count,
+		   short_threshold, interval_time);
+
+	return 0;
+}
+
+static int boost_summary_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, boost_summary_show, inode->i_private);
+}
+
+static const struct file_operations boost_summary_fops = {
+	.open		= boost_summary_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int boost_config_show(struct seq_file *s, void *data)
+{
+	struct rockchip_clk_pll *pll = (struct rockchip_clk_pll *)s->private;
+
+	seq_printf(s, "boost_enabled:   %d\n", pll->boost_enabled);
+	seq_printf(s, "boost_low_rate:  %lu\n", pll->boost_low_rate);
+	seq_printf(s, "boost_high_rate: %lu\n", pll->boost_high_rate);
+
+	return 0;
+}
+
+static int boost_config_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, boost_config_show, inode->i_private);
+}
+
+static const struct file_operations boost_config_fops = {
+	.open		= boost_config_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int boost_debug_create_one(struct rockchip_clk_pll *pll,
+				  struct dentry *rootdir)
+{
+	struct dentry *pdentry, *d;
+
+	pdentry = debugfs_lookup(clk_hw_get_name(&pll->hw), rootdir);
+	if (!pdentry) {
+		pr_err("%s: failed to lookup %s dentry\n", __func__,
+		       clk_hw_get_name(&pll->hw));
+		return -ENOMEM;
+	}
+
+	d = debugfs_create_file("boost_summary", 0444, pdentry,
+				pll, &boost_summary_fops);
+	if (!d) {
+		pr_err("%s: failed to create boost_summary file\n", __func__);
+		return -ENOMEM;
+	}
+
+	d = debugfs_create_file("boost_config", 0444, pdentry,
+				pll, &boost_config_fops);
+	if (!d) {
+		pr_err("%s: failed to create boost config file\n", __func__);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int __init boost_debug_init(void)
+{
+	struct rockchip_clk_pll *pll;
+	struct dentry *rootdir;
+
+	rootdir = debugfs_lookup("clk", NULL);
+	if (!rootdir) {
+		pr_err("%s: failed to lookup clk dentry\n", __func__);
+		return -ENOMEM;
+	}
+
+	mutex_lock(&clk_boost_lock);
+
+	hlist_for_each_entry(pll, &clk_boost_list, debug_node)
+		boost_debug_create_one(pll, rootdir);
+
+	mutex_unlock(&clk_boost_lock);
+
+	return 0;
+}
+late_initcall(boost_debug_init);
+#endif /* MODULE */
+#endif /* CONFIG_DEBUG_FS */
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index b443169dd..6c8e47067 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -38,6 +38,7 @@ static struct clk *rockchip_clk_register_branch(const char *name,
 		const char *const *parent_names, u8 num_parents,
 		void __iomem *base,
 		int muxdiv_offset, u8 mux_shift, u8 mux_width, u8 mux_flags,
+		u32 *mux_table,
 		int div_offset, u8 div_shift, u8 div_width, u8 div_flags,
 		struct clk_div_table *div_table, int gate_offset,
 		u8 gate_shift, u8 gate_flags, unsigned long flags,
@@ -60,6 +61,7 @@ static struct clk *rockchip_clk_register_branch(const char *name,
 		mux->shift = mux_shift;
 		mux->mask = BIT(mux_width) - 1;
 		mux->flags = mux_flags;
+		mux->table = mux_table;
 		mux->lock = lock;
 		mux_ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops
 							: &clk_mux_ops;
@@ -182,12 +184,43 @@ static void rockchip_fractional_approximation(struct clk_hw *hw,
 	unsigned long p_rate, p_parent_rate;
 	struct clk_hw *p_parent;
 	unsigned long scale;
+	u32 div;
 
 	p_rate = clk_hw_get_rate(clk_hw_get_parent(hw));
-	if ((rate * 20 > p_rate) && (p_rate % rate != 0)) {
+	if (((rate * 20 > p_rate) && (p_rate % rate != 0)) ||
+	    (fd->max_prate && fd->max_prate < p_rate)) {
 		p_parent = clk_hw_get_parent(clk_hw_get_parent(hw));
-		p_parent_rate = clk_hw_get_rate(p_parent);
-		*parent_rate = p_parent_rate;
+		if (!p_parent) {
+			*parent_rate = p_rate;
+		} else {
+			p_parent_rate = clk_hw_get_rate(p_parent);
+			*parent_rate = p_parent_rate;
+			if (fd->max_prate && p_parent_rate > fd->max_prate) {
+				div = DIV_ROUND_UP(p_parent_rate,
+						   fd->max_prate);
+				*parent_rate = p_parent_rate / div;
+			}
+		}
+
+		if (*parent_rate < rate * 20) {
+			/*
+			 * Fractional frequency divider to do
+			 * integer frequency divider does not
+			 * need 20 times the limit.
+			 */
+			if (!(*parent_rate % rate)) {
+				*m = 1;
+				*n = *parent_rate / rate;
+				return;
+			} else if (!(fd->flags & CLK_FRAC_DIVIDER_NO_LIMIT)) {
+				pr_warn("%s p_rate(%ld) is low than rate(%ld)*20, use integer or half-div\n",
+					clk_hw_get_name(hw),
+					*parent_rate, rate);
+				*m = 0;
+				*n = 1;
+				return;
+			}
+		}
 	}
 
 	/*
@@ -210,7 +243,7 @@ static struct clk *rockchip_clk_register_frac_branch(
 		void __iomem *base, int muxdiv_offset, u8 div_flags,
 		int gate_offset, u8 gate_shift, u8 gate_flags,
 		unsigned long flags, struct rockchip_clk_branch *child,
-		spinlock_t *lock)
+		unsigned long max_prate, spinlock_t *lock)
 {
 	struct clk_hw *hw;
 	struct rockchip_clk_frac *frac;
@@ -251,6 +284,7 @@ static struct clk *rockchip_clk_register_frac_branch(
 	div->nmask = GENMASK(div->nwidth - 1, 0) << div->nshift;
 	div->lock = lock;
 	div->approximation = rockchip_fractional_approximation;
+	div->max_prate = max_prate;
 	div_ops = &clk_fractional_divider_ops;
 
 	hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
@@ -278,6 +312,8 @@ static struct clk *rockchip_clk_register_frac_branch(
 		frac_mux->shift = child->mux_shift;
 		frac_mux->mask = BIT(child->mux_width) - 1;
 		frac_mux->flags = child->mux_flags;
+		if (child->mux_table)
+			frac_mux->table = child->mux_table;
 		frac_mux->lock = lock;
 		frac_mux->hw.init = &init;
 
@@ -360,6 +396,61 @@ static struct clk *rockchip_clk_register_factor_branch(const char *name,
 	return hw->clk;
 }
 
+static struct clk *rockchip_clk_register_composite_brother_branch(
+		struct rockchip_clk_provider *ctx, const char *name,
+		const char *const *parent_names, u8 num_parents,
+		void __iomem *base, int muxdiv_offset, u8 mux_shift,
+		u8 mux_width, u8 mux_flags, u32 *mux_table,
+		int div_offset, u8 div_shift, u8 div_width, u8 div_flags,
+		struct clk_div_table *div_table, int gate_offset,
+		u8 gate_shift, u8 gate_flags, unsigned long flags,
+		struct rockchip_clk_branch *brother, spinlock_t *lock)
+{
+	struct clk *clk, *brother_clk;
+	struct clk_composite *composite, *brother_composite;
+	struct clk_hw *hw, *brother_hw;
+
+	if (brother && brother->branch_type != branch_half_divider) {
+		pr_err("%s: composite brother for %s can only be a halfdiv\n",
+		       __func__, name);
+		return ERR_PTR(-EINVAL);
+	}
+
+	clk = rockchip_clk_register_branch(name, parent_names, num_parents,
+					   base, muxdiv_offset, mux_shift,
+					   mux_width, mux_flags, mux_table,
+					   div_offset, div_shift, div_width,
+					   div_flags, div_table,
+					   gate_offset, gate_shift, gate_flags,
+					   flags, lock);
+	if (IS_ERR(clk))
+		return clk;
+
+	brother_clk = rockchip_clk_register_halfdiv(brother->name,
+				brother->parent_names, brother->num_parents,
+				base, brother->muxdiv_offset,
+				brother->mux_shift, brother->mux_width,
+				brother->mux_flags, brother->div_offset,
+				brother->div_shift, brother->div_width,
+				brother->div_flags, brother->gate_offset,
+				brother->gate_shift, brother->gate_flags,
+				flags, lock);
+	if (IS_ERR(brother_clk))
+		return brother_clk;
+	rockchip_clk_add_lookup(ctx, brother_clk, brother->id);
+
+	hw = __clk_get_hw(clk);
+	brother_hw = __clk_get_hw(brother_clk);
+	if (hw && brother_hw) {
+		composite = to_clk_composite(hw);
+		brother_composite = to_clk_composite(brother_hw);
+		composite->brother_hw = brother_hw;
+		brother_composite->brother_hw = hw;
+	}
+
+	return clk;
+}
+
 struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
 						void __iomem *base,
 						unsigned long nr_clks)
@@ -387,6 +478,8 @@ struct rockchip_clk_provider *rockchip_clk_init(struct device_node *np,
 
 	ctx->grf = syscon_regmap_lookup_by_phandle(ctx->cru_node,
 						   "rockchip,grf");
+	ctx->pmugrf = syscon_regmap_lookup_by_phandle(ctx->cru_node,
+						   "rockchip,pmugrf");
 
 	return ctx;
 
@@ -452,11 +545,22 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
 		/* catch simple muxes */
 		switch (list->branch_type) {
 		case branch_mux:
-			clk = clk_register_mux(NULL, list->name,
-				list->parent_names, list->num_parents,
-				flags, ctx->reg_base + list->muxdiv_offset,
-				list->mux_shift, list->mux_width,
-				list->mux_flags, &ctx->lock);
+			if (list->mux_table)
+				clk = clk_register_mux_table(NULL, list->name,
+					list->parent_names, list->num_parents,
+					flags,
+					ctx->reg_base + list->muxdiv_offset,
+					list->mux_shift,
+					BIT(list->mux_width) - 1,
+					list->mux_flags, list->mux_table,
+					&ctx->lock);
+			else
+				clk = clk_register_mux(NULL, list->name,
+					list->parent_names, list->num_parents,
+					flags,
+					ctx->reg_base + list->muxdiv_offset,
+					list->mux_shift, list->mux_width,
+					list->mux_flags, &ctx->lock);
 			break;
 		case branch_muxgrf:
 			clk = rockchip_clk_register_muxgrf(list->name,
@@ -465,6 +569,13 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
 				list->mux_shift, list->mux_width,
 				list->mux_flags);
 			break;
+		case branch_muxpmugrf:
+			clk = rockchip_clk_register_muxgrf(list->name,
+				list->parent_names, list->num_parents,
+				flags, ctx->pmugrf, list->muxdiv_offset,
+				list->mux_shift, list->mux_width,
+				list->mux_flags);
+			break;
 		case branch_divider:
 			if (list->div_table)
 				clk = clk_register_divider_table(NULL,
@@ -488,17 +599,18 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
 				list->div_flags,
 				list->gate_offset, list->gate_shift,
 				list->gate_flags, flags, list->child,
-				&ctx->lock);
+				list->max_prate, &ctx->lock);
 			break;
 		case branch_half_divider:
 			clk = rockchip_clk_register_halfdiv(list->name,
 				list->parent_names, list->num_parents,
 				ctx->reg_base, list->muxdiv_offset,
 				list->mux_shift, list->mux_width,
-				list->mux_flags, list->div_shift,
-				list->div_width, list->div_flags,
-				list->gate_offset, list->gate_shift,
-				list->gate_flags, flags, &ctx->lock);
+				list->mux_flags, list->div_offset,
+				list->div_shift, list->div_width,
+				list->div_flags, list->gate_offset,
+				list->gate_shift, list->gate_flags,
+				flags, &ctx->lock);
 			break;
 		case branch_gate:
 			flags |= CLK_SET_RATE_PARENT;
@@ -514,11 +626,25 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
 				ctx->reg_base, list->muxdiv_offset,
 				list->mux_shift,
 				list->mux_width, list->mux_flags,
-				list->div_offset, list->div_shift, list->div_width,
+				list->mux_table, list->div_offset,
+				list->div_shift, list->div_width,
 				list->div_flags, list->div_table,
 				list->gate_offset, list->gate_shift,
 				list->gate_flags, flags, &ctx->lock);
 			break;
+		case branch_composite_brother:
+			clk = rockchip_clk_register_composite_brother_branch(
+				ctx, list->name, list->parent_names,
+				list->num_parents, ctx->reg_base,
+				list->muxdiv_offset, list->mux_shift,
+				list->mux_width, list->mux_flags,
+				list->mux_table, list->div_offset,
+				list->div_shift, list->div_width,
+				list->div_flags, list->div_table,
+				list->gate_offset, list->gate_shift,
+				list->gate_flags, flags, list->child,
+				&ctx->lock);
+			break;
 		case branch_mmc:
 			clk = rockchip_clk_register_mmc(
 				list->name,
@@ -549,7 +675,17 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
 				list->muxdiv_offset, list->mux_shift,
 				list->mux_width, list->div_shift,
 				list->div_width, list->div_flags,
-				ctx->reg_base, &ctx->lock);
+				ctx->reg_base);
+			break;
+		case branch_dclk_divider:
+			clk = rockchip_clk_register_dclk_branch(list->name,
+				list->parent_names, list->num_parents,
+				ctx->reg_base, list->muxdiv_offset, list->mux_shift,
+				list->mux_width, list->mux_flags,
+				list->div_offset, list->div_shift, list->div_width,
+				list->div_flags, list->div_table,
+				list->gate_offset, list->gate_shift,
+				list->gate_flags, flags, list->max_prate, &ctx->lock);
 			break;
 		}
 
@@ -573,15 +709,17 @@ EXPORT_SYMBOL_GPL(rockchip_clk_register_branches);
 
 void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
 				  unsigned int lookup_id,
-				  const char *name, const char *const *parent_names,
+				  const char *name,
 				  u8 num_parents,
+				  struct clk *parent, struct clk *alt_parent,
 				  const struct rockchip_cpuclk_reg_data *reg_data,
 				  const struct rockchip_cpuclk_rate_table *rates,
 				  int nrates)
 {
 	struct clk *clk;
 
-	clk = rockchip_clk_register_cpuclk(name, parent_names, num_parents,
+	clk = rockchip_clk_register_cpuclk(name, num_parents,
+		parent, alt_parent,
 					   reg_data, rates, nrates,
 					   ctx->reg_base, &ctx->lock);
 	if (IS_ERR(clk)) {
@@ -594,20 +732,20 @@ void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
 }
 EXPORT_SYMBOL_GPL(rockchip_clk_register_armclk);
 
-void rockchip_clk_protect_critical(const char *const clocks[],
-				   int nclocks)
-{
-	int i;
-
-	/* Protect the clocks that needs to stay on */
-	for (i = 0; i < nclocks; i++) {
-		struct clk *clk = __clk_lookup(clocks[i]);
+void (*rk_dump_cru)(void);
+EXPORT_SYMBOL(rk_dump_cru);
 
-		if (clk)
-			clk_prepare_enable(clk);
-	}
+static int rk_clk_panic(struct notifier_block *this,
+			unsigned long ev, void *ptr)
+{
+	if (rk_dump_cru)
+		rk_dump_cru();
+	return NOTIFY_DONE;
 }
-EXPORT_SYMBOL_GPL(rockchip_clk_protect_critical);
+
+static struct notifier_block rk_clk_panic_block = {
+	.notifier_call = rk_clk_panic,
+};
 
 static void __iomem *rst_base;
 static unsigned int reg_restart;
@@ -641,5 +779,7 @@ rockchip_register_restart_notifier(struct rockchip_clk_provider *ctx,
 	if (ret)
 		pr_err("%s: cannot register restart handler, %d\n",
 		       __func__, ret);
+	atomic_notifier_chain_register(&panic_notifier_list,
+				       &rk_clk_panic_block);
 }
 EXPORT_SYMBOL_GPL(rockchip_register_restart_notifier);
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index 2271a8412..26cf2553c 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -37,12 +37,25 @@ struct clk;
 #define BOOST_SWITCH_THRESHOLD		0x0024
 #define BOOST_FSM_STATUS		0x0028
 #define BOOST_PLL_L_CON(x)		((x) * 0x4 + 0x2c)
+#define BOOST_PLL_CON_MASK		0xffff
+#define BOOST_CORE_DIV_MASK		0x1f
+#define BOOST_CORE_DIV_SHIFT		0
+#define BOOST_BACKUP_PLL_MASK		0x3
+#define BOOST_BACKUP_PLL_SHIFT		8
+#define BOOST_BACKUP_PLL_USAGE_MASK	0x1
+#define BOOST_BACKUP_PLL_USAGE_SHIFT	12
+#define BOOST_BACKUP_PLL_USAGE_BORROW	0
+#define BOOST_BACKUP_PLL_USAGE_TARGET	1
+#define BOOST_ENABLE_MASK		0x1
+#define BOOST_ENABLE_SHIFT		0
 #define BOOST_RECOVERY_MASK		0x1
 #define BOOST_RECOVERY_SHIFT		1
 #define BOOST_SW_CTRL_MASK		0x1
 #define BOOST_SW_CTRL_SHIFT		2
 #define BOOST_LOW_FREQ_EN_MASK		0x1
 #define BOOST_LOW_FREQ_EN_SHIFT		3
+#define BOOST_STATIS_ENABLE_MASK	0x1
+#define BOOST_STATIS_ENABLE_SHIFT	4
 #define BOOST_BUSY_STATE		BIT(8)
 
 #define PX30_PLL_CON(x)			((x) * 0x4)
@@ -79,6 +92,51 @@ struct clk;
 #define RV1108_EMMC_CON0		0x1e8
 #define RV1108_EMMC_CON1		0x1ec
 
+#define RV1126_PMU_MODE			0x0
+#define RV1126_PMU_PLL_CON(x)		((x) * 0x4 + 0x10)
+#define RV1126_PMU_CLKSEL_CON(x)	((x) * 0x4 + 0x100)
+#define RV1126_PMU_CLKGATE_CON(x)	((x) * 0x4 + 0x180)
+#define RV1126_PMU_SOFTRST_CON(x)	((x) * 0x4 + 0x200)
+#define RV1126_PLL_CON(x)		((x) * 0x4)
+#define RV1126_MODE_CON			0x90
+#define RV1126_CLKSEL_CON(x)		((x) * 0x4 + 0x100)
+#define RV1126_CLKGATE_CON(x)		((x) * 0x4 + 0x280)
+#define RV1126_SOFTRST_CON(x)		((x) * 0x4 + 0x300)
+#define RV1126_GLB_SRST_FST		0x408
+#define RV1126_GLB_SRST_SND		0x40c
+#define RV1126_SDMMC_CON0		0x440
+#define RV1126_SDMMC_CON1		0x444
+#define RV1126_SDIO_CON0		0x448
+#define RV1126_SDIO_CON1		0x44c
+#define RV1126_EMMC_CON0		0x450
+#define RV1126_EMMC_CON1		0x454
+
+/*
+ * register positions shared by RK1808 RK2928, RK3036,
+ * RK3066, RK3188 and RK3228
+ */
+
+#define RK1808_PLL_CON(x)		((x) * 0x4)
+#define RK1808_MODE_CON			0xa0
+#define RK1808_MISC_CON			0xa4
+#define RK1808_MISC1_CON		0xa8
+#define RK1808_GLB_SRST_FST		0xb8
+#define RK1808_GLB_SRST_SND		0xbc
+#define RK1808_CLKSEL_CON(x)		((x) * 0x4 + 0x100)
+#define RK1808_CLKGATE_CON(x)		((x) * 0x4 + 0x230)
+#define RK1808_SOFTRST_CON(x)		((x) * 0x4 + 0x300)
+#define RK1808_SDMMC_CON0		0x380
+#define RK1808_SDMMC_CON1		0x384
+#define RK1808_SDIO_CON0		0x388
+#define RK1808_SDIO_CON1		0x38c
+#define RK1808_EMMC_CON0		0x390
+#define RK1808_EMMC_CON1		0x394
+
+#define RK1808_PMU_PLL_CON(x)		((x) * 0x4 + 0x4000)
+#define RK1808_PMU_MODE_CON		0x4020
+#define RK1808_PMU_CLKSEL_CON(x)	((x) * 0x4 + 0x4040)
+#define RK1808_PMU_CLKGATE_CON(x)	((x) * 0x4 + 0x4080)
+
 #define RK2928_PLL_CON(x)		((x) * 0x4)
 #define RK2928_MODE_CON		0x40
 #define RK2928_CLKSEL_CON(x)	((x) * 0x4 + 0x44)
@@ -188,11 +246,83 @@ struct clk;
 #define RK3399_PMU_CLKGATE_CON(x)	((x) * 0x4 + 0x100)
 #define RK3399_PMU_SOFTRST_CON(x)	((x) * 0x4 + 0x110)
 
+#define RK3568_PLL_CON(x)		RK2928_PLL_CON(x)
+#define RK3568_MODE_CON0		0xc0
+#define RK3568_MISC_CON0		0xc4
+#define RK3568_MISC_CON1		0xc8
+#define RK3568_MISC_CON2		0xcc
+#define RK3568_GLB_CNT_TH		0xd0
+#define RK3568_GLB_SRST_FST		0xd4
+#define RK3568_GLB_SRST_SND		0xd8
+#define RK3568_GLB_RST_CON		0xdc
+#define RK3568_GLB_RST_ST		0xe0
+#define RK3568_CLKSEL_CON(x)		((x) * 0x4 + 0x100)
+#define RK3568_CLKGATE_CON(x)		((x) * 0x4 + 0x300)
+#define RK3568_SOFTRST_CON(x)		((x) * 0x4 + 0x400)
+#define RK3568_SDMMC0_CON0		0x580
+#define RK3568_SDMMC0_CON1		0x584
+#define RK3568_SDMMC1_CON0		0x588
+#define RK3568_SDMMC1_CON1		0x58c
+#define RK3568_SDMMC2_CON0		0x590
+#define RK3568_SDMMC2_CON1		0x594
+#define RK3568_EMMC_CON0		0x598
+#define RK3568_EMMC_CON1		0x59c
+
+#define RK3568_PMU_PLL_CON(x)		RK2928_PLL_CON(x)
+#define RK3568_PMU_MODE_CON0		0x80
+#define RK3568_PMU_CLKSEL_CON(x)	((x) * 0x4 + 0x100)
+#define RK3568_PMU_CLKGATE_CON(x)	((x) * 0x4 + 0x180)
+#define RK3568_PMU_SOFTRST_CON(x)	((x) * 0x4 + 0x200)
+
+#define RK3588_PHP_CRU_BASE		0x8000
+#define RK3588_PMU_CRU_BASE		0x30000
+#define RK3588_BIGCORE0_CRU_BASE	0x50000
+#define RK3588_BIGCORE1_CRU_BASE	0x52000
+#define RK3588_DSU_CRU_BASE		0x58000
+
+#define RK3588_PLL_CON(x)		RK2928_PLL_CON(x)
+#define RK3588_MODE_CON0		0x280
+#define RK3588_CLKSEL_CON(x)		((x) * 0x4 + 0x300)
+#define RK3588_CLKGATE_CON(x)		((x) * 0x4 + 0x800)
+#define RK3588_SOFTRST_CON(x)		((x) * 0x4 + 0xa00)
+#define RK3588_GLB_CNT_TH		0xc00
+#define RK3588_GLB_SRST_FST		0xc08
+#define RK3588_GLB_SRST_SND		0xc0c
+#define RK3588_GLB_RST_CON		0xc10
+#define RK3588_GLB_RST_ST		0xc04
+#define RK3588_SDIO_CON0		0xC24
+#define RK3588_SDIO_CON1		0xC28
+#define RK3588_SDMMC_CON0		0xC30
+#define RK3588_SDMMC_CON1		0xC34
+
+#define RK3588_PHP_CLKGATE_CON(x)	((x) * 0x4 + RK3588_PHP_CRU_BASE + 0x800)
+#define RK3588_PHP_SOFTRST_CON(x)	((x) * 0x4 + RK3588_PHP_CRU_BASE + 0xa00)
+
+#define RK3588_PMU_PLL_CON(x)		((x) * 0x4 + RK3588_PHP_CRU_BASE)
+#define RK3588_PMU_CLKSEL_CON(x)	((x) * 0x4 + RK3588_PMU_CRU_BASE + 0x300)
+#define RK3588_PMU_CLKGATE_CON(x)	((x) * 0x4 + RK3588_PMU_CRU_BASE + 0x800)
+#define RK3588_PMU_SOFTRST_CON(x)	((x) * 0x4 + RK3588_PMU_CRU_BASE + 0xa00)
+
+#define RK3588_B0_PLL_CON(x)		((x) * 0x4 + RK3588_BIGCORE0_CRU_BASE)
+#define RK3588_BIGCORE0_CLKSEL_CON(x)	((x) * 0x4 + RK3588_BIGCORE0_CRU_BASE + 0x300)
+#define RK3588_BIGCORE0_CLKGATE_CON(x)	((x) * 0x4 + RK3588_BIGCORE0_CRU_BASE + 0x800)
+#define RK3588_BIGCORE0_SOFTRST_CON(x)	((x) * 0x4 + RK3588_BIGCORE0_CRU_BASE + 0xa00)
+#define RK3588_B1_PLL_CON(x)		((x) * 0x4 + RK3588_BIGCORE1_CRU_BASE)
+#define RK3588_BIGCORE1_CLKSEL_CON(x)	((x) * 0x4 + RK3588_BIGCORE1_CRU_BASE + 0x300)
+#define RK3588_BIGCORE1_CLKGATE_CON(x)	((x) * 0x4 + RK3588_BIGCORE1_CRU_BASE + 0x800)
+#define RK3588_BIGCORE1_SOFTRST_CON(x)	((x) * 0x4 + RK3588_BIGCORE1_CRU_BASE + 0xa00)
+#define RK3588_LPLL_CON(x)		((x) * 0x4 + RK3588_DSU_CRU_BASE)
+#define RK3588_DSU_CLKSEL_CON(x)	((x) * 0x4 + RK3588_DSU_CRU_BASE + 0x300)
+#define RK3588_DSU_CLKGATE_CON(x)	((x) * 0x4 + RK3588_DSU_CRU_BASE + 0x800)
+#define RK3588_DSU_SOFTRST_CON(x)	((x) * 0x4 + RK3588_DSU_CRU_BASE + 0xa00)
+
 enum rockchip_pll_type {
 	pll_rk3036,
 	pll_rk3066,
 	pll_rk3328,
 	pll_rk3399,
+	pll_rk3588,
+	pll_rk3588_core,
 };
 
 #define RK3036_PLL_RATE(_rate, _refdiv, _fbdiv, _postdiv1,	\
@@ -225,6 +355,15 @@ enum rockchip_pll_type {
 	.nb = _nb,						\
 }
 
+#define RK3588_PLL_RATE(_rate, _p, _m, _s, _k)			\
+{								\
+	.rate	= _rate##U,					\
+	.p = _p,						\
+	.m = _m,						\
+	.s = _s,						\
+	.k = _k,						\
+}
+
 /**
  * struct rockchip_clk_provider - information about clock provider
  * @reg_base: virtual address for the register base.
@@ -238,22 +377,37 @@ struct rockchip_clk_provider {
 	struct clk_onecell_data clk_data;
 	struct device_node *cru_node;
 	struct regmap *grf;
+	struct regmap *pmugrf;
 	spinlock_t lock;
 };
 
 struct rockchip_pll_rate_table {
 	unsigned long rate;
-	unsigned int nr;
-	unsigned int nf;
-	unsigned int no;
-	unsigned int nb;
-	/* for RK3036/RK3399 */
-	unsigned int fbdiv;
-	unsigned int postdiv1;
-	unsigned int refdiv;
-	unsigned int postdiv2;
-	unsigned int dsmpd;
-	unsigned int frac;
+	union {
+		struct {
+			/* for RK3066 */
+			unsigned int nr;
+			unsigned int nf;
+			unsigned int no;
+			unsigned int nb;
+		};
+		struct {
+			/* for RK3036/RK3399 */
+			unsigned int fbdiv;
+			unsigned int postdiv1;
+			unsigned int refdiv;
+			unsigned int postdiv2;
+			unsigned int dsmpd;
+			unsigned int frac;
+		};
+		struct {
+			/* for RK3588 */
+			unsigned int m;
+			unsigned int p;
+			unsigned int s;
+			unsigned int k;
+		};
+	};
 };
 
 /**
@@ -317,39 +471,56 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
 		struct rockchip_pll_rate_table *rate_table,
 		unsigned long flags, u8 clk_pll_flags);
 
+void rockchip_boost_init(struct clk_hw *hw);
+
+void rockchip_boost_enable_recovery_sw_low(struct clk_hw *hw);
+
+void rockchip_boost_disable_recovery_sw(struct clk_hw *hw);
+
+void rockchip_boost_add_core_div(struct clk_hw *hw, unsigned long prate);
+
 struct rockchip_cpuclk_clksel {
 	int reg;
 	u32 val;
 };
 
-#define ROCKCHIP_CPUCLK_NUM_DIVIDERS	2
+#define ROCKCHIP_CPUCLK_NUM_DIVIDERS	6
+#define ROCKCHIP_CPUCLK_MAX_CORES	4
 struct rockchip_cpuclk_rate_table {
 	unsigned long prate;
 	struct rockchip_cpuclk_clksel divs[ROCKCHIP_CPUCLK_NUM_DIVIDERS];
+	struct rockchip_cpuclk_clksel pre_muxs[ROCKCHIP_CPUCLK_NUM_DIVIDERS];
+	struct rockchip_cpuclk_clksel post_muxs[ROCKCHIP_CPUCLK_NUM_DIVIDERS];
 };
 
 /**
  * struct rockchip_cpuclk_reg_data - register offsets and masks of the cpuclock
- * @core_reg:		register offset of the core settings register
- * @div_core_shift:	core divider offset used to divide the pll value
- * @div_core_mask:	core divider mask
- * @mux_core_alt:	mux value to select alternate parent
+ * @core_reg[]:	register offset of the cores setting register
+ * @div_core_shift[]:	cores divider offset used to divide the pll value
+ * @div_core_mask[]:	cores divider mask
+ * @num_cores:	number of cpu cores
+ * @mux_core_reg:       register offset of the cores select parent
+ * @mux_core_alt:       mux value to select alternate parent
  * @mux_core_main:	mux value to select main parent of core
  * @mux_core_shift:	offset of the core multiplexer
  * @mux_core_mask:	core multiplexer mask
  */
 struct rockchip_cpuclk_reg_data {
-	int		core_reg;
-	u8		div_core_shift;
-	u32		div_core_mask;
-	u8		mux_core_alt;
-	u8		mux_core_main;
-	u8		mux_core_shift;
-	u32		mux_core_mask;
+	int	core_reg[ROCKCHIP_CPUCLK_MAX_CORES];
+	u8	div_core_shift[ROCKCHIP_CPUCLK_MAX_CORES];
+	u32	div_core_mask[ROCKCHIP_CPUCLK_MAX_CORES];
+	int	num_cores;
+	int	mux_core_reg;
+	u8	mux_core_alt;
+	u8	mux_core_main;
+	u8	mux_core_shift;
+	u32	mux_core_mask;
+	const char	*pll_name;
 };
 
 struct clk *rockchip_clk_register_cpuclk(const char *name,
-			const char *const *parent_names, u8 num_parents,
+			u8 num_parents,
+			struct clk *parent, struct clk *alt_parent,
 			const struct rockchip_cpuclk_reg_data *reg_data,
 			const struct rockchip_cpuclk_rate_table *rates,
 			int nrates, void __iomem *reg_base, spinlock_t *lock);
@@ -361,16 +532,21 @@ struct clk *rockchip_clk_register_mmc(const char *name,
 /*
  * DDRCLK flags, including method of setting the rate
  * ROCKCHIP_DDRCLK_SIP: use SIP call to bl31 to change ddrclk rate.
+ * ROCKCHIP_DDRCLK_SCPI: use SCPI APIs to let mcu change ddrclk rate.
  */
 #define ROCKCHIP_DDRCLK_SIP		BIT(0)
+#define ROCKCHIP_DDRCLK_SCPI		0x02
+#define ROCKCHIP_DDRCLK_SIP_V2		0x03
+
+void rockchip_set_ddrclk_params(void __iomem *params);
+void rockchip_set_ddrclk_dmcfreq_wait_complete(int (*func)(void));
 
 struct clk *rockchip_clk_register_ddrclk(const char *name, int flags,
 					 const char *const *parent_names,
 					 u8 num_parents, int mux_offset,
 					 int mux_shift, int mux_width,
 					 int div_shift, int div_width,
-					 int ddr_flags, void __iomem *reg_base,
-					 spinlock_t *lock);
+					 int ddr_flags, void __iomem *reg_base);
 
 #define ROCKCHIP_INVERTER_HIWORD_MASK	BIT(0)
 
@@ -388,8 +564,10 @@ struct clk *rockchip_clk_register_muxgrf(const char *name,
 
 enum rockchip_clk_branch_type {
 	branch_composite,
+	branch_composite_brother,
 	branch_mux,
 	branch_muxgrf,
+	branch_muxpmugrf,
 	branch_divider,
 	branch_fraction_divider,
 	branch_gate,
@@ -398,6 +576,7 @@ enum rockchip_clk_branch_type {
 	branch_factor,
 	branch_ddrclk,
 	branch_half_divider,
+	branch_dclk_divider,
 };
 
 struct rockchip_clk_branch {
@@ -411,6 +590,7 @@ struct rockchip_clk_branch {
 	u8				mux_shift;
 	u8				mux_width;
 	u8				mux_flags;
+	u32				*mux_table;
 	int				div_offset;
 	u8				div_shift;
 	u8				div_width;
@@ -420,6 +600,7 @@ struct rockchip_clk_branch {
 	u8				gate_shift;
 	u8				gate_flags;
 	struct rockchip_clk_branch	*child;
+	unsigned long			max_prate;
 };
 
 #define COMPOSITE(_id, cname, pnames, f, mo, ms, mw, mf, ds, dw,\
@@ -443,6 +624,50 @@ struct rockchip_clk_branch {
 		.gate_flags	= gf,				\
 	}
 
+#define COMPOSITE_BROTHER(_id, cname, pnames, f, mo, ms, mw, mf,\
+			  ds, dw, df, go, gs, gf, bro)		\
+	{							\
+		.id		= _id,				\
+		.branch_type	= branch_composite_brother,	\
+		.name		= cname,			\
+		.parent_names	= pnames,			\
+		.num_parents	= ARRAY_SIZE(pnames),		\
+		.flags		= f,				\
+		.muxdiv_offset	= mo,				\
+		.mux_shift	= ms,				\
+		.mux_width	= mw,				\
+		.mux_flags	= mf,				\
+		.div_shift	= ds,				\
+		.div_width	= dw,				\
+		.div_flags	= df,				\
+		.gate_offset	= go,				\
+		.gate_shift	= gs,				\
+		.gate_flags	= gf,				\
+		.child		= bro,				\
+	}
+
+#define COMPOSITE_MUXTBL(_id, cname, pnames, f, mo, ms, mw, mf,	\
+		 mt, ds, dw, df, go, gs, gf)			\
+	{							\
+		.id		= _id,				\
+		.branch_type	= branch_composite,		\
+		.name		= cname,			\
+		.parent_names	= pnames,			\
+		.num_parents	= ARRAY_SIZE(pnames),		\
+		.flags		= f,				\
+		.muxdiv_offset	= mo,				\
+		.mux_shift	= ms,				\
+		.mux_width	= mw,				\
+		.mux_flags	= mf,				\
+		.mux_table	= mt,				\
+		.div_shift	= ds,				\
+		.div_width	= dw,				\
+		.div_flags	= df,				\
+		.gate_offset	= go,				\
+		.gate_shift	= gs,				\
+		.gate_flags	= gf,				\
+	}
+
 #define COMPOSITE_DIV_OFFSET(_id, cname, pnames, f, mo, ms, mw,	\
 			     mf, do, ds, dw, df, go, gs, gf)	\
 	{							\
@@ -539,6 +764,26 @@ struct rockchip_clk_branch {
 		.gate_offset	= -1,				\
 	}
 
+#define COMPOSITE_BROTHER_NOGATE(_id, cname, pnames, f, mo, ms, \
+				 mw, mf, ds, dw, df, bro)	\
+	{							\
+		.id		= _id,				\
+		.branch_type	= branch_composite_brother,	\
+		.name		= cname,			\
+		.parent_names	= pnames,			\
+		.num_parents	= ARRAY_SIZE(pnames),		\
+		.flags		= f,				\
+		.muxdiv_offset	= mo,				\
+		.mux_shift	= ms,				\
+		.mux_width	= mw,				\
+		.mux_flags	= mf,				\
+		.div_shift	= ds,				\
+		.div_width	= dw,				\
+		.div_flags	= df,				\
+		.gate_offset	= -1,				\
+		.child		= bro,				\
+	}
+
 #define COMPOSITE_NOGATE_DIVTBL(_id, cname, pnames, f, mo, ms,	\
 				mw, mf, ds, dw, df, dt)		\
 	{							\
@@ -559,7 +804,7 @@ struct rockchip_clk_branch {
 		.gate_offset	= -1,				\
 	}
 
-#define COMPOSITE_FRAC(_id, cname, pname, f, mo, df, go, gs, gf)\
+#define COMPOSITE_FRAC(_id, cname, pname, f, mo, df, go, gs, gf, prate)\
 	{							\
 		.id		= _id,				\
 		.branch_type	= branch_fraction_divider,	\
@@ -574,9 +819,10 @@ struct rockchip_clk_branch {
 		.gate_offset	= go,				\
 		.gate_shift	= gs,				\
 		.gate_flags	= gf,				\
+		.max_prate	= prate,			\
 	}
 
-#define COMPOSITE_FRACMUX(_id, cname, pname, f, mo, df, go, gs, gf, ch) \
+#define COMPOSITE_FRACMUX(_id, cname, pname, f, mo, df, go, gs, gf, ch, prate) \
 	{							\
 		.id		= _id,				\
 		.branch_type	= branch_fraction_divider,	\
@@ -592,9 +838,10 @@ struct rockchip_clk_branch {
 		.gate_shift	= gs,				\
 		.gate_flags	= gf,				\
 		.child		= ch,				\
+		.max_prate	= prate,			\
 	}
 
-#define COMPOSITE_FRACMUX_NOGATE(_id, cname, pname, f, mo, df, ch) \
+#define COMPOSITE_FRACMUX_NOGATE(_id, cname, pname, f, mo, df, ch, prate) \
 	{							\
 		.id		= _id,				\
 		.branch_type	= branch_fraction_divider,	\
@@ -608,6 +855,7 @@ struct rockchip_clk_branch {
 		.div_flags	= df,				\
 		.gate_offset	= -1,				\
 		.child		= ch,				\
+		.max_prate	= prate,			\
 	}
 
 #define COMPOSITE_DDRCLK(_id, cname, pnames, f, mo, ms, mw,	\
@@ -643,6 +891,22 @@ struct rockchip_clk_branch {
 		.gate_offset	= -1,				\
 	}
 
+#define MUXTBL(_id, cname, pnames, f, o, s, w, mf, mt)		\
+	{							\
+		.id		= _id,				\
+		.branch_type	= branch_mux,			\
+		.name		= cname,			\
+		.parent_names	= pnames,			\
+		.num_parents	= ARRAY_SIZE(pnames),		\
+		.flags		= f,				\
+		.muxdiv_offset	= o,				\
+		.mux_shift	= s,				\
+		.mux_width	= w,				\
+		.mux_flags	= mf,				\
+		.gate_offset	= -1,				\
+		.mux_table	= mt,				\
+	}
+
 #define MUXGRF(_id, cname, pnames, f, o, s, w, mf)		\
 	{							\
 		.id		= _id,				\
@@ -658,6 +922,21 @@ struct rockchip_clk_branch {
 		.gate_offset	= -1,				\
 	}
 
+#define MUXPMUGRF(_id, cname, pnames, f, o, s, w, mf)		\
+	{							\
+		.id		= _id,				\
+		.branch_type	= branch_muxpmugrf,		\
+		.name		= cname,			\
+		.parent_names	= pnames,			\
+		.num_parents	= ARRAY_SIZE(pnames),		\
+		.flags		= f,				\
+		.muxdiv_offset	= o,				\
+		.mux_shift	= s,				\
+		.mux_width	= w,				\
+		.mux_flags	= mf,				\
+		.gate_offset	= -1,				\
+	}
+
 #define DIV(_id, cname, pname, f, o, s, w, df)			\
 	{							\
 		.id		= _id,				\
@@ -772,6 +1051,28 @@ struct rockchip_clk_branch {
 		.gate_flags	= gf,				\
 	}
 
+#define COMPOSITE_HALFDIV_OFFSET(_id, cname, pnames, f, mo, ms, mw, mf, do,\
+				 ds, dw, df, go, gs, gf)		   \
+	{							\
+		.id		= _id,				\
+		.branch_type	= branch_half_divider,		\
+		.name		= cname,			\
+		.parent_names	= pnames,			\
+		.num_parents	= ARRAY_SIZE(pnames),		\
+		.flags		= f,				\
+		.muxdiv_offset	= mo,				\
+		.mux_shift	= ms,				\
+		.mux_width	= mw,				\
+		.mux_flags	= mf,				\
+		.div_offset	= do,				\
+		.div_shift	= ds,				\
+		.div_width	= dw,				\
+		.div_flags	= df,				\
+		.gate_offset	= go,				\
+		.gate_shift	= gs,				\
+		.gate_flags	= gf,				\
+	}
+
 #define COMPOSITE_NOGATE_HALFDIV(_id, cname, pnames, f, mo, ms, mw, mf,	\
 				 ds, dw, df)				\
 	{							\
@@ -824,6 +1125,28 @@ struct rockchip_clk_branch {
 		.gate_offset	= -1,				\
 	}
 
+#define COMPOSITE_DCLK(_id, cname, pnames, f, mo, ms, mw, mf, ds, dw,\
+		  df, go, gs, gf, prate)				\
+	{							\
+		.id		= _id,				\
+		.branch_type	= branch_dclk_divider,		\
+		.name		= cname,			\
+		.parent_names	= pnames,			\
+		.num_parents	= ARRAY_SIZE(pnames),		\
+		.flags		= f,				\
+		.muxdiv_offset	= mo,				\
+		.mux_shift	= ms,				\
+		.mux_width	= mw,				\
+		.mux_flags	= mf,				\
+		.div_shift	= ds,				\
+		.div_width	= dw,				\
+		.div_flags	= df,				\
+		.gate_offset	= go,				\
+		.gate_shift	= gs,				\
+		.gate_flags	= gf,				\
+		.max_prate	= prate,				\
+	}
+
 /* SGRF clocks are only accessible from secure mode, so not controllable */
 #define SGRF_GATE(_id, cname, pname)				\
 		FACTOR(_id, cname, pname, 0, 1, 1)
@@ -840,13 +1163,17 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx,
 void rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
 				struct rockchip_pll_clock *pll_list,
 				unsigned int nr_pll, int grf_lock_offset);
-void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
-			unsigned int lookup_id, const char *name,
-			const char *const *parent_names, u8 num_parents,
-			const struct rockchip_cpuclk_reg_data *reg_data,
-			const struct rockchip_cpuclk_rate_table *rates,
-			int nrates);
-void rockchip_clk_protect_critical(const char *const clocks[], int nclocks);
+void __init rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
+					 unsigned int lookup_id,
+					 const char *name,
+					 u8 num_parents,
+					 struct clk *parent, struct clk *alt_parent,
+					 const struct rockchip_cpuclk_reg_data *reg_data,
+					 const struct rockchip_cpuclk_rate_table *rates,
+					 int nrates);
+int rockchip_pll_clk_rate_to_scale(struct clk *clk, unsigned long rate);
+int rockchip_pll_clk_scale_to_rate(struct clk *clk, unsigned int scale);
+int rockchip_pll_clk_adaptive_scaling(struct clk *clk, int sel);
 void rockchip_register_restart_notifier(struct rockchip_clk_provider *ctx,
 					unsigned int reg, void (*cb)(void));
 
@@ -857,12 +1184,27 @@ struct clk *rockchip_clk_register_halfdiv(const char *name,
 					  u8 num_parents, void __iomem *base,
 					  int muxdiv_offset, u8 mux_shift,
 					  u8 mux_width, u8 mux_flags,
-					  u8 div_shift, u8 div_width,
-					  u8 div_flags, int gate_offset,
-					  u8 gate_shift, u8 gate_flags,
-					  unsigned long flags,
+					  int div_offset, u8 div_shift,
+					  u8 div_width, u8 div_flags,
+					  int gate_offset, u8 gate_shift,
+					  u8 gate_flags, unsigned long flags,
 					  spinlock_t *lock);
 
+struct clk *rockchip_clk_register_dclk_branch(const char *name,
+					      const char *const *parent_names,
+					      u8 num_parents,
+					      void __iomem *base,
+					      int muxdiv_offset, u8 mux_shift,
+					      u8 mux_width, u8 mux_flags,
+					      int div_offset, u8 div_shift,
+					      u8 div_width, u8 div_flags,
+					      struct clk_div_table *div_table,
+					      int gate_offset,
+					      u8 gate_shift, u8 gate_flags,
+					      unsigned long flags,
+					      unsigned long max_prate,
+					      spinlock_t *lock);
+
 #ifdef CONFIG_RESET_CONTROLLER
 void rockchip_register_softrst(struct device_node *np,
 			       unsigned int num_regs,
@@ -874,5 +1216,6 @@ static inline void rockchip_register_softrst(struct device_node *np,
 {
 }
 #endif
+extern void (*rk_dump_cru)(void);
 
 #endif
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index a0c6e88be..9d9cb5757 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -85,7 +85,9 @@ config IXP4XX_TIMER
 	  Enables support for the Intel XScale IXP4xx SoC timer.
 
 config ROCKCHIP_TIMER
-	bool "Rockchip timer driver" if COMPILE_TEST
+	tristate "Rockchip timer driver"
+	default ARCH_ROCKCHIP
+	depends on ARCH_ROCKCHIP || COMPILE_TEST
 	depends on ARM || ARM64
 	select TIMER_OF
 	select CLKSRC_MMIO
diff --git a/drivers/clocksource/timer-rockchip.c b/drivers/clocksource/timer-rockchip.c
index 1f95d0aca..2f4e970d7 100644
--- a/drivers/clocksource/timer-rockchip.c
+++ b/drivers/clocksource/timer-rockchip.c
@@ -8,11 +8,13 @@
 #include <linux/clockchips.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
+#include <linux/module.h>
 #include <linux/sched_clock.h>
 #include <linux/slab.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
+#include <linux/platform_device.h>
 
 #define TIMER_NAME "rk_timer"
 
@@ -45,7 +47,9 @@ struct rk_clkevt {
 };
 
 static struct rk_clkevt *rk_clkevt;
+#ifndef MODULE
 static struct rk_timer *rk_clksrc;
+#endif
 
 static inline struct rk_timer *rk_timer(struct clock_event_device *ce)
 {
@@ -119,10 +123,12 @@ static irqreturn_t rk_timer_interrupt(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+#ifndef MODULE
 static u64 notrace rk_timer_sched_read(void)
 {
 	return ~readl_relaxed(rk_clksrc->base + TIMER_CURRENT_VALUE0);
 }
+#endif
 
 static int __init
 rk_timer_probe(struct rk_timer *timer, struct device_node *np)
@@ -250,6 +256,7 @@ static int __init rk_clkevt_init(struct device_node *np)
 	return ret;
 }
 
+#ifndef MODULE
 static int __init rk_clksrc_init(struct device_node *np)
 {
 	int ret = -EINVAL;
@@ -287,14 +294,17 @@ static int __init rk_clksrc_init(struct device_node *np)
 	rk_clksrc = ERR_PTR(ret);
 	return ret;
 }
+#endif
 
 static int __init rk_timer_init(struct device_node *np)
 {
 	if (!rk_clkevt)
 		return rk_clkevt_init(np);
 
+#ifndef MODULE
 	if (!rk_clksrc)
 		return rk_clksrc_init(np);
+#endif
 
 	pr_err("Too many timer definitions for '%s'\n", TIMER_NAME);
 	return -EINVAL;
@@ -302,3 +312,26 @@ static int __init rk_timer_init(struct device_node *np)
 
 TIMER_OF_DECLARE(rk3288_timer, "rockchip,rk3288-timer", rk_timer_init);
 TIMER_OF_DECLARE(rk3399_timer, "rockchip,rk3399-timer", rk_timer_init);
+
+#ifdef MODULE
+static int __init rk_timer_driver_probe(struct platform_device *pdev)
+{
+	return rk_timer_init(pdev->dev.of_node);
+}
+
+static const struct of_device_id rk_timer_match_table[] = {
+	{ .compatible = "rockchip,rk3288-timer" },
+	{ .compatible = "rockchip,rk3399-timer" },
+	{ /* sentinel */ },
+};
+
+static struct platform_driver rk_timer_driver = {
+	.driver = {
+		.name = TIMER_NAME,
+		.of_match_table = rk_timer_match_table,
+	},
+};
+module_platform_driver_probe(rk_timer_driver, rk_timer_driver_probe);
+
+MODULE_LICENSE("GPL");
+#endif
diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c
index a3734014d..93306e683 100644
--- a/drivers/cpufreq/cpufreq-dt-platdev.c
+++ b/drivers/cpufreq/cpufreq-dt-platdev.c
@@ -66,21 +66,6 @@ static const struct of_device_id whitelist[] __initconst = {
 	{ .compatible = "renesas,r8a7794", },
 	{ .compatible = "renesas,sh73a0", },
 
-	{ .compatible = "rockchip,rk2928", },
-	{ .compatible = "rockchip,rk3036", },
-	{ .compatible = "rockchip,rk3066a", },
-	{ .compatible = "rockchip,rk3066b", },
-	{ .compatible = "rockchip,rk3188", },
-	{ .compatible = "rockchip,rk3228", },
-	{ .compatible = "rockchip,rk3288", },
-	{ .compatible = "rockchip,rk3328", },
-	{ .compatible = "rockchip,rk3366", },
-	{ .compatible = "rockchip,rk3368", },
-	{ .compatible = "rockchip,rk3399",
-	  .data = &(struct cpufreq_dt_platform_data)
-		{ .have_governor_per_policy = true, },
-	},
-
 	{ .compatible = "st-ericsson,u8500", },
 	{ .compatible = "st-ericsson,u8540", },
 	{ .compatible = "st-ericsson,u9500", },
@@ -138,6 +123,30 @@ static const struct of_device_id blacklist[] __initconst = {
 	{ .compatible = "qcom,sdm845", },
 	{ .compatible = "qcom,sm8150", },
 
+	{ .compatible = "rockchip,px30", },
+	{ .compatible = "rockchip,rk2928", },
+	{ .compatible = "rockchip,rk3036", },
+	{ .compatible = "rockchip,rk3066a", },
+	{ .compatible = "rockchip,rk3066b", },
+	{ .compatible = "rockchip,rk3126", },
+	{ .compatible = "rockchip,rk3128", },
+	{ .compatible = "rockchip,rk3188", },
+	{ .compatible = "rockchip,rk3228", },
+	{ .compatible = "rockchip,rk3229", },
+	{ .compatible = "rockchip,rk3288", },
+	{ .compatible = "rockchip,rk3288w", },
+	{ .compatible = "rockchip,rk3326", },
+	{ .compatible = "rockchip,rk3328", },
+	{ .compatible = "rockchip,rk3366", },
+	{ .compatible = "rockchip,rk3368", },
+	{ .compatible = "rockchip,rk3399", },
+	{ .compatible = "rockchip,rk3399pro", },
+	{ .compatible = "rockchip,rk3566", },
+	{ .compatible = "rockchip,rk3568", },
+	{ .compatible = "rockchip,rk3588", },
+	{ .compatible = "rockchip,rv1109", },
+	{ .compatible = "rockchip,rv1126", },
+
 	{ .compatible = "st,stih407", },
 	{ .compatible = "st,stih410", },
 	{ .compatible = "st,stih418", },
diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c
index e363ae04a..bbf10ab42 100644
--- a/drivers/cpufreq/cpufreq-dt.c
+++ b/drivers/cpufreq/cpufreq-dt.c
@@ -23,6 +23,9 @@
 #include <linux/thermal.h>
 
 #include "cpufreq-dt.h"
+#ifdef CONFIG_ARCH_ROCKCHIP
+#include <linux/rockchip-cpufreq.h>
+#endif
 
 struct private_data {
 	struct list_head node;
@@ -30,7 +33,7 @@ struct private_data {
 	cpumask_var_t cpus;
 	struct device *cpu_dev;
 	struct opp_table *opp_table;
-	struct opp_table *reg_opp_table;
+	struct cpufreq_frequency_table *freq_table;
 	bool have_static_opps;
 };
 
@@ -59,7 +62,11 @@ static int set_target(struct cpufreq_policy *policy, unsigned int index)
 	struct private_data *priv = policy->driver_data;
 	unsigned long freq = policy->freq_table[index].frequency;
 
+#ifdef CONFIG_ARCH_ROCKCHIP
+	return rockchip_cpufreq_opp_set_rate(priv->cpu_dev, freq * 1000);
+#else
 	return dev_pm_opp_set_rate(priv->cpu_dev, freq * 1000);
+#endif
 }
 
 /*
@@ -102,7 +109,6 @@ static const char *find_supply_name(struct device *dev)
 
 static int cpufreq_init(struct cpufreq_policy *policy)
 {
-	struct cpufreq_frequency_table *freq_table;
 	struct private_data *priv;
 	struct device *cpu_dev;
 	struct clk *cpu_clk;
@@ -114,9 +120,7 @@ static int cpufreq_init(struct cpufreq_policy *policy)
 		pr_err("failed to find data for cpu%d\n", policy->cpu);
 		return -ENODEV;
 	}
-
 	cpu_dev = priv->cpu_dev;
-	cpumask_copy(policy->cpus, priv->cpus);
 
 	cpu_clk = clk_get(cpu_dev, NULL);
 	if (IS_ERR(cpu_clk)) {
@@ -125,67 +129,32 @@ static int cpufreq_init(struct cpufreq_policy *policy)
 		return ret;
 	}
 
-	/*
-	 * Initialize OPP tables for all policy->cpus. They will be shared by
-	 * all CPUs which have marked their CPUs shared with OPP bindings.
-	 *
-	 * For platforms not using operating-points-v2 bindings, we do this
-	 * before updating policy->cpus. Otherwise, we will end up creating
-	 * duplicate OPPs for policy->cpus.
-	 *
-	 * OPPs might be populated at runtime, don't check for error here
-	 */
-	if (!dev_pm_opp_of_cpumask_add_table(policy->cpus))
-		priv->have_static_opps = true;
-
-	/*
-	 * But we need OPP table to function so if it is not there let's
-	 * give platform code chance to provide it for us.
-	 */
-	ret = dev_pm_opp_get_opp_count(cpu_dev);
-	if (ret <= 0) {
-		dev_err(cpu_dev, "OPP table can't be empty\n");
-		ret = -ENODEV;
-		goto out_free_opp;
-	}
-
-	ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
-	if (ret) {
-		dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
-		goto out_free_opp;
-	}
+	transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev);
+	if (!transition_latency)
+		transition_latency = CPUFREQ_ETERNAL;
 
+	cpumask_copy(policy->cpus, priv->cpus);
 	policy->driver_data = priv;
 	policy->clk = cpu_clk;
-	policy->freq_table = freq_table;
-
+	policy->freq_table = priv->freq_table;
 	policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(cpu_dev) / 1000;
+	policy->cpuinfo.transition_latency = transition_latency;
+	policy->dvfs_possible_from_any_cpu = true;
 
 	/* Support turbo/boost mode */
 	if (policy_has_boost_freq(policy)) {
 		/* This gets disabled by core on driver unregister */
 		ret = cpufreq_enable_boost_support();
 		if (ret)
-			goto out_free_cpufreq_table;
+			goto out_clk_put;
 		cpufreq_dt_attr[1] = &cpufreq_freq_attr_scaling_boost_freqs;
 	}
 
-	transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev);
-	if (!transition_latency)
-		transition_latency = CPUFREQ_ETERNAL;
-
-	policy->cpuinfo.transition_latency = transition_latency;
-	policy->dvfs_possible_from_any_cpu = true;
-
 	dev_pm_opp_of_register_em(cpu_dev, policy->cpus);
 
 	return 0;
 
-out_free_cpufreq_table:
-	dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
-out_free_opp:
-	if (priv->have_static_opps)
-		dev_pm_opp_of_cpumask_remove_table(policy->cpus);
+out_clk_put:
 	clk_put(cpu_clk);
 
 	return ret;
@@ -208,11 +177,6 @@ static int cpufreq_offline(struct cpufreq_policy *policy)
 
 static int cpufreq_exit(struct cpufreq_policy *policy)
 {
-	struct private_data *priv = policy->driver_data;
-
-	dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
-	if (priv->have_static_opps)
-		dev_pm_opp_of_cpumask_remove_table(policy->related_cpus);
 	clk_put(policy->clk);
 	return 0;
 }
@@ -236,6 +200,7 @@ static int dt_cpufreq_early_init(struct device *dev, int cpu)
 {
 	struct private_data *priv;
 	struct device *cpu_dev;
+	bool fallback = false;
 	const char *reg_name;
 	int ret;
 
@@ -254,68 +219,91 @@ static int dt_cpufreq_early_init(struct device *dev, int cpu)
 	if (!alloc_cpumask_var(&priv->cpus, GFP_KERNEL))
 		return -ENOMEM;
 
+	cpumask_set_cpu(cpu, priv->cpus);
 	priv->cpu_dev = cpu_dev;
 
-	/* Try to get OPP table early to ensure resources are available */
-	priv->opp_table = dev_pm_opp_get_opp_table(cpu_dev);
-	if (IS_ERR(priv->opp_table)) {
-		ret = PTR_ERR(priv->opp_table);
-		if (ret != -EPROBE_DEFER)
-			dev_err(cpu_dev, "failed to get OPP table: %d\n", ret);
-		goto free_cpumask;
-	}
-
 	/*
 	 * OPP layer will be taking care of regulators now, but it needs to know
 	 * the name of the regulator first.
 	 */
 	reg_name = find_supply_name(cpu_dev);
 	if (reg_name) {
-		priv->reg_opp_table = dev_pm_opp_set_regulators(cpu_dev,
-								&reg_name, 1);
-		if (IS_ERR(priv->reg_opp_table)) {
-			ret = PTR_ERR(priv->reg_opp_table);
+		priv->opp_table = dev_pm_opp_set_regulators(cpu_dev, &reg_name,
+							    1);
+		if (IS_ERR(priv->opp_table)) {
+			ret = PTR_ERR(priv->opp_table);
 			if (ret != -EPROBE_DEFER)
 				dev_err(cpu_dev, "failed to set regulators: %d\n",
 					ret);
-			goto put_table;
+			goto free_cpumask;
 		}
 	}
 
-	/* Find OPP sharing information so we can fill pri->cpus here */
 	/* Get OPP-sharing information from "operating-points-v2" bindings */
 	ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, priv->cpus);
 	if (ret) {
 		if (ret != -ENOENT)
-			goto put_reg;
+			goto out;
 
 		/*
 		 * operating-points-v2 not supported, fallback to all CPUs share
 		 * OPP for backward compatibility if the platform hasn't set
 		 * sharing CPUs.
 		 */
-		if (dev_pm_opp_get_sharing_cpus(cpu_dev, priv->cpus)) {
-			cpumask_setall(priv->cpus);
-
-			/*
-			 * OPP tables are initialized only for cpu, do it for
-			 * others as well.
-			 */
-			ret = dev_pm_opp_set_sharing_cpus(cpu_dev, priv->cpus);
-			if (ret)
-				dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
-					__func__, ret);
-		}
+		if (dev_pm_opp_get_sharing_cpus(cpu_dev, priv->cpus))
+			fallback = true;
+	}
+
+	/*
+	 * Initialize OPP tables for all priv->cpus. They will be shared by
+	 * all CPUs which have marked their CPUs shared with OPP bindings.
+	 *
+	 * For platforms not using operating-points-v2 bindings, we do this
+	 * before updating priv->cpus. Otherwise, we will end up creating
+	 * duplicate OPPs for the CPUs.
+	 *
+	 * OPPs might be populated at runtime, don't check for error here.
+	 */
+	if (!dev_pm_opp_of_cpumask_add_table(priv->cpus))
+		priv->have_static_opps = true;
+
+	/*
+	 * The OPP table must be initialized, statically or dynamically, by this
+	 * point.
+	 */
+	ret = dev_pm_opp_get_opp_count(cpu_dev);
+	if (ret <= 0) {
+		dev_err(cpu_dev, "OPP table can't be empty\n");
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (fallback) {
+		cpumask_setall(priv->cpus);
+		ret = dev_pm_opp_set_sharing_cpus(cpu_dev, priv->cpus);
+		if (ret)
+			dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
+				__func__, ret);
+	}
+
+#ifdef CONFIG_ARCH_ROCKCHIP
+	rockchip_cpufreq_adjust_power_scale(cpu_dev);
+#endif
+
+	ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &priv->freq_table);
+	if (ret) {
+		dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
+		goto out;
 	}
 
 	list_add(&priv->node, &priv_list);
 	return 0;
 
-put_reg:
-	if (priv->reg_opp_table)
-		dev_pm_opp_put_regulators(priv->reg_opp_table);
-put_table:
-	dev_pm_opp_put_opp_table(priv->opp_table);
+out:
+	if (priv->have_static_opps)
+		dev_pm_opp_of_cpumask_remove_table(priv->cpus);
+	if (priv->opp_table)
+		dev_pm_opp_put_regulators(priv->opp_table);
 free_cpumask:
 	free_cpumask_var(priv->cpus);
 	return ret;
@@ -326,9 +314,11 @@ static void dt_cpufreq_release(void)
 	struct private_data *priv, *tmp;
 
 	list_for_each_entry_safe(priv, tmp, &priv_list, node) {
-		if (priv->reg_opp_table)
-			dev_pm_opp_put_regulators(priv->reg_opp_table);
-		dev_pm_opp_put_opp_table(priv->opp_table);
+		dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &priv->freq_table);
+		if (priv->have_static_opps)
+			dev_pm_opp_of_cpumask_remove_table(priv->cpus);
+		if (priv->opp_table)
+			dev_pm_opp_put_regulators(priv->opp_table);
 		free_cpumask_var(priv->cpus);
 		list_del(&priv->node);
 	}
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 583423909..6926796dc 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -688,8 +688,12 @@ static ssize_t show_##file_name				\
 	return sprintf(buf, "%u\n", policy->object);	\
 }
 
+static ssize_t show_cpuinfo_max_freq(struct cpufreq_policy *policy, char *buf)
+{
+	unsigned int max_freq = policy->cpuinfo.max_freq;
+	return sprintf(buf, "%u\n", max_freq);
+}
 show_one(cpuinfo_min_freq, cpuinfo.min_freq);
-show_one(cpuinfo_max_freq, cpuinfo.max_freq);
 show_one(cpuinfo_transition_latency, cpuinfo.transition_latency);
 show_one(scaling_min_freq, min);
 show_one(scaling_max_freq, max);
@@ -1400,7 +1404,7 @@ static int cpufreq_online(unsigned int cpu)
 
 		ret = freq_qos_add_request(&policy->constraints,
 					   policy->min_freq_req, FREQ_QOS_MIN,
-					   FREQ_QOS_MIN_DEFAULT_VALUE);
+					   policy->min);
 		if (ret < 0) {
 			/*
 			 * So we don't call freq_qos_remove_request() for an
@@ -1420,7 +1424,7 @@ static int cpufreq_online(unsigned int cpu)
 
 		ret = freq_qos_add_request(&policy->constraints,
 					   policy->max_freq_req, FREQ_QOS_MAX,
-					   FREQ_QOS_MAX_DEFAULT_VALUE);
+					   policy->max);
 		if (ret < 0) {
 			policy->max_freq_req = NULL;
 			goto out_destroy_policy;
@@ -2535,6 +2539,7 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy,
 
 	return ret;
 }
+EXPORT_TRACEPOINT_SYMBOL_GPL(cpu_frequency_limits);
 
 /**
  * cpufreq_update_policy - Re-evaluate an existing cpufreq policy.
diff --git a/drivers/cpufreq/cpufreq_userspace.c b/drivers/cpufreq/cpufreq_userspace.c
index 50a4d7846..1f001d281 100644
--- a/drivers/cpufreq/cpufreq_userspace.c
+++ b/drivers/cpufreq/cpufreq_userspace.c
@@ -78,20 +78,18 @@ static int cpufreq_userspace_policy_start(struct cpufreq_policy *policy)
 
 	mutex_lock(&userspace_mutex);
 	per_cpu(cpu_is_managed, policy->cpu) = 1;
-	*setspeed = policy->cur;
+	if (!*setspeed)
+		*setspeed = policy->cur;
 	mutex_unlock(&userspace_mutex);
 	return 0;
 }
 
 static void cpufreq_userspace_policy_stop(struct cpufreq_policy *policy)
 {
-	unsigned int *setspeed = policy->governor_data;
-
 	pr_debug("managing cpu %u stopped\n", policy->cpu);
 
 	mutex_lock(&userspace_mutex);
 	per_cpu(cpu_is_managed, policy->cpu) = 0;
-	*setspeed = 0;
 	mutex_unlock(&userspace_mutex);
 }
 
diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c
index 4070e573b..557f59ac4 100644
--- a/drivers/cpuidle/driver.c
+++ b/drivers/cpuidle/driver.c
@@ -381,3 +381,4 @@ void cpuidle_driver_state_disabled(struct cpuidle_driver *drv, int idx,
 
 	mutex_unlock(&cpuidle_lock);
 }
+EXPORT_SYMBOL_GPL(cpuidle_driver_state_disabled);
diff --git a/drivers/cpuidle/governor.c b/drivers/cpuidle/governor.c
index 29acaf48e..0e51ed256 100644
--- a/drivers/cpuidle/governor.c
+++ b/drivers/cpuidle/governor.c
@@ -102,6 +102,7 @@ int cpuidle_register_governor(struct cpuidle_governor *gov)
 
 	return ret;
 }
+EXPORT_SYMBOL_GPL(cpuidle_register_governor);
 
 /**
  * cpuidle_governor_latency_req - Compute a latency constraint for CPU
@@ -118,3 +119,4 @@ s64 cpuidle_governor_latency_req(unsigned int cpu)
 
 	return (s64)device_req * NSEC_PER_USEC;
 }
+EXPORT_SYMBOL_GPL(cpuidle_governor_latency_req);
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index c6f460550..db0fe99c8 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -1761,6 +1761,40 @@ static ssize_t timer_store(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RW(timer);
 
+static ssize_t load_show(struct device *dev, struct device_attribute *attr,
+			 char *buf)
+{
+	int err;
+	struct devfreq *devfreq = to_devfreq(dev);
+	struct devfreq_dev_status stat = devfreq->last_status;
+	unsigned long freq;
+	ssize_t len;
+
+	err = devfreq_update_stats(devfreq);
+	if (err)
+		return err;
+
+	if (stat.total_time < stat.busy_time) {
+		err = devfreq_update_stats(devfreq);
+		if (err)
+			return err;
+	};
+
+	if (!stat.total_time)
+		return 0;
+
+	len = sprintf(buf, "%lu", stat.busy_time * 100 / stat.total_time);
+
+	if (devfreq->profile->get_cur_freq &&
+	    !devfreq->profile->get_cur_freq(devfreq->dev.parent, &freq))
+		len += sprintf(buf + len, "@%luHz\n", freq);
+	else
+		len += sprintf(buf + len, "@%luHz\n", devfreq->previous_freq);
+
+	return len;
+}
+static DEVICE_ATTR_RO(load);
+
 static struct attribute *devfreq_attrs[] = {
 	&dev_attr_name.attr,
 	&dev_attr_governor.attr,
@@ -1773,6 +1807,7 @@ static struct attribute *devfreq_attrs[] = {
 	&dev_attr_max_freq.attr,
 	&dev_attr_trans_stat.attr,
 	&dev_attr_timer.attr,
+	&dev_attr_load.attr,
 	NULL,
 };
 ATTRIBUTE_GROUPS(devfreq);
diff --git a/drivers/devfreq/event/rockchip-dfi.c b/drivers/devfreq/event/rockchip-dfi.c
index 9a88faaf8..395790397 100644
--- a/drivers/devfreq/event/rockchip-dfi.c
+++ b/drivers/devfreq/event/rockchip-dfi.c
@@ -20,26 +20,81 @@
 
 #include <soc/rockchip/rk3399_grf.h>
 
-#define RK3399_DMC_NUM_CH	2
-
+#define PX30_PMUGRF_OS_REG2		0x208
+#define PX30_PMUGRF_OS_REG3		0x20c
+
+#define RK3588_PMUGRF_OS_REG(n)		(0x200 + (n) * 4)
+
+#define RK3128_GRF_SOC_CON0		0x140
+#define RK3128_GRF_OS_REG1		0x1cc
+#define RK3128_GRF_DFI_WRNUM		0x220
+#define RK3128_GRF_DFI_RDNUM		0x224
+#define RK3128_GRF_DFI_TIMERVAL		0x22c
+#define RK3128_DDR_MONITOR_EN		((1 << (16 + 6)) + (1 << 6))
+#define RK3128_DDR_MONITOR_DISB		((1 << (16 + 6)) + (0 << 6))
+
+#define RK3288_PMU_SYS_REG2		0x9c
+#define RK3288_GRF_SOC_CON4		0x254
+#define RK3288_GRF_SOC_STATUS(n)	(0x280 + (n) * 4)
+#define RK3288_DFI_EN			(0x30003 << 14)
+#define RK3288_DFI_DIS			(0x30000 << 14)
+#define RK3288_LPDDR_SEL		(0x10001 << 13)
+#define RK3288_DDR3_SEL			(0x10000 << 13)
+
+#define RK3328_GRF_OS_REG2		0x5d0
+
+#define RK3368_GRF_DDRC0_CON0		0x600
+#define RK3368_GRF_SOC_STATUS5		0x494
+#define RK3368_GRF_SOC_STATUS6		0x498
+#define RK3368_GRF_SOC_STATUS8		0x4a0
+#define RK3368_GRF_SOC_STATUS9		0x4a4
+#define RK3368_GRF_SOC_STATUS10		0x4a8
+#define RK3368_DFI_EN			(0x30003 << 5)
+#define RK3368_DFI_DIS			(0x30000 << 5)
+
+#define MAX_DMC_NUM_CH			4
+#define READ_DRAMTYPE_INFO(n)		(((n) >> 13) & 0x7)
+#define READ_CH_INFO(n)			(((n) >> 28) & 0x3)
+#define READ_DRAMTYPE_INFO_V3(n, m)	((((n) >> 13) & 0x7) | ((((m) >> 12) & 0x3) << 3))
+#define READ_SYSREG_VERSION(m)		(((m) >> 28) & 0xf)
+#define READ_LP5_BANK_MODE(m)		(((m) >> 1) & 0x3)
+#define READ_LP5_CKR(m)			(((m) >> 0) & 0x1)
 /* DDRMON_CTRL */
-#define DDRMON_CTRL	0x04
-#define CLR_DDRMON_CTRL	(0x1f0000 << 0)
-#define LPDDR4_EN	(0x10001 << 4)
-#define HARDWARE_EN	(0x10001 << 3)
-#define LPDDR3_EN	(0x10001 << 2)
-#define SOFTWARE_EN	(0x10001 << 1)
-#define SOFTWARE_DIS	(0x10000 << 1)
-#define TIME_CNT_EN	(0x10001 << 0)
+#define DDRMON_CTRL			0x04
+#define CLR_DDRMON_CTRL			(0xffff0000 << 0)
+#define LPDDR5_BANK_MODE(m)		((0x30000 | ((m) & 0x3)) << 7)
+#define LPDDR5_EN			(0x10001 << 6)
+#define DDR4_EN				(0x10001 << 5)
+#define LPDDR4_EN			(0x10001 << 4)
+#define HARDWARE_EN			(0x10001 << 3)
+#define LPDDR2_3_EN			(0x10001 << 2)
+#define SOFTWARE_EN			(0x10001 << 1)
+#define SOFTWARE_DIS			(0x10000 << 1)
+#define TIME_CNT_EN			(0x10001 << 0)
 
 #define DDRMON_CH0_COUNT_NUM		0x28
 #define DDRMON_CH0_DFI_ACCESS_NUM	0x2c
 #define DDRMON_CH1_COUNT_NUM		0x3c
 #define DDRMON_CH1_DFI_ACCESS_NUM	0x40
 
+/* pmu grf */
+#define PMUGRF_OS_REG2			0x308
+
+enum {
+	DDR4 = 0,
+	DDR3 = 3,
+	LPDDR2 = 5,
+	LPDDR3 = 6,
+	LPDDR4 = 7,
+	LPDDR4X = 8,
+	LPDDR5 = 9,
+	DDR5 = 10,
+	UNUSED = 0xFF
+};
+
 struct dmc_usage {
-	u32 access;
-	u32 total;
+	u64 access;
+	u64 total;
 };
 
 /*
@@ -50,44 +105,307 @@ struct dmc_usage {
 struct rockchip_dfi {
 	struct devfreq_event_dev *edev;
 	struct devfreq_event_desc *desc;
-	struct dmc_usage ch_usage[RK3399_DMC_NUM_CH];
+	struct dmc_usage ch_usage[MAX_DMC_NUM_CH];
 	struct device *dev;
 	void __iomem *regs;
 	struct regmap *regmap_pmu;
+	struct regmap *regmap_grf;
+	struct regmap *regmap_pmugrf;
 	struct clk *clk;
+	u32 dram_type;
+	u32 mon_idx;
+	u32 count_rate;
+	u32 dram_dynamic_info_reg;
+	/* 0: BG mode, 1: 16 Bank mode, 2: 8 bank mode */
+	u32 lp5_bank_mode;
+	/* 0: clk:dqs = 1:2, 1: 1:4 */
+	u32 lp5_ckr;
+	/*
+	 * available mask, 1: available, 0: not available
+	 * each bit represent a channel
+	 */
+	u32 ch_msk;
+};
+
+static void rk3128_dfi_start_hardware_counter(struct devfreq_event_dev *edev)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+
+	regmap_write(info->regmap_grf,
+		     RK3128_GRF_SOC_CON0,
+		     RK3128_DDR_MONITOR_EN);
+}
+
+static void rk3128_dfi_stop_hardware_counter(struct devfreq_event_dev *edev)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+
+	regmap_write(info->regmap_grf,
+		     RK3128_GRF_SOC_CON0,
+		     RK3128_DDR_MONITOR_DISB);
+}
+
+static int rk3128_dfi_disable(struct devfreq_event_dev *edev)
+{
+	rk3128_dfi_stop_hardware_counter(edev);
+
+	return 0;
+}
+
+static int rk3128_dfi_enable(struct devfreq_event_dev *edev)
+{
+	rk3128_dfi_start_hardware_counter(edev);
+
+	return 0;
+}
+
+static int rk3128_dfi_set_event(struct devfreq_event_dev *edev)
+{
+	return 0;
+}
+
+static int rk3128_dfi_get_event(struct devfreq_event_dev *edev,
+				struct devfreq_event_data *edata)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+	unsigned long flags;
+	u32 dfi_wr, dfi_rd, dfi_timer;
+
+	local_irq_save(flags);
+
+	rk3128_dfi_stop_hardware_counter(edev);
+
+	regmap_read(info->regmap_grf, RK3128_GRF_DFI_WRNUM, &dfi_wr);
+	regmap_read(info->regmap_grf, RK3128_GRF_DFI_RDNUM, &dfi_rd);
+	regmap_read(info->regmap_grf, RK3128_GRF_DFI_TIMERVAL, &dfi_timer);
+
+	edata->load_count = (dfi_wr + dfi_rd) * 4;
+	edata->total_count = dfi_timer;
+
+	rk3128_dfi_start_hardware_counter(edev);
+
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+static const struct devfreq_event_ops rk3128_dfi_ops = {
+	.disable = rk3128_dfi_disable,
+	.enable = rk3128_dfi_enable,
+	.get_event = rk3128_dfi_get_event,
+	.set_event = rk3128_dfi_set_event,
+};
+
+static void rk3288_dfi_start_hardware_counter(struct devfreq_event_dev *edev)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+
+	regmap_write(info->regmap_grf, RK3288_GRF_SOC_CON4, RK3288_DFI_EN);
+}
+
+static void rk3288_dfi_stop_hardware_counter(struct devfreq_event_dev *edev)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+
+	regmap_write(info->regmap_grf, RK3288_GRF_SOC_CON4, RK3288_DFI_DIS);
+}
+
+static int rk3288_dfi_disable(struct devfreq_event_dev *edev)
+{
+	rk3288_dfi_stop_hardware_counter(edev);
+
+	return 0;
+}
+
+static int rk3288_dfi_enable(struct devfreq_event_dev *edev)
+{
+	rk3288_dfi_start_hardware_counter(edev);
+
+	return 0;
+}
+
+static int rk3288_dfi_set_event(struct devfreq_event_dev *edev)
+{
+	return 0;
+}
+
+static int rk3288_dfi_get_busier_ch(struct devfreq_event_dev *edev)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+	u32 tmp, max = 0;
+	u32 i, busier_ch = 0;
+	u32 rd_count, wr_count, total_count;
+
+	rk3288_dfi_stop_hardware_counter(edev);
+
+	/* Find out which channel is busier */
+	for (i = 0; i < MAX_DMC_NUM_CH; i++) {
+		if (!(info->ch_msk & BIT(i)))
+			continue;
+		regmap_read(info->regmap_grf,
+			    RK3288_GRF_SOC_STATUS(11 + i * 4), &wr_count);
+		regmap_read(info->regmap_grf,
+			    RK3288_GRF_SOC_STATUS(12 + i * 4), &rd_count);
+		regmap_read(info->regmap_grf,
+			    RK3288_GRF_SOC_STATUS(14 + i * 4), &total_count);
+		info->ch_usage[i].access = (wr_count + rd_count) * 4;
+		info->ch_usage[i].total = total_count;
+		tmp = info->ch_usage[i].access;
+		if (tmp > max) {
+			busier_ch = i;
+			max = tmp;
+		}
+	}
+	rk3288_dfi_start_hardware_counter(edev);
+
+	return busier_ch;
+}
+
+static int rk3288_dfi_get_event(struct devfreq_event_dev *edev,
+				struct devfreq_event_data *edata)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+	int busier_ch;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	busier_ch = rk3288_dfi_get_busier_ch(edev);
+	local_irq_restore(flags);
+
+	edata->load_count = info->ch_usage[busier_ch].access;
+	edata->total_count = info->ch_usage[busier_ch].total;
+
+	return 0;
+}
+
+static const struct devfreq_event_ops rk3288_dfi_ops = {
+	.disable = rk3288_dfi_disable,
+	.enable = rk3288_dfi_enable,
+	.get_event = rk3288_dfi_get_event,
+	.set_event = rk3288_dfi_set_event,
+};
+
+static void rk3368_dfi_start_hardware_counter(struct devfreq_event_dev *edev)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+
+	regmap_write(info->regmap_grf, RK3368_GRF_DDRC0_CON0, RK3368_DFI_EN);
+}
+
+static void rk3368_dfi_stop_hardware_counter(struct devfreq_event_dev *edev)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+
+	regmap_write(info->regmap_grf, RK3368_GRF_DDRC0_CON0, RK3368_DFI_DIS);
+}
+
+static int rk3368_dfi_disable(struct devfreq_event_dev *edev)
+{
+	rk3368_dfi_stop_hardware_counter(edev);
+
+	return 0;
+}
+
+static int rk3368_dfi_enable(struct devfreq_event_dev *edev)
+{
+	rk3368_dfi_start_hardware_counter(edev);
+
+	return 0;
+}
+
+static int rk3368_dfi_set_event(struct devfreq_event_dev *edev)
+{
+	return 0;
+}
+
+static int rk3368_dfi_get_event(struct devfreq_event_dev *edev,
+				struct devfreq_event_data *edata)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+	unsigned long flags;
+	u32 dfi0_wr, dfi0_rd, dfi1_wr, dfi1_rd, dfi_timer;
+
+	local_irq_save(flags);
+
+	rk3368_dfi_stop_hardware_counter(edev);
+
+	regmap_read(info->regmap_grf, RK3368_GRF_SOC_STATUS5, &dfi0_wr);
+	regmap_read(info->regmap_grf, RK3368_GRF_SOC_STATUS6, &dfi0_rd);
+	regmap_read(info->regmap_grf, RK3368_GRF_SOC_STATUS9, &dfi1_wr);
+	regmap_read(info->regmap_grf, RK3368_GRF_SOC_STATUS10, &dfi1_rd);
+	regmap_read(info->regmap_grf, RK3368_GRF_SOC_STATUS8, &dfi_timer);
+
+	edata->load_count = (dfi0_wr + dfi0_rd + dfi1_wr + dfi1_rd) * 2;
+	edata->total_count = dfi_timer;
+
+	rk3368_dfi_start_hardware_counter(edev);
+
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+static const struct devfreq_event_ops rk3368_dfi_ops = {
+	.disable = rk3368_dfi_disable,
+	.enable = rk3368_dfi_enable,
+	.get_event = rk3368_dfi_get_event,
+	.set_event = rk3368_dfi_set_event,
 };
 
 static void rockchip_dfi_start_hardware_counter(struct devfreq_event_dev *edev)
 {
 	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
 	void __iomem *dfi_regs = info->regs;
-	u32 val;
-	u32 ddr_type;
+	u32 mon_idx = 0, val_6 = 0;
+	u32 i;
 
-	/* get ddr type */
-	regmap_read(info->regmap_pmu, RK3399_PMUGRF_OS_REG2, &val);
-	ddr_type = (val >> RK3399_PMUGRF_DDRTYPE_SHIFT) &
-		    RK3399_PMUGRF_DDRTYPE_MASK;
+	if (info->mon_idx)
+		mon_idx = info->mon_idx;
 
-	/* clear DDRMON_CTRL setting */
-	writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + DDRMON_CTRL);
+	if (info->dram_dynamic_info_reg)
+		regmap_read(info->regmap_pmugrf, info->dram_dynamic_info_reg, &val_6);
 
-	/* set ddr type to dfi */
-	if (ddr_type == RK3399_PMUGRF_DDRTYPE_LPDDR3)
-		writel_relaxed(LPDDR3_EN, dfi_regs + DDRMON_CTRL);
-	else if (ddr_type == RK3399_PMUGRF_DDRTYPE_LPDDR4)
-		writel_relaxed(LPDDR4_EN, dfi_regs + DDRMON_CTRL);
+	if (info->dram_type == LPDDR5) {
+		info->lp5_bank_mode = READ_LP5_BANK_MODE(val_6);
+		info->lp5_ckr = READ_LP5_CKR(val_6);
+	}
 
-	/* enable count, use software mode */
-	writel_relaxed(SOFTWARE_EN, dfi_regs + DDRMON_CTRL);
+	for (i = 0; i < MAX_DMC_NUM_CH; i++) {
+		if (!(info->ch_msk & BIT(i)))
+			continue;
+		/* clear DDRMON_CTRL setting */
+		writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + i * mon_idx + DDRMON_CTRL);
+
+		/* set ddr type to dfi */
+		if (info->dram_type == LPDDR3 || info->dram_type == LPDDR2)
+			writel_relaxed(LPDDR2_3_EN, dfi_regs + i * mon_idx + DDRMON_CTRL);
+		else if (info->dram_type == LPDDR4 || info->dram_type == LPDDR4X)
+			writel_relaxed(LPDDR4_EN, dfi_regs + i * mon_idx + DDRMON_CTRL);
+		else if (info->dram_type == DDR4)
+			writel_relaxed(DDR4_EN, dfi_regs + i * mon_idx + DDRMON_CTRL);
+		else if (info->dram_type == LPDDR5)
+			writel_relaxed(LPDDR5_EN | LPDDR5_BANK_MODE(info->lp5_bank_mode),
+				       dfi_regs + i * mon_idx + DDRMON_CTRL);
+
+		/* enable count, use software mode */
+		writel_relaxed(SOFTWARE_EN, dfi_regs + i * mon_idx + DDRMON_CTRL);
+	}
 }
 
 static void rockchip_dfi_stop_hardware_counter(struct devfreq_event_dev *edev)
 {
 	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
 	void __iomem *dfi_regs = info->regs;
+	u32 mon_idx = 0, i;
 
-	writel_relaxed(SOFTWARE_DIS, dfi_regs + DDRMON_CTRL);
+	if (info->mon_idx)
+		mon_idx = info->mon_idx;
+
+	for (i = 0; i < MAX_DMC_NUM_CH; i++) {
+		if (!(info->ch_msk & BIT(i)))
+			continue;
+		writel_relaxed(SOFTWARE_DIS, dfi_regs + i * mon_idx + DDRMON_CTRL);
+	}
 }
 
 static int rockchip_dfi_get_busier_ch(struct devfreq_event_dev *edev)
@@ -96,16 +414,35 @@ static int rockchip_dfi_get_busier_ch(struct devfreq_event_dev *edev)
 	u32 tmp, max = 0;
 	u32 i, busier_ch = 0;
 	void __iomem *dfi_regs = info->regs;
+	u32 mon_idx = 0x20, count_rate = 1;
 
 	rockchip_dfi_stop_hardware_counter(edev);
 
+	if (info->mon_idx)
+		mon_idx = info->mon_idx;
+	if (info->count_rate)
+		count_rate = info->count_rate;
+
 	/* Find out which channel is busier */
-	for (i = 0; i < RK3399_DMC_NUM_CH; i++) {
-		info->ch_usage[i].access = readl_relaxed(dfi_regs +
-				DDRMON_CH0_DFI_ACCESS_NUM + i * 20) * 4;
+	for (i = 0; i < MAX_DMC_NUM_CH; i++) {
+		if (!(info->ch_msk & BIT(i)))
+			continue;
+
+		/* rk3588 counter is dfi clk rate */
 		info->ch_usage[i].total = readl_relaxed(dfi_regs +
-				DDRMON_CH0_COUNT_NUM + i * 20);
-		tmp = info->ch_usage[i].access;
+				DDRMON_CH0_COUNT_NUM + i * mon_idx) * count_rate;
+
+		/* LPDDR5 LPDDR4 and LPDDR4X BL = 16,other DDR type BL = 8 */
+		tmp = readl_relaxed(dfi_regs +
+				DDRMON_CH0_DFI_ACCESS_NUM + i * mon_idx);
+		if (info->dram_type == LPDDR4 || info->dram_type == LPDDR4X)
+			tmp *= 8;
+		else if (info->dram_type == LPDDR5)
+			tmp *= 16 / (4 << info->lp5_ckr);
+		else
+			tmp *= 4;
+		info->ch_usage[i].access = tmp;
+
 		if (tmp > max) {
 			busier_ch = i;
 			max = tmp;
@@ -121,7 +458,8 @@ static int rockchip_dfi_disable(struct devfreq_event_dev *edev)
 	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
 
 	rockchip_dfi_stop_hardware_counter(edev);
-	clk_disable_unprepare(info->clk);
+	if (info->clk)
+		clk_disable_unprepare(info->clk);
 
 	return 0;
 }
@@ -131,10 +469,13 @@ static int rockchip_dfi_enable(struct devfreq_event_dev *edev)
 	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
 	int ret;
 
-	ret = clk_prepare_enable(info->clk);
-	if (ret) {
-		dev_err(&edev->dev, "failed to enable dfi clk: %d\n", ret);
-		return ret;
+	if (info->clk) {
+		ret = clk_prepare_enable(info->clk);
+		if (ret) {
+			dev_err(&edev->dev, "failed to enable dfi clk: %d\n",
+				ret);
+			return ret;
+		}
 	}
 
 	rockchip_dfi_start_hardware_counter(edev);
@@ -151,8 +492,11 @@ static int rockchip_dfi_get_event(struct devfreq_event_dev *edev,
 {
 	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
 	int busier_ch;
+	unsigned long flags;
 
+	local_irq_save(flags);
 	busier_ch = rockchip_dfi_get_busier_ch(edev);
+	local_irq_restore(flags);
 
 	edata->load_count = info->ch_usage[busier_ch].access;
 	edata->total_count = info->ch_usage[busier_ch].total;
@@ -167,22 +511,156 @@ static const struct devfreq_event_ops rockchip_dfi_ops = {
 	.set_event = rockchip_dfi_set_event,
 };
 
-static const struct of_device_id rockchip_dfi_id_match[] = {
-	{ .compatible = "rockchip,rk3399-dfi" },
-	{ },
-};
-MODULE_DEVICE_TABLE(of, rockchip_dfi_id_match);
+static __init int rk3588_dfi_init(struct platform_device *pdev,
+				  struct rockchip_dfi *data,
+				  struct devfreq_event_desc *desc)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct resource *res;
+	u32 val_2, val_3, val_4;
 
-static int rockchip_dfi_probe(struct platform_device *pdev)
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	data->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(data->regs))
+		return PTR_ERR(data->regs);
+
+	data->regmap_pmugrf = syscon_regmap_lookup_by_phandle(np, "rockchip,pmu_grf");
+	if (IS_ERR(data->regmap_pmugrf))
+		return PTR_ERR(data->regmap_pmugrf);
+
+	regmap_read(data->regmap_pmugrf, RK3588_PMUGRF_OS_REG(2), &val_2);
+	regmap_read(data->regmap_pmugrf, RK3588_PMUGRF_OS_REG(3), &val_3);
+	regmap_read(data->regmap_pmugrf, RK3588_PMUGRF_OS_REG(4), &val_4);
+	if (READ_SYSREG_VERSION(val_3) >= 0x3)
+		data->dram_type = READ_DRAMTYPE_INFO_V3(val_2, val_3);
+	else
+		data->dram_type = READ_DRAMTYPE_INFO(val_2);
+
+	data->mon_idx = 0x4000;
+	data->count_rate = 2;
+	data->dram_dynamic_info_reg = RK3588_PMUGRF_OS_REG(6);
+	data->ch_msk = READ_CH_INFO(val_2) | READ_CH_INFO(val_4) << 2;
+	data->clk = NULL;
+
+	desc->ops = &rockchip_dfi_ops;
+
+	return 0;
+}
+
+static __init int px30_dfi_init(struct platform_device *pdev,
+				  struct rockchip_dfi *data,
+				  struct devfreq_event_desc *desc)
 {
-	struct device *dev = &pdev->dev;
-	struct rockchip_dfi *data;
-	struct devfreq_event_desc *desc;
 	struct device_node *np = pdev->dev.of_node, *node;
+	struct resource *res;
+	u32 val_2, val_3;
 
-	data = devm_kzalloc(dev, sizeof(struct rockchip_dfi), GFP_KERNEL);
-	if (!data)
-		return -ENOMEM;
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	data->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(data->regs))
+		return PTR_ERR(data->regs);
+
+	node = of_parse_phandle(np, "rockchip,pmugrf", 0);
+	if (node) {
+		data->regmap_pmugrf = syscon_node_to_regmap(node);
+		if (IS_ERR(data->regmap_pmugrf))
+			return PTR_ERR(data->regmap_pmugrf);
+	}
+
+	regmap_read(data->regmap_pmugrf, PX30_PMUGRF_OS_REG2, &val_2);
+	regmap_read(data->regmap_pmugrf, PX30_PMUGRF_OS_REG3, &val_3);
+	if (READ_SYSREG_VERSION(val_3) >= 0x3)
+		data->dram_type = READ_DRAMTYPE_INFO_V3(val_2, val_3);
+	else
+		data->dram_type = READ_DRAMTYPE_INFO(val_2);
+	data->ch_msk = 1;
+	data->clk = NULL;
+
+	desc->ops = &rockchip_dfi_ops;
+
+	return 0;
+}
+
+static __init int rk3128_dfi_init(struct platform_device *pdev,
+				  struct rockchip_dfi *data,
+				  struct devfreq_event_desc *desc)
+{
+	struct device_node *np = pdev->dev.of_node, *node;
+
+	node = of_parse_phandle(np, "rockchip,grf", 0);
+	if (node) {
+		data->regmap_grf = syscon_node_to_regmap(node);
+		if (IS_ERR(data->regmap_grf))
+			return PTR_ERR(data->regmap_grf);
+	}
+
+	desc->ops = &rk3128_dfi_ops;
+
+	return 0;
+}
+
+static __init int rk3288_dfi_init(struct platform_device *pdev,
+				  struct rockchip_dfi *data,
+				  struct devfreq_event_desc *desc)
+{
+	struct device_node *np = pdev->dev.of_node, *node;
+	u32 val;
+
+	node = of_parse_phandle(np, "rockchip,pmu", 0);
+	if (node) {
+		data->regmap_pmu = syscon_node_to_regmap(node);
+		if (IS_ERR(data->regmap_pmu))
+			return PTR_ERR(data->regmap_pmu);
+	}
+
+	node = of_parse_phandle(np, "rockchip,grf", 0);
+	if (node) {
+		data->regmap_grf = syscon_node_to_regmap(node);
+		if (IS_ERR(data->regmap_grf))
+			return PTR_ERR(data->regmap_grf);
+	}
+
+	regmap_read(data->regmap_pmu, RK3288_PMU_SYS_REG2, &val);
+	data->dram_type = READ_DRAMTYPE_INFO(val);
+	data->ch_msk = READ_CH_INFO(val);
+
+	if (data->dram_type == DDR3)
+		regmap_write(data->regmap_grf, RK3288_GRF_SOC_CON4,
+			     RK3288_DDR3_SEL);
+	else
+		regmap_write(data->regmap_grf, RK3288_GRF_SOC_CON4,
+			     RK3288_LPDDR_SEL);
+
+	desc->ops = &rk3288_dfi_ops;
+
+	return 0;
+}
+
+static __init int rk3368_dfi_init(struct platform_device *pdev,
+				  struct rockchip_dfi *data,
+				  struct devfreq_event_desc *desc)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->parent || !dev->parent->of_node)
+		return -EINVAL;
+
+	data->regmap_grf = syscon_node_to_regmap(dev->parent->of_node);
+	if (IS_ERR(data->regmap_grf))
+		return PTR_ERR(data->regmap_grf);
+
+	desc->ops = &rk3368_dfi_ops;
+
+	return 0;
+}
+
+static __init int rockchip_dfi_init(struct platform_device *pdev,
+				    struct rockchip_dfi *data,
+				    struct devfreq_event_desc *desc)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = pdev->dev.of_node, *node;
+	u32 val;
 
 	data->regs = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(data->regs))
@@ -202,23 +680,101 @@ static int rockchip_dfi_probe(struct platform_device *pdev)
 		if (IS_ERR(data->regmap_pmu))
 			return PTR_ERR(data->regmap_pmu);
 	}
-	data->dev = dev;
+
+	regmap_read(data->regmap_pmu, PMUGRF_OS_REG2, &val);
+	data->dram_type = READ_DRAMTYPE_INFO(val);
+	data->ch_msk = READ_CH_INFO(val);
+
+	desc->ops = &rockchip_dfi_ops;
+
+	return 0;
+}
+
+static __init int rk3328_dfi_init(struct platform_device *pdev,
+				  struct rockchip_dfi *data,
+				  struct devfreq_event_desc *desc)
+{
+	struct device_node *np = pdev->dev.of_node, *node;
+	struct resource *res;
+	u32 val;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	data->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(data->regs))
+		return PTR_ERR(data->regs);
+
+	node = of_parse_phandle(np, "rockchip,grf", 0);
+	if (node) {
+		data->regmap_grf = syscon_node_to_regmap(node);
+		if (IS_ERR(data->regmap_grf))
+			return PTR_ERR(data->regmap_grf);
+	}
+
+	regmap_read(data->regmap_grf, RK3328_GRF_OS_REG2, &val);
+	data->dram_type = READ_DRAMTYPE_INFO(val);
+	data->ch_msk = 1;
+	data->clk = NULL;
+
+	desc->ops = &rockchip_dfi_ops;
+
+	return 0;
+}
+
+static const struct of_device_id rockchip_dfi_id_match[] = {
+	{ .compatible = "rockchip,px30-dfi", .data = px30_dfi_init },
+	{ .compatible = "rockchip,rk1808-dfi", .data = px30_dfi_init },
+	{ .compatible = "rockchip,rk3128-dfi", .data = rk3128_dfi_init },
+	{ .compatible = "rockchip,rk3288-dfi", .data = rk3288_dfi_init },
+	{ .compatible = "rockchip,rk3328-dfi", .data = rk3328_dfi_init },
+	{ .compatible = "rockchip,rk3368-dfi", .data = rk3368_dfi_init },
+	{ .compatible = "rockchip,rk3399-dfi", .data = rockchip_dfi_init },
+	{ .compatible = "rockchip,rk3568-dfi", .data = px30_dfi_init },
+	{ .compatible = "rockchip,rk3588-dfi", .data = rk3588_dfi_init },
+	{ .compatible = "rockchip,rv1126-dfi", .data = px30_dfi_init },
+	{ },
+};
+
+static int rockchip_dfi_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rockchip_dfi *data;
+	struct devfreq_event_desc *desc;
+	struct device_node *np = pdev->dev.of_node;
+	const struct of_device_id *match;
+	int (*init)(struct platform_device *pdev, struct rockchip_dfi *data,
+		    struct devfreq_event_desc *desc);
+
+	data = devm_kzalloc(dev, sizeof(struct rockchip_dfi), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
 
 	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
 	if (!desc)
 		return -ENOMEM;
 
-	desc->ops = &rockchip_dfi_ops;
+	match = of_match_node(rockchip_dfi_id_match, pdev->dev.of_node);
+	if (match) {
+		init = match->data;
+		if (init) {
+			if (init(pdev, data, desc))
+				return -EINVAL;
+		} else {
+			return 0;
+		}
+	} else {
+		return 0;
+	}
+
 	desc->driver_data = data;
 	desc->name = np->name;
-	data->desc = desc;
 
-	data->edev = devm_devfreq_event_add_edev(&pdev->dev, desc);
+	data->edev = devm_devfreq_event_add_edev(dev, desc);
 	if (IS_ERR(data->edev)) {
-		dev_err(&pdev->dev,
-			"failed to add devfreq-event device\n");
+		dev_err(dev, "failed to add devfreq-event device\n");
 		return PTR_ERR(data->edev);
 	}
+	data->desc = desc;
+	data->dev = &pdev->dev;
 
 	platform_set_drvdata(pdev, data);
 
diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig
index 594b77d89..2d55b177a 100644
--- a/drivers/dma-buf/Kconfig
+++ b/drivers/dma-buf/Kconfig
@@ -18,10 +18,9 @@ config SYNC_FILE
 	  Documentation/driver-api/sync_file.rst.
 
 config SW_SYNC
-	bool "Sync File Validation Framework"
+	tristate "Sync File Validation Framework"
 	default n
 	depends on SYNC_FILE
-	depends on DEBUG_FS
 	help
 	  A sync object driver that uses a 32bit counter to coordinate
 	  synchronization.  Useful when there is no hardware primitive backing
@@ -42,7 +41,6 @@ config UDMABUF
 config DMABUF_MOVE_NOTIFY
 	bool "Move notify between drivers (EXPERIMENTAL)"
 	default n
-	depends on DMA_SHARED_BUFFER
 	help
 	  Don't pin buffers if the dynamic DMA-buf interface is available on
 	  both the exporter as well as the importer. This fixes a security
@@ -56,19 +54,6 @@ config DMABUF_SELFTESTS
 	default n
 	depends on DMA_SHARED_BUFFER
 
-config DMABUF_PROCESS_INFO
-	bool "Show dmabuf usage of all processes"
-	default n
-	depends on DMA_SHARED_BUFFER
-	depends on PROC_FS || DEBUG_FS
-	help
-	  Choose this option to show dmabuf objects usage of all processes.
-	  Firstly, with this option, when a process creates a dmabuf object,
-	  its pid and task_comm will be recorded in the dmabuf.
-	  Secondly, this option creates dma_buf/process_bufinfo file in
-	  debugfs (if DEBUG_FS enabled) and process_dmabuf_info file in procfs
-	  (if PROC_FS enabled) to show dmabuf objects usage of all processes.
-
 menuconfig DMABUF_HEAPS
 	bool "DMA-BUF Userland Memory Heaps"
 	select DMA_SHARED_BUFFER
@@ -80,7 +65,7 @@ menuconfig DMABUF_HEAPS
 
 menuconfig DMABUF_SYSFS_STATS
 	bool "DMA-BUF sysfs statistics"
-	depends on DMA_SHARED_BUFFER
+	select DMA_SHARED_BUFFER
 	help
 	   Choose this option to enable DMA-BUF sysfs statistics
 	   in location /sys/kernel/dmabuf/buffers.
diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile
index cfbc5e3da..32757328b 100644
--- a/drivers/dma-buf/Makefile
+++ b/drivers/dma-buf/Makefile
@@ -4,7 +4,8 @@ obj-y := dma-buf.o dma-fence.o dma-fence-array.o dma-fence-chain.o \
 obj-$(CONFIG_DMABUF_HEAPS)	+= dma-heap.o
 obj-$(CONFIG_DMABUF_HEAPS)	+= heaps/
 obj-$(CONFIG_SYNC_FILE)		+= sync_file.o
-obj-$(CONFIG_SW_SYNC)		+= sw_sync.o sync_debug.o
+obj-$(CONFIG_SW_SYNC)		+= sw_sync.o
+obj-$(CONFIG_SW_SYNC_DEBUG)	+= sync_debug.o
 obj-$(CONFIG_UDMABUF)		+= udmabuf.o
 obj-$(CONFIG_DMABUF_SYSFS_STATS) += dma-buf-sysfs-stats.o
 
@@ -14,5 +15,3 @@ dmabuf_selftests-y := \
 	st-dma-fence-chain.o
 
 obj-$(CONFIG_DMABUF_SELFTESTS)	+= dmabuf_selftests.o
-
-obj-$(CONFIG_DMABUF_PROCESS_INFO)	+= dma-buf-process-info.o
diff --git a/drivers/dma-buf/dma-buf-sysfs-stats.c b/drivers/dma-buf/dma-buf-sysfs-stats.c
index 053baadca..2389a363b 100644
--- a/drivers/dma-buf/dma-buf-sysfs-stats.c
+++ b/drivers/dma-buf/dma-buf-sysfs-stats.c
@@ -16,40 +16,6 @@
 
 #define to_dma_buf_entry_from_kobj(x) container_of(x, struct dma_buf_sysfs_entry, kobj)
 
-/**
- * DOC: overview
- *
- * ``/sys/kernel/debug/dma_buf/bufinfo`` provides an overview of every DMA-BUF
- * in the system. However, since debugfs is not safe to be mounted in
- * production, procfs and sysfs can be used to gather DMA-BUF statistics on
- * production systems.
- *
- * The ``/proc/<pid>/fdinfo/<fd>`` files in procfs can be used to gather
- * information about DMA-BUF fds. Detailed documentation about the interface
- * is present in Documentation/filesystems/proc.rst.
- *
- * Unfortunately, the existing procfs interfaces can only provide information
- * about the DMA-BUFs for which processes hold fds or have the buffers mmapped
- * into their address space. This necessitated the creation of the DMA-BUF sysfs
- * statistics interface to provide per-buffer information on production systems.
- *
- * The interface at ``/sys/kernel/dma-buf/buffers`` exposes information about
- * every DMA-BUF when ``CONFIG_DMABUF_SYSFS_STATS`` is enabled.
- *
- * The following stats are exposed by the interface:
- *
- * * ``/sys/kernel/dmabuf/buffers/<inode_number>/exporter_name``
- * * ``/sys/kernel/dmabuf/buffers/<inode_number>/size``
- *
- * The information in the interface can also be used to derive per-exporter
- * statistics. The data from the interface can be gathered on error conditions
- * or other important events to provide a snapshot of DMA-BUF usage.
- * It can also be collected periodically by telemetry to monitor various metrics.
- *
- * Detailed documentation about the interface is present in
- * Documentation/ABI/testing/sysfs-kernel-dmabuf-buffers.
- */
-
 struct dma_buf_stats_attribute {
 	struct attribute attr;
 	ssize_t (*show)(struct dma_buf *dmabuf,
@@ -130,8 +96,9 @@ void dma_buf_stats_teardown(struct dma_buf *dmabuf)
 	kobject_put(&sysfs_entry->kobj);
 }
 
-
-/* Statistics files do not need to send uevents. */
+/*
+ * Statistics files do not need to send uevents.
+ */
 static int dmabuf_sysfs_uevent_filter(struct kset *kset, struct kobject *kobj)
 {
 	return 0;
diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index 89c10136b..068e0aeeb 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -30,9 +30,6 @@
 #include <uapi/linux/magic.h>
 
 #include "dma-buf-sysfs-stats.h"
-#include "dma-buf-process-info.h"
-
-static inline int is_dma_buf_file(struct file *);
 
 struct dma_buf_list {
 	struct list_head head;
@@ -41,6 +38,30 @@ struct dma_buf_list {
 
 static struct dma_buf_list db_list;
 
+/*
+ * This function helps in traversing the db_list and calls the
+ * callback function which can extract required info out of each
+ * dmabuf.
+ */
+int get_each_dmabuf(int (*callback)(const struct dma_buf *dmabuf,
+		    void *private), void *private)
+{
+	struct dma_buf *buf;
+	int ret = mutex_lock_interruptible(&db_list.lock);
+
+	if (ret)
+		return ret;
+
+	list_for_each_entry(buf, &db_list.head, list_node) {
+		ret = callback(buf, private);
+		if (ret)
+			break;
+	}
+	mutex_unlock(&db_list.lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(get_each_dmabuf);
+
 static char *dmabuffs_dname(struct dentry *dentry, char *buffer, int buflen)
 {
 	struct dma_buf *dmabuf;
@@ -60,6 +81,9 @@ static char *dmabuffs_dname(struct dentry *dentry, char *buffer, int buflen)
 static void dma_buf_release(struct dentry *dentry)
 {
 	struct dma_buf *dmabuf;
+#ifdef CONFIG_NO_GKI
+	int dtor_ret = 0;
+#endif
 
 	dmabuf = dentry->d_fsdata;
 	if (unlikely(!dmabuf))
@@ -77,13 +101,19 @@ static void dma_buf_release(struct dentry *dentry)
 	 */
 	BUG_ON(dmabuf->cb_shared.active || dmabuf->cb_excl.active);
 
-	dmabuf->ops->release(dmabuf);
+	dma_buf_stats_teardown(dmabuf);
+#ifdef CONFIG_NO_GKI
+	if (dmabuf->dtor)
+		dtor_ret = dmabuf->dtor(dmabuf, dmabuf->dtor_data);
+
+	if (!dtor_ret)
+#endif
+		dmabuf->ops->release(dmabuf);
 
 	if (dmabuf->resv == (struct dma_resv *)&dmabuf[1])
 		dma_resv_fini(dmabuf->resv);
 
-	WARN_ON(!list_empty(&dmabuf->attachments));
-	dma_buf_stats_teardown(dmabuf);
+    WARN_ON(!list_empty(&dmabuf->attachments));
 	module_put(dmabuf->owner);
 	kfree(dmabuf->name);
 	kfree(dmabuf);
@@ -328,6 +358,25 @@ static __poll_t dma_buf_poll(struct file *file, poll_table *poll)
 	return events;
 }
 
+static long _dma_buf_set_name(struct dma_buf *dmabuf, const char *name)
+{
+	long ret = 0;
+
+	dma_resv_lock(dmabuf->resv, NULL);
+	if (!list_empty(&dmabuf->attachments)) {
+		ret = -EBUSY;
+		goto out_unlock;
+	}
+	spin_lock(&dmabuf->name_lock);
+	kfree(dmabuf->name);
+	dmabuf->name = name;
+	spin_unlock(&dmabuf->name_lock);
+
+out_unlock:
+	dma_resv_unlock(dmabuf->resv);
+	return ret;
+}
+
 /**
  * dma_buf_set_name - Set a name to a specific dma_buf to track the usage.
  * The name of the dma-buf buffer can only be set when the dma-buf is not
@@ -343,7 +392,23 @@ static __poll_t dma_buf_poll(struct file *file, poll_table *poll)
  * devices, return -EBUSY.
  *
  */
-static long dma_buf_set_name(struct dma_buf *dmabuf, const char __user *buf)
+long dma_buf_set_name(struct dma_buf *dmabuf, const char *name)
+{
+	long ret = 0;
+	char *buf = kstrndup(name, DMA_BUF_NAME_LEN, GFP_KERNEL);
+
+	if (!buf)
+		return -ENOMEM;
+
+	ret = _dma_buf_set_name(dmabuf, buf);
+	if (ret)
+		kfree(buf);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dma_buf_set_name);
+
+static long dma_buf_set_name_user(struct dma_buf *dmabuf, const char __user *buf)
 {
 	char *name = strndup_user(buf, DMA_BUF_NAME_LEN);
 	long ret = 0;
@@ -351,19 +416,10 @@ static long dma_buf_set_name(struct dma_buf *dmabuf, const char __user *buf)
 	if (IS_ERR(name))
 		return PTR_ERR(name);
 
-	dma_resv_lock(dmabuf->resv, NULL);
-	if (!list_empty(&dmabuf->attachments)) {
-		ret = -EBUSY;
+	ret = _dma_buf_set_name(dmabuf, name);
+	if (ret)
 		kfree(name);
-		goto out_unlock;
-	}
-	spin_lock(&dmabuf->name_lock);
-	kfree(dmabuf->name);
-	dmabuf->name = name;
-	spin_unlock(&dmabuf->name_lock);
 
-out_unlock:
-	dma_resv_unlock(dmabuf->resv);
 	return ret;
 }
 
@@ -372,6 +428,7 @@ static long dma_buf_ioctl(struct file *file,
 {
 	struct dma_buf *dmabuf;
 	struct dma_buf_sync sync;
+	struct dma_buf_sync_partial sync_p;
 	enum dma_data_direction direction;
 	int ret;
 
@@ -408,7 +465,45 @@ static long dma_buf_ioctl(struct file *file,
 
 	case DMA_BUF_SET_NAME_A:
 	case DMA_BUF_SET_NAME_B:
-		return dma_buf_set_name(dmabuf, (const char __user *)arg);
+		return dma_buf_set_name_user(dmabuf, (const char __user *)arg);
+
+	case DMA_BUF_IOCTL_SYNC_PARTIAL:
+		if (copy_from_user(&sync_p, (void __user *) arg, sizeof(sync_p)))
+			return -EFAULT;
+
+		if (sync_p.len == 0)
+			return 0;
+
+		if (sync_p.len > dmabuf->size || sync_p.offset > dmabuf->size - sync_p.len)
+			return -EINVAL;
+
+		if (sync_p.flags & ~DMA_BUF_SYNC_VALID_FLAGS_MASK)
+			return -EINVAL;
+
+		switch (sync_p.flags & DMA_BUF_SYNC_RW) {
+		case DMA_BUF_SYNC_READ:
+			direction = DMA_FROM_DEVICE;
+			break;
+		case DMA_BUF_SYNC_WRITE:
+			direction = DMA_TO_DEVICE;
+			break;
+		case DMA_BUF_SYNC_RW:
+			direction = DMA_BIDIRECTIONAL;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		if (sync_p.flags & DMA_BUF_SYNC_END)
+			ret = dma_buf_end_cpu_access_partial(dmabuf, direction,
+							     sync_p.offset,
+							     sync_p.len);
+		else
+			ret = dma_buf_begin_cpu_access_partial(dmabuf, direction,
+							       sync_p.offset,
+							       sync_p.len);
+
+		return ret;
 
 	default:
 		return -ENOTTY;
@@ -442,10 +537,11 @@ static const struct file_operations dma_buf_fops = {
 /*
  * is_dma_buf_file - Check if struct file* is associated with dma_buf
  */
-static inline int is_dma_buf_file(struct file *file)
+int is_dma_buf_file(struct file *file)
 {
 	return file->f_op == &dma_buf_fops;
 }
+EXPORT_SYMBOL_GPL(is_dma_buf_file);
 
 static struct file *dma_buf_getfile(struct dma_buf *dmabuf, int flags)
 {
@@ -595,7 +691,6 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
 	list_add(&dmabuf->list_node, &db_list.head);
 	mutex_unlock(&db_list.lock);
 
-	init_dma_buf_task_info(dmabuf);
 	return dmabuf;
 
 err_sysfs:
@@ -1132,6 +1227,30 @@ int dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
 }
 EXPORT_SYMBOL_GPL(dma_buf_begin_cpu_access);
 
+int dma_buf_begin_cpu_access_partial(struct dma_buf *dmabuf,
+				     enum dma_data_direction direction,
+				     unsigned int offset, unsigned int len)
+{
+	int ret = 0;
+
+	if (WARN_ON(!dmabuf))
+		return -EINVAL;
+
+	if (dmabuf->ops->begin_cpu_access_partial)
+		ret = dmabuf->ops->begin_cpu_access_partial(dmabuf, direction,
+							    offset, len);
+
+	/* Ensure that all fences are waited upon - but we first allow
+	 * the native handler the chance to do so more efficiently if it
+	 * chooses. A double invocation here will be reasonably cheap no-op.
+	 */
+	if (ret == 0)
+		ret = __dma_buf_begin_cpu_access(dmabuf, direction);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dma_buf_begin_cpu_access_partial);
+
 /**
  * dma_buf_end_cpu_access - Must be called after accessing a dma_buf from the
  * cpu in the kernel context. Calls end_cpu_access to allow exporter-specific
@@ -1158,6 +1277,21 @@ int dma_buf_end_cpu_access(struct dma_buf *dmabuf,
 }
 EXPORT_SYMBOL_GPL(dma_buf_end_cpu_access);
 
+int dma_buf_end_cpu_access_partial(struct dma_buf *dmabuf,
+				   enum dma_data_direction direction,
+				   unsigned int offset, unsigned int len)
+{
+	int ret = 0;
+
+	WARN_ON(!dmabuf);
+
+	if (dmabuf->ops->end_cpu_access_partial)
+		ret = dmabuf->ops->end_cpu_access_partial(dmabuf, direction,
+							  offset, len);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dma_buf_end_cpu_access_partial);
 
 /**
  * dma_buf_mmap - Setup up a userspace mmap with the given vma
@@ -1286,6 +1420,32 @@ void dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr)
 }
 EXPORT_SYMBOL_GPL(dma_buf_vunmap);
 
+int dma_buf_get_flags(struct dma_buf *dmabuf, unsigned long *flags)
+{
+	int ret = 0;
+
+	if (WARN_ON(!dmabuf) || !flags)
+		return -EINVAL;
+
+	if (dmabuf->ops->get_flags)
+		ret = dmabuf->ops->get_flags(dmabuf, flags);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(dma_buf_get_flags);
+
+int dma_buf_get_uuid(struct dma_buf *dmabuf, uuid_t *uuid)
+{
+	if (WARN_ON(!dmabuf) || !uuid)
+		return -EINVAL;
+
+	if (!dmabuf->ops->get_uuid)
+		return -ENODEV;
+
+	return dmabuf->ops->get_uuid(dmabuf, uuid);
+}
+EXPORT_SYMBOL_GPL(dma_buf_get_uuid);
+
 #ifdef CONFIG_DEBUG_FS
 static int dma_buf_debug_show(struct seq_file *s, void *unused)
 {
@@ -1305,10 +1465,8 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused)
 		return ret;
 
 	seq_puts(s, "\nDma-buf Objects:\n");
-	seq_printf(s, "%-8s\t%-8s\t%-8s\t%-8s\texp_name\t%-8s\t"
-		   "%-16s\t%-16s\t%-16s\n",
-		   "size", "flags", "mode", "count", "ino",
-		   "buf_name", "exp_pid",  "exp_task_comm");
+	seq_printf(s, "%-8s\t%-8s\t%-8s\t%-8s\texp_name\t%-8s\n",
+		   "size", "flags", "mode", "count", "ino");
 
 	list_for_each_entry(buf_obj, &db_list.head, list_node) {
 
@@ -1316,16 +1474,15 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused)
 		if (ret)
 			goto error_unlock;
 
-		seq_printf(s, "%08zu\t%08x\t%08x\t%08ld\t%s\t%08lu\t%s\t"
-			   "%-16d\t%-16s\n",
+		spin_lock(&buf_obj->name_lock);
+		seq_printf(s, "%08zu\t%08x\t%08x\t%08ld\t%s\t%08lu\t%s\n",
 				buf_obj->size,
 				buf_obj->file->f_flags, buf_obj->file->f_mode,
 				file_count(buf_obj->file),
 				buf_obj->exp_name,
 				file_inode(buf_obj->file)->i_ino,
-				buf_obj->name ?: "NULL",
-				dma_buf_exp_pid(buf_obj),
-				dma_buf_exp_task_comm(buf_obj) ?: "NULL");
+				buf_obj->name ?: "");
+		spin_unlock(&buf_obj->name_lock);
 
 		robj = buf_obj->resv;
 		while (true) {
@@ -1406,7 +1563,6 @@ static int dma_buf_init_debugfs(void)
 		err = PTR_ERR(d);
 	}
 
-	dma_buf_process_info_init_debugfs(dma_buf_debugfs_dir);
 	return err;
 }
 
@@ -1424,19 +1580,6 @@ static inline void dma_buf_uninit_debugfs(void)
 }
 #endif
 
-#ifdef CONFIG_DMABUF_PROCESS_INFO
-struct dma_buf *get_dma_buf_from_file(struct file *f)
-{
-	if (IS_ERR_OR_NULL(f))
-		return NULL;
-
-	if (!is_dma_buf_file(f))
-		return NULL;
-
-	return f->private_data;
-}
-#endif /* CONFIG_DMABUF_PROCESS_INFO */
-
 static int __init dma_buf_init(void)
 {
 	int ret;
@@ -1452,7 +1595,6 @@ static int __init dma_buf_init(void)
 	mutex_init(&db_list.lock);
 	INIT_LIST_HEAD(&db_list.head);
 	dma_buf_init_debugfs();
-	dma_buf_process_info_init_procfs();
 	return 0;
 }
 subsys_initcall(dma_buf_init);
@@ -1462,6 +1604,5 @@ static void __exit dma_buf_deinit(void)
 	dma_buf_uninit_debugfs();
 	kern_unmount(dma_buf_mnt);
 	dma_buf_uninit_sysfs_statistics();
-	dma_buf_process_info_uninit_procfs();
 }
 __exitcall(dma_buf_deinit);
diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c
index 7475e09b0..d64fc0392 100644
--- a/drivers/dma-buf/dma-fence.c
+++ b/drivers/dma-buf/dma-fence.c
@@ -312,22 +312,25 @@ void __dma_fence_might_wait(void)
 
 
 /**
- * dma_fence_signal_locked - signal completion of a fence
+ * dma_fence_signal_timestamp_locked - signal completion of a fence
  * @fence: the fence to signal
+ * @timestamp: fence signal timestamp in kernel's CLOCK_MONOTONIC time domain
  *
  * Signal completion for software callbacks on a fence, this will unblock
  * dma_fence_wait() calls and run all the callbacks added with
  * dma_fence_add_callback(). Can be called multiple times, but since a fence
  * can only go from the unsignaled to the signaled state and not back, it will
- * only be effective the first time.
+ * only be effective the first time. Set the timestamp provided as the fence
+ * signal timestamp.
  *
- * Unlike dma_fence_signal(), this function must be called with &dma_fence.lock
- * held.
+ * Unlike dma_fence_signal_timestamp(), this function must be called with
+ * &dma_fence.lock held.
  *
  * Returns 0 on success and a negative error value when @fence has been
  * signalled already.
  */
-int dma_fence_signal_locked(struct dma_fence *fence)
+int dma_fence_signal_timestamp_locked(struct dma_fence *fence,
+				      ktime_t timestamp)
 {
 	struct dma_fence_cb *cur, *tmp;
 	struct list_head cb_list;
@@ -341,7 +344,7 @@ int dma_fence_signal_locked(struct dma_fence *fence)
 	/* Stash the cb_list before replacing it with the timestamp */
 	list_replace(&fence->cb_list, &cb_list);
 
-	fence->timestamp = ktime_get();
+	fence->timestamp = timestamp;
 	set_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags);
 	trace_dma_fence_signaled(fence);
 
@@ -352,6 +355,59 @@ int dma_fence_signal_locked(struct dma_fence *fence)
 
 	return 0;
 }
+EXPORT_SYMBOL(dma_fence_signal_timestamp_locked);
+
+/**
+ * dma_fence_signal_timestamp - signal completion of a fence
+ * @fence: the fence to signal
+ * @timestamp: fence signal timestamp in kernel's CLOCK_MONOTONIC time domain
+ *
+ * Signal completion for software callbacks on a fence, this will unblock
+ * dma_fence_wait() calls and run all the callbacks added with
+ * dma_fence_add_callback(). Can be called multiple times, but since a fence
+ * can only go from the unsignaled to the signaled state and not back, it will
+ * only be effective the first time. Set the timestamp provided as the fence
+ * signal timestamp.
+ *
+ * Returns 0 on success and a negative error value when @fence has been
+ * signalled already.
+ */
+int dma_fence_signal_timestamp(struct dma_fence *fence, ktime_t timestamp)
+{
+	unsigned long flags;
+	int ret;
+
+	if (!fence)
+		return -EINVAL;
+
+	spin_lock_irqsave(fence->lock, flags);
+	ret = dma_fence_signal_timestamp_locked(fence, timestamp);
+	spin_unlock_irqrestore(fence->lock, flags);
+
+	return ret;
+}
+EXPORT_SYMBOL(dma_fence_signal_timestamp);
+
+/**
+ * dma_fence_signal_locked - signal completion of a fence
+ * @fence: the fence to signal
+ *
+ * Signal completion for software callbacks on a fence, this will unblock
+ * dma_fence_wait() calls and run all the callbacks added with
+ * dma_fence_add_callback(). Can be called multiple times, but since a fence
+ * can only go from the unsignaled to the signaled state and not back, it will
+ * only be effective the first time.
+ *
+ * Unlike dma_fence_signal(), this function must be called with &dma_fence.lock
+ * held.
+ *
+ * Returns 0 on success and a negative error value when @fence has been
+ * signalled already.
+ */
+int dma_fence_signal_locked(struct dma_fence *fence)
+{
+	return dma_fence_signal_timestamp_locked(fence, ktime_get());
+}
 EXPORT_SYMBOL(dma_fence_signal_locked);
 
 /**
@@ -379,7 +435,7 @@ int dma_fence_signal(struct dma_fence *fence)
 	tmp = dma_fence_begin_signalling();
 
 	spin_lock_irqsave(fence->lock, flags);
-	ret = dma_fence_signal_locked(fence);
+	ret = dma_fence_signal_timestamp_locked(fence, ktime_get());
 	spin_unlock_irqrestore(fence->lock, flags);
 
 	dma_fence_end_signalling(tmp);
diff --git a/drivers/dma-buf/dma-heap.c b/drivers/dma-buf/dma-heap.c
index bbbfa28b2..4fb22001b 100644
--- a/drivers/dma-buf/dma-heap.c
+++ b/drivers/dma-buf/dma-heap.c
@@ -14,7 +14,6 @@
 #include <linux/xarray.h>
 #include <linux/list.h>
 #include <linux/slab.h>
-#include <linux/nospec.h>
 #include <linux/uaccess.h>
 #include <linux/syscalls.h>
 #include <linux/dma-heap.h>
@@ -31,6 +30,7 @@
  * @heap_devt		heap device node
  * @list		list head connecting to list of heaps
  * @heap_cdev		heap char device
+ * @heap_dev		heap device struct
  *
  * Represents a heap of memory from which buffers can be made.
  */
@@ -41,6 +41,8 @@ struct dma_heap {
 	dev_t heap_devt;
 	struct list_head list;
 	struct cdev heap_cdev;
+	struct kref refcount;
+	struct device *heap_dev;
 };
 
 static LIST_HEAD(heap_list);
@@ -49,20 +51,72 @@ static dev_t dma_heap_devt;
 static struct class *dma_heap_class;
 static DEFINE_XARRAY_ALLOC(dma_heap_minors);
 
-static int dma_heap_buffer_alloc(struct dma_heap *heap, size_t len,
-				 unsigned int fd_flags,
-				 unsigned int heap_flags)
+struct dma_heap *dma_heap_find(const char *name)
 {
+	struct dma_heap *h;
+
+	mutex_lock(&heap_list_lock);
+	list_for_each_entry(h, &heap_list, list) {
+		if (!strcmp(h->name, name)) {
+			kref_get(&h->refcount);
+			mutex_unlock(&heap_list_lock);
+			return h;
+		}
+	}
+	mutex_unlock(&heap_list_lock);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(dma_heap_find);
+
+
+void dma_heap_buffer_free(struct dma_buf *dmabuf)
+{
+	dma_buf_put(dmabuf);
+}
+EXPORT_SYMBOL_GPL(dma_heap_buffer_free);
+
+struct dma_buf *dma_heap_buffer_alloc(struct dma_heap *heap, size_t len,
+				      unsigned int fd_flags,
+				      unsigned int heap_flags)
+{
+	if (fd_flags & ~DMA_HEAP_VALID_FD_FLAGS)
+		return ERR_PTR(-EINVAL);
+
+	if (heap_flags & ~DMA_HEAP_VALID_HEAP_FLAGS)
+		return ERR_PTR(-EINVAL);
 	/*
 	 * Allocations from all heaps have to begin
 	 * and end on page boundaries.
 	 */
 	len = PAGE_ALIGN(len);
 	if (!len)
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 
 	return heap->ops->allocate(heap, len, fd_flags, heap_flags);
 }
+EXPORT_SYMBOL_GPL(dma_heap_buffer_alloc);
+
+int dma_heap_bufferfd_alloc(struct dma_heap *heap, size_t len,
+			    unsigned int fd_flags,
+			    unsigned int heap_flags)
+{
+	struct dma_buf *dmabuf;
+	int fd;
+
+	dmabuf = dma_heap_buffer_alloc(heap, len, fd_flags, heap_flags);
+
+	if (IS_ERR(dmabuf))
+		return PTR_ERR(dmabuf);
+
+	fd = dma_buf_fd(dmabuf, fd_flags);
+	if (fd < 0) {
+		dma_buf_put(dmabuf);
+		/* just return, as put will call release and that will free */
+	}
+	return fd;
+
+}
+EXPORT_SYMBOL_GPL(dma_heap_bufferfd_alloc);
 
 static int dma_heap_open(struct inode *inode, struct file *file)
 {
@@ -90,15 +144,9 @@ static long dma_heap_ioctl_allocate(struct file *file, void *data)
 	if (heap_allocation->fd)
 		return -EINVAL;
 
-	if (heap_allocation->fd_flags & ~DMA_HEAP_VALID_FD_FLAGS)
-		return -EINVAL;
-
-	if (heap_allocation->heap_flags & ~DMA_HEAP_VALID_HEAP_FLAGS)
-		return -EINVAL;
-
-	fd = dma_heap_buffer_alloc(heap, heap_allocation->len,
-				   heap_allocation->fd_flags,
-				   heap_allocation->heap_flags);
+	fd = dma_heap_bufferfd_alloc(heap, heap_allocation->len,
+				     heap_allocation->fd_flags,
+				     heap_allocation->heap_flags);
 	if (fd < 0)
 		return fd;
 
@@ -124,7 +172,6 @@ static long dma_heap_ioctl(struct file *file, unsigned int ucmd,
 	if (nr >= ARRAY_SIZE(dma_heap_ioctl_cmds))
 		return -EINVAL;
 
-	nr = array_index_nospec(nr, ARRAY_SIZE(dma_heap_ioctl_cmds));
 	/* Get the kernel ioctl cmd that matches */
 	kcmd = dma_heap_ioctl_cmds[nr];
 
@@ -191,6 +238,47 @@ void *dma_heap_get_drvdata(struct dma_heap *heap)
 {
 	return heap->priv;
 }
+EXPORT_SYMBOL_GPL(dma_heap_get_drvdata);
+
+static void dma_heap_release(struct kref *ref)
+{
+	struct dma_heap *heap = container_of(ref, struct dma_heap, refcount);
+	int minor = MINOR(heap->heap_devt);
+
+	/* Note, we already holding the heap_list_lock here */
+	list_del(&heap->list);
+
+	device_destroy(dma_heap_class, heap->heap_devt);
+	cdev_del(&heap->heap_cdev);
+	xa_erase(&dma_heap_minors, minor);
+
+	kfree(heap);
+}
+
+void dma_heap_put(struct dma_heap *h)
+{
+	/*
+	 * Take the heap_list_lock now to avoid racing with code
+	 * scanning the list and then taking a kref.
+	 */
+	mutex_lock(&heap_list_lock);
+	kref_put(&h->refcount, dma_heap_release);
+	mutex_unlock(&heap_list_lock);
+}
+EXPORT_SYMBOL_GPL(dma_heap_put);
+
+/**
+ * dma_heap_get_dev() - get device struct for the heap
+ * @heap: DMA-Heap to retrieve device struct from
+ *
+ * Returns:
+ * The device struct for the heap.
+ */
+struct device *dma_heap_get_dev(struct dma_heap *heap)
+{
+	return heap->heap_dev;
+}
+EXPORT_SYMBOL_GPL(dma_heap_get_dev);
 
 /**
  * dma_heap_get_name() - get heap name
@@ -203,11 +291,11 @@ const char *dma_heap_get_name(struct dma_heap *heap)
 {
 	return heap->name;
 }
+EXPORT_SYMBOL_GPL(dma_heap_get_name);
 
 struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info)
 {
-	struct dma_heap *heap, *h, *err_ret;
-	struct device *dev_ret;
+	struct dma_heap *heap, *err_ret;
 	unsigned int minor;
 	int ret;
 
@@ -221,10 +309,20 @@ struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info)
 		return ERR_PTR(-EINVAL);
 	}
 
+	/* check the name is unique */
+	heap = dma_heap_find(exp_info->name);
+	if (heap) {
+		pr_err("dma_heap: Already registered heap named %s\n",
+		       exp_info->name);
+		dma_heap_put(heap);
+		return ERR_PTR(-EINVAL);
+	}
+
 	heap = kzalloc(sizeof(*heap), GFP_KERNEL);
 	if (!heap)
 		return ERR_PTR(-ENOMEM);
 
+	kref_init(&heap->refcount);
 	heap->name = exp_info->name;
 	heap->ops = exp_info->ops;
 	heap->priv = exp_info->priv;
@@ -249,37 +347,27 @@ struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info)
 		goto err1;
 	}
 
-	dev_ret = device_create(dma_heap_class,
-				NULL,
-				heap->heap_devt,
-				NULL,
-				heap->name);
-	if (IS_ERR(dev_ret)) {
+	heap->heap_dev = device_create(dma_heap_class,
+				       NULL,
+				       heap->heap_devt,
+				       NULL,
+				       heap->name);
+	if (IS_ERR(heap->heap_dev)) {
 		pr_err("dma_heap: Unable to create device\n");
-		err_ret = ERR_CAST(dev_ret);
+		err_ret = ERR_CAST(heap->heap_dev);
 		goto err2;
 	}
 
-	mutex_lock(&heap_list_lock);
-	/* check the name is unique */
-	list_for_each_entry(h, &heap_list, list) {
-		if (!strcmp(h->name, exp_info->name)) {
-			mutex_unlock(&heap_list_lock);
-			pr_err("dma_heap: Already registered heap named %s\n",
-			       exp_info->name);
-			err_ret = ERR_PTR(-EINVAL);
-			goto err3;
-		}
-	}
+	/* Make sure it doesn't disappear on us */
+	heap->heap_dev = get_device(heap->heap_dev);
 
 	/* Add heap to the list */
+	mutex_lock(&heap_list_lock);
 	list_add(&heap->list, &heap_list);
 	mutex_unlock(&heap_list_lock);
 
 	return heap;
 
-err3:
-	device_destroy(dma_heap_class, heap->heap_devt);
 err2:
 	cdev_del(&heap->heap_cdev);
 err1:
@@ -288,27 +376,88 @@ struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info)
 	kfree(heap);
 	return err_ret;
 }
+EXPORT_SYMBOL_GPL(dma_heap_add);
 
 static char *dma_heap_devnode(struct device *dev, umode_t *mode)
 {
 	return kasprintf(GFP_KERNEL, "dma_heap/%s", dev_name(dev));
 }
 
+static ssize_t total_pools_kb_show(struct kobject *kobj,
+				   struct kobj_attribute *attr, char *buf)
+{
+	struct dma_heap *heap;
+	u64 total_pool_size = 0;
+
+	mutex_lock(&heap_list_lock);
+	list_for_each_entry(heap, &heap_list, list) {
+		if (heap->ops->get_pool_size)
+			total_pool_size += heap->ops->get_pool_size(heap);
+	}
+	mutex_unlock(&heap_list_lock);
+
+	return sysfs_emit(buf, "%llu\n", total_pool_size / 1024);
+}
+
+static struct kobj_attribute total_pools_kb_attr =
+	__ATTR_RO(total_pools_kb);
+
+static struct attribute *dma_heap_sysfs_attrs[] = {
+	&total_pools_kb_attr.attr,
+	NULL,
+};
+
+ATTRIBUTE_GROUPS(dma_heap_sysfs);
+
+static struct kobject *dma_heap_kobject;
+
+static int dma_heap_sysfs_setup(void)
+{
+	int ret;
+
+	dma_heap_kobject = kobject_create_and_add("dma_heap", kernel_kobj);
+	if (!dma_heap_kobject)
+		return -ENOMEM;
+
+	ret = sysfs_create_groups(dma_heap_kobject, dma_heap_sysfs_groups);
+	if (ret) {
+		kobject_put(dma_heap_kobject);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void dma_heap_sysfs_teardown(void)
+{
+	kobject_put(dma_heap_kobject);
+}
+
 static int dma_heap_init(void)
 {
 	int ret;
 
-	ret = alloc_chrdev_region(&dma_heap_devt, 0, NUM_HEAP_MINORS, DEVNAME);
+	ret = dma_heap_sysfs_setup();
 	if (ret)
 		return ret;
 
+	ret = alloc_chrdev_region(&dma_heap_devt, 0, NUM_HEAP_MINORS, DEVNAME);
+	if (ret)
+		goto err_chrdev;
+
 	dma_heap_class = class_create(THIS_MODULE, DEVNAME);
 	if (IS_ERR(dma_heap_class)) {
-		unregister_chrdev_region(dma_heap_devt, NUM_HEAP_MINORS);
-		return PTR_ERR(dma_heap_class);
+		ret = PTR_ERR(dma_heap_class);
+		goto err_class;
 	}
 	dma_heap_class->devnode = dma_heap_devnode;
 
 	return 0;
+
+err_class:
+	unregister_chrdev_region(dma_heap_devt, NUM_HEAP_MINORS);
+err_chrdev:
+	dma_heap_sysfs_teardown();
+	return ret;
 }
 subsys_initcall(dma_heap_init);
diff --git a/drivers/dma-buf/heaps/Kconfig b/drivers/dma-buf/heaps/Kconfig
index a5eef06c4..341d6d50f 100644
--- a/drivers/dma-buf/heaps/Kconfig
+++ b/drivers/dma-buf/heaps/Kconfig
@@ -1,12 +1,12 @@
 config DMABUF_HEAPS_SYSTEM
-	bool "DMA-BUF System Heap"
-	depends on DMABUF_HEAPS
+	tristate "DMA-BUF System Heap"
+	depends on DMABUF_HEAPS && DMABUF_HEAPS_DEFERRED_FREE && DMABUF_HEAPS_PAGE_POOL
 	help
 	  Choose this option to enable the system dmabuf heap. The system heap
 	  is backed by pages from the buddy allocator. If in doubt, say Y.
 
 config DMABUF_HEAPS_CMA
-	bool "DMA-BUF CMA Heap"
+	tristate "DMA-BUF CMA Heap"
 	depends on DMABUF_HEAPS && DMA_CMA
 	help
 	  Choose this option to enable dma-buf CMA heap. This heap is backed
diff --git a/drivers/dma-buf/heaps/Makefile b/drivers/dma-buf/heaps/Makefile
index 6e54cdec3..d06078f26 100644
--- a/drivers/dma-buf/heaps/Makefile
+++ b/drivers/dma-buf/heaps/Makefile
@@ -1,4 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0
-obj-y					+= heap-helpers.o
+
 obj-$(CONFIG_DMABUF_HEAPS_SYSTEM)	+= system_heap.o
 obj-$(CONFIG_DMABUF_HEAPS_CMA)		+= cma_heap.o
diff --git a/drivers/dma-buf/heaps/cma_heap.c b/drivers/dma-buf/heaps/cma_heap.c
index e55384dc1..fd564aa70 100644
--- a/drivers/dma-buf/heaps/cma_heap.c
+++ b/drivers/dma-buf/heaps/cma_heap.c
@@ -2,76 +2,306 @@
 /*
  * DMABUF CMA heap exporter
  *
- * Copyright (C) 2012, 2019 Linaro Ltd.
+ * Copyright (C) 2012, 2019, 2020 Linaro Ltd.
  * Author: <benjamin.gaignard@linaro.org> for ST-Ericsson.
+ *
+ * Also utilizing parts of Andrew Davis' SRAM heap:
+ * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
+ *	Andrew F. Davis <afd@ti.com>
  */
-
 #include <linux/cma.h>
-#include <linux/device.h>
 #include <linux/dma-buf.h>
 #include <linux/dma-heap.h>
 #include <linux/dma-map-ops.h>
 #include <linux/err.h>
-#include <linux/errno.h>
 #include <linux/highmem.h>
+#include <linux/io.h>
+#include <linux/mm.h>
 #include <linux/module.h>
-#include <linux/slab.h>
 #include <linux/scatterlist.h>
-#include <linux/sched/signal.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
 
-#include "heap-helpers.h"
 
 struct cma_heap {
 	struct dma_heap *heap;
 	struct cma *cma;
 };
 
-static void cma_heap_free(struct heap_helper_buffer *buffer)
+struct cma_heap_buffer {
+	struct cma_heap *heap;
+	struct list_head attachments;
+	struct mutex lock;
+	unsigned long len;
+	struct page *cma_pages;
+	struct page **pages;
+	pgoff_t pagecount;
+	int vmap_cnt;
+	void *vaddr;
+};
+
+struct dma_heap_attachment {
+	struct device *dev;
+	struct sg_table table;
+	struct list_head list;
+	bool mapped;
+};
+
+static int cma_heap_attach(struct dma_buf *dmabuf,
+			   struct dma_buf_attachment *attachment)
+{
+	struct cma_heap_buffer *buffer = dmabuf->priv;
+	struct dma_heap_attachment *a;
+	int ret;
+
+	a = kzalloc(sizeof(*a), GFP_KERNEL);
+	if (!a)
+		return -ENOMEM;
+
+	ret = sg_alloc_table_from_pages(&a->table, buffer->pages,
+					buffer->pagecount, 0,
+					buffer->pagecount << PAGE_SHIFT,
+					GFP_KERNEL);
+	if (ret) {
+		kfree(a);
+		return ret;
+	}
+
+	a->dev = attachment->dev;
+	INIT_LIST_HEAD(&a->list);
+	a->mapped = false;
+
+	attachment->priv = a;
+
+	mutex_lock(&buffer->lock);
+	list_add(&a->list, &buffer->attachments);
+	mutex_unlock(&buffer->lock);
+
+	return 0;
+}
+
+static void cma_heap_detach(struct dma_buf *dmabuf,
+			    struct dma_buf_attachment *attachment)
+{
+	struct cma_heap_buffer *buffer = dmabuf->priv;
+	struct dma_heap_attachment *a = attachment->priv;
+
+	mutex_lock(&buffer->lock);
+	list_del(&a->list);
+	mutex_unlock(&buffer->lock);
+
+	sg_free_table(&a->table);
+	kfree(a);
+}
+
+static struct sg_table *cma_heap_map_dma_buf(struct dma_buf_attachment *attachment,
+					     enum dma_data_direction direction)
 {
-	struct cma_heap *cma_heap = dma_heap_get_drvdata(buffer->heap);
-	unsigned long nr_pages = buffer->pagecount;
-	struct page *cma_pages = buffer->priv_virt;
+	struct dma_heap_attachment *a = attachment->priv;
+	struct sg_table *table = &a->table;
+	int attrs = attachment->dma_map_attrs;
+	int ret;
+
+	ret = dma_map_sgtable(attachment->dev, table, direction, attrs);
+	if (ret)
+		return ERR_PTR(-ENOMEM);
+	a->mapped = true;
+	return table;
+}
+
+static void cma_heap_unmap_dma_buf(struct dma_buf_attachment *attachment,
+				   struct sg_table *table,
+				   enum dma_data_direction direction)
+{
+	struct dma_heap_attachment *a = attachment->priv;
+	int attrs = attachment->dma_map_attrs;
+
+	a->mapped = false;
+	dma_unmap_sgtable(attachment->dev, table, direction, attrs);
+}
+
+static int cma_heap_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
+					     enum dma_data_direction direction)
+{
+	struct cma_heap_buffer *buffer = dmabuf->priv;
+	struct dma_heap_attachment *a;
+
+	if (buffer->vmap_cnt)
+		invalidate_kernel_vmap_range(buffer->vaddr, buffer->len);
+
+	mutex_lock(&buffer->lock);
+	list_for_each_entry(a, &buffer->attachments, list) {
+		if (!a->mapped)
+			continue;
+		dma_sync_sgtable_for_cpu(a->dev, &a->table, direction);
+	}
+	mutex_unlock(&buffer->lock);
+
+	return 0;
+}
+
+static int cma_heap_dma_buf_end_cpu_access(struct dma_buf *dmabuf,
+					   enum dma_data_direction direction)
+{
+	struct cma_heap_buffer *buffer = dmabuf->priv;
+	struct dma_heap_attachment *a;
+
+	if (buffer->vmap_cnt)
+		flush_kernel_vmap_range(buffer->vaddr, buffer->len);
+
+	mutex_lock(&buffer->lock);
+	list_for_each_entry(a, &buffer->attachments, list) {
+		if (!a->mapped)
+			continue;
+		dma_sync_sgtable_for_device(a->dev, &a->table, direction);
+	}
+	mutex_unlock(&buffer->lock);
+
+	return 0;
+}
+
+static vm_fault_t cma_heap_vm_fault(struct vm_fault *vmf)
+{
+	struct vm_area_struct *vma = vmf->vma;
+	struct cma_heap_buffer *buffer = vma->vm_private_data;
+
+	if (vmf->pgoff > buffer->pagecount)
+		return VM_FAULT_SIGBUS;
+
+	vmf->page = buffer->pages[vmf->pgoff];
+	get_page(vmf->page);
+
+	return 0;
+}
+
+static const struct vm_operations_struct dma_heap_vm_ops = {
+	.fault = cma_heap_vm_fault,
+};
+
+static int cma_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
+{
+	struct cma_heap_buffer *buffer = dmabuf->priv;
+
+	if ((vma->vm_flags & (VM_SHARED | VM_MAYSHARE)) == 0)
+		return -EINVAL;
+
+	vma->vm_ops = &dma_heap_vm_ops;
+	vma->vm_private_data = buffer;
+
+	return 0;
+}
+
+static void *cma_heap_do_vmap(struct cma_heap_buffer *buffer)
+{
+	void *vaddr;
+
+	vaddr = vmap(buffer->pages, buffer->pagecount, VM_MAP, PAGE_KERNEL);
+	if (!vaddr)
+		return ERR_PTR(-ENOMEM);
+
+	return vaddr;
+}
+
+static void *cma_heap_vmap(struct dma_buf *dmabuf)
+{
+	struct cma_heap_buffer *buffer = dmabuf->priv;
+	void *vaddr;
+
+	mutex_lock(&buffer->lock);
+	if (buffer->vmap_cnt) {
+		buffer->vmap_cnt++;
+		vaddr = buffer->vaddr;
+		goto out;
+	}
+
+	vaddr = cma_heap_do_vmap(buffer);
+	if (IS_ERR(vaddr))
+		goto out;
+
+	buffer->vaddr = vaddr;
+	buffer->vmap_cnt++;
+out:
+	mutex_unlock(&buffer->lock);
+
+	return vaddr;
+}
+
+static void cma_heap_vunmap(struct dma_buf *dmabuf, void *vaddr)
+{
+	struct cma_heap_buffer *buffer = dmabuf->priv;
+
+	mutex_lock(&buffer->lock);
+	if (!--buffer->vmap_cnt) {
+		vunmap(buffer->vaddr);
+		buffer->vaddr = NULL;
+	}
+	mutex_unlock(&buffer->lock);
+}
+
+static void cma_heap_dma_buf_release(struct dma_buf *dmabuf)
+{
+	struct cma_heap_buffer *buffer = dmabuf->priv;
+	struct cma_heap *cma_heap = buffer->heap;
+
+	if (buffer->vmap_cnt > 0) {
+		WARN(1, "%s: buffer still mapped in the kernel\n", __func__);
+		vunmap(buffer->vaddr);
+	}
 
 	/* free page list */
 	kfree(buffer->pages);
 	/* release memory */
-	cma_release(cma_heap->cma, cma_pages, nr_pages);
+	cma_release(cma_heap->cma, buffer->cma_pages, buffer->pagecount);
 	kfree(buffer);
 }
 
-/* dmabuf heap CMA operations functions */
-static int cma_heap_allocate(struct dma_heap *heap,
-			     unsigned long len,
-			     unsigned long fd_flags,
-			     unsigned long heap_flags)
+static const struct dma_buf_ops cma_heap_buf_ops = {
+	.attach = cma_heap_attach,
+	.detach = cma_heap_detach,
+	.map_dma_buf = cma_heap_map_dma_buf,
+	.unmap_dma_buf = cma_heap_unmap_dma_buf,
+	.begin_cpu_access = cma_heap_dma_buf_begin_cpu_access,
+	.end_cpu_access = cma_heap_dma_buf_end_cpu_access,
+	.mmap = cma_heap_mmap,
+	.vmap = cma_heap_vmap,
+	.vunmap = cma_heap_vunmap,
+	.release = cma_heap_dma_buf_release,
+};
+
+static struct dma_buf *cma_heap_allocate(struct dma_heap *heap,
+					 unsigned long len,
+					 unsigned long fd_flags,
+					 unsigned long heap_flags)
 {
 	struct cma_heap *cma_heap = dma_heap_get_drvdata(heap);
-	struct heap_helper_buffer *helper_buffer;
-	struct page *cma_pages;
+	struct cma_heap_buffer *buffer;
+	DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
 	size_t size = PAGE_ALIGN(len);
-	unsigned long nr_pages = size >> PAGE_SHIFT;
+	pgoff_t pagecount = size >> PAGE_SHIFT;
 	unsigned long align = get_order(size);
+	struct page *cma_pages;
 	struct dma_buf *dmabuf;
 	int ret = -ENOMEM;
 	pgoff_t pg;
 
-	if (align > CONFIG_CMA_ALIGNMENT)
-		align = CONFIG_CMA_ALIGNMENT;
+	buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
+	if (!buffer)
+		return ERR_PTR(-ENOMEM);
 
-	helper_buffer = kzalloc(sizeof(*helper_buffer), GFP_KERNEL);
-	if (!helper_buffer)
-		return -ENOMEM;
+	INIT_LIST_HEAD(&buffer->attachments);
+	mutex_init(&buffer->lock);
+	buffer->len = size;
 
-	init_heap_helper_buffer(helper_buffer, cma_heap_free);
-	helper_buffer->heap = heap;
-	helper_buffer->size = len;
+	if (align > CONFIG_CMA_ALIGNMENT)
+		align = CONFIG_CMA_ALIGNMENT;
 
-	cma_pages = cma_alloc(cma_heap->cma, nr_pages, align, false);
+	cma_pages = cma_alloc(cma_heap->cma, pagecount, align, GFP_KERNEL);
 	if (!cma_pages)
-		goto free_buf;
+		goto free_buffer;
 
+	/* Clear the cma pages */
 	if (PageHighMem(cma_pages)) {
-		unsigned long nr_clear_pages = nr_pages;
+		unsigned long nr_clear_pages = pagecount;
 		struct page *page = cma_pages;
 
 		while (nr_clear_pages > 0) {
@@ -85,7 +315,6 @@ static int cma_heap_allocate(struct dma_heap *heap,
 			 */
 			if (fatal_signal_pending(current))
 				goto free_cma;
-
 			page++;
 			nr_clear_pages--;
 		}
@@ -93,44 +322,41 @@ static int cma_heap_allocate(struct dma_heap *heap,
 		memset(page_address(cma_pages), 0, size);
 	}
 
-	helper_buffer->pagecount = nr_pages;
-	helper_buffer->pages = kmalloc_array(helper_buffer->pagecount,
-					     sizeof(*helper_buffer->pages),
-					     GFP_KERNEL);
-	if (!helper_buffer->pages) {
+	buffer->pages = kmalloc_array(pagecount, sizeof(*buffer->pages), GFP_KERNEL);
+	if (!buffer->pages) {
 		ret = -ENOMEM;
 		goto free_cma;
 	}
 
-	for (pg = 0; pg < helper_buffer->pagecount; pg++)
-		helper_buffer->pages[pg] = &cma_pages[pg];
+	for (pg = 0; pg < pagecount; pg++)
+		buffer->pages[pg] = &cma_pages[pg];
+
+	buffer->cma_pages = cma_pages;
+	buffer->heap = cma_heap;
+	buffer->pagecount = pagecount;
 
 	/* create the dmabuf */
-	dmabuf = heap_helper_export_dmabuf(helper_buffer, fd_flags);
+	exp_info.exp_name = dma_heap_get_name(heap);
+	exp_info.ops = &cma_heap_buf_ops;
+	exp_info.size = buffer->len;
+	exp_info.flags = fd_flags;
+	exp_info.priv = buffer;
+	dmabuf = dma_buf_export(&exp_info);
 	if (IS_ERR(dmabuf)) {
 		ret = PTR_ERR(dmabuf);
 		goto free_pages;
 	}
 
-	helper_buffer->dmabuf = dmabuf;
-	helper_buffer->priv_virt = cma_pages;
-
-	ret = dma_buf_fd(dmabuf, fd_flags);
-	if (ret < 0) {
-		dma_buf_put(dmabuf);
-		/* just return, as put will call release and that will free */
-		return ret;
-	}
-
-	return ret;
+	return dmabuf;
 
 free_pages:
-	kfree(helper_buffer->pages);
+	kfree(buffer->pages);
 free_cma:
-	cma_release(cma_heap->cma, cma_pages, nr_pages);
-free_buf:
-	kfree(helper_buffer);
-	return ret;
+	cma_release(cma_heap->cma, cma_pages, pagecount);
+free_buffer:
+	kfree(buffer);
+
+	return ERR_PTR(ret);
 }
 
 static const struct dma_heap_ops cma_heap_ops = {
diff --git a/drivers/dma-buf/heaps/system_heap.c b/drivers/dma-buf/heaps/system_heap.c
index 0bf688e3c..18f55f954 100644
--- a/drivers/dma-buf/heaps/system_heap.c
+++ b/drivers/dma-buf/heaps/system_heap.c
@@ -3,7 +3,11 @@
  * DMABUF System heap exporter
  *
  * Copyright (C) 2011 Google, Inc.
- * Copyright (C) 2019 Linaro Ltd.
+ * Copyright (C) 2019, 2020 Linaro Ltd.
+ *
+ * Portions based off of Andrew Davis' SRAM heap:
+ * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
+ *	Andrew F. Davis <afd@ti.com>
  */
 
 #include <linux/dma-buf.h>
@@ -15,99 +19,804 @@
 #include <linux/module.h>
 #include <linux/scatterlist.h>
 #include <linux/slab.h>
-#include <linux/sched/signal.h>
-#include <asm/page.h>
+#include <linux/swiotlb.h>
+#include <linux/vmalloc.h>
+#include <linux/rockchip/rockchip_sip.h>
+
+#include <linux/page_pool.h>
+#include <linux/deferred-free-helper.h>
+
+#define CONFIG_SYSTEM_HEAP_FORCE_DMA_SYNC
+
+static struct dma_heap *sys_heap;
+static struct dma_heap *sys_dma32_heap;
+static struct dma_heap *sys_uncached_heap;
+static struct dma_heap *sys_uncached_dma32_heap;
+
+/* Default setting */
+static u32 bank_bit_first = 12;
+static u32 bank_bit_mask = 0x7;
+
+struct system_heap_buffer {
+	struct dma_heap *heap;
+	struct list_head attachments;
+	struct mutex lock;
+	unsigned long len;
+	struct sg_table sg_table;
+	int vmap_cnt;
+	void *vaddr;
+	struct deferred_freelist_item deferred_free;
+
+	bool uncached;
+};
 
-#include "heap-helpers.h"
+struct dma_heap_attachment {
+	struct device *dev;
+	struct sg_table *table;
+	struct list_head list;
+	bool mapped;
 
-struct dma_heap *sys_heap;
+	bool uncached;
+};
 
-static void system_heap_free(struct heap_helper_buffer *buffer)
+#define LOW_ORDER_GFP (GFP_HIGHUSER | __GFP_ZERO | __GFP_COMP)
+#define MID_ORDER_GFP (LOW_ORDER_GFP | __GFP_NOWARN)
+#define HIGH_ORDER_GFP  (((GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN \
+				| __GFP_NORETRY) & ~__GFP_RECLAIM) \
+				| __GFP_COMP)
+static gfp_t order_flags[] = {HIGH_ORDER_GFP, MID_ORDER_GFP, LOW_ORDER_GFP};
+/*
+ * The selection of the orders used for allocation (1MB, 64K, 4K) is designed
+ * to match with the sizes often found in IOMMUs. Using order 4 pages instead
+ * of order 0 pages can significantly improve the performance of many IOMMUs
+ * by reducing TLB pressure and time spent updating page tables.
+ */
+static unsigned int orders[] = {8, 4, 0};
+#define NUM_ORDERS ARRAY_SIZE(orders)
+struct dmabuf_page_pool *pools[NUM_ORDERS];
+struct dmabuf_page_pool *dma32_pools[NUM_ORDERS];
+
+static struct sg_table *dup_sg_table(struct sg_table *table)
 {
-	pgoff_t pg;
+	struct sg_table *new_table;
+	int ret, i;
+	struct scatterlist *sg, *new_sg;
 
-	for (pg = 0; pg < buffer->pagecount; pg++)
-		__free_page(buffer->pages[pg]);
-	kfree(buffer->pages);
-	kfree(buffer);
+	new_table = kzalloc(sizeof(*new_table), GFP_KERNEL);
+	if (!new_table)
+		return ERR_PTR(-ENOMEM);
+
+	ret = sg_alloc_table(new_table, table->orig_nents, GFP_KERNEL);
+	if (ret) {
+		kfree(new_table);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	new_sg = new_table->sgl;
+	for_each_sgtable_sg(table, sg, i) {
+		sg_set_page(new_sg, sg_page(sg), sg->length, sg->offset);
+		new_sg = sg_next(new_sg);
+	}
+
+	return new_table;
 }
 
-static int system_heap_allocate(struct dma_heap *heap,
-				unsigned long len,
-				unsigned long fd_flags,
-				unsigned long heap_flags)
+static int system_heap_attach(struct dma_buf *dmabuf,
+			      struct dma_buf_attachment *attachment)
 {
-	struct heap_helper_buffer *helper_buffer;
-	struct dma_buf *dmabuf;
-	int ret = -ENOMEM;
-	pgoff_t pg;
+	struct system_heap_buffer *buffer = dmabuf->priv;
+	struct dma_heap_attachment *a;
+	struct sg_table *table;
+
+	a = kzalloc(sizeof(*a), GFP_KERNEL);
+	if (!a)
+		return -ENOMEM;
 
-	helper_buffer = kzalloc(sizeof(*helper_buffer), GFP_KERNEL);
-	if (!helper_buffer)
+	table = dup_sg_table(&buffer->sg_table);
+	if (IS_ERR(table)) {
+		kfree(a);
 		return -ENOMEM;
+	}
+
+	a->table = table;
+	a->dev = attachment->dev;
+	INIT_LIST_HEAD(&a->list);
+	a->mapped = false;
+	a->uncached = buffer->uncached;
+	attachment->priv = a;
+
+	mutex_lock(&buffer->lock);
+	list_add(&a->list, &buffer->attachments);
+	mutex_unlock(&buffer->lock);
+
+	return 0;
+}
+
+static void system_heap_detach(struct dma_buf *dmabuf,
+			       struct dma_buf_attachment *attachment)
+{
+	struct system_heap_buffer *buffer = dmabuf->priv;
+	struct dma_heap_attachment *a = attachment->priv;
+
+	mutex_lock(&buffer->lock);
+	list_del(&a->list);
+	mutex_unlock(&buffer->lock);
+
+	sg_free_table(a->table);
+	kfree(a->table);
+	kfree(a);
+}
+
+static struct sg_table *system_heap_map_dma_buf(struct dma_buf_attachment *attachment,
+						enum dma_data_direction direction)
+{
+	struct dma_heap_attachment *a = attachment->priv;
+	struct sg_table *table = a->table;
+	int attr = attachment->dma_map_attrs;
+	int ret;
+
+	if (a->uncached)
+		attr |= DMA_ATTR_SKIP_CPU_SYNC;
+
+	ret = dma_map_sgtable(attachment->dev, table, direction, attr);
+	if (ret)
+		return ERR_PTR(ret);
+
+	a->mapped = true;
+	return table;
+}
+
+static void system_heap_unmap_dma_buf(struct dma_buf_attachment *attachment,
+				      struct sg_table *table,
+				      enum dma_data_direction direction)
+{
+	struct dma_heap_attachment *a = attachment->priv;
+	int attr = attachment->dma_map_attrs;
+
+	if (a->uncached)
+		attr |= DMA_ATTR_SKIP_CPU_SYNC;
+	a->mapped = false;
+	dma_unmap_sgtable(attachment->dev, table, direction, attr);
+}
+
+static int system_heap_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
+						enum dma_data_direction direction)
+{
+	struct system_heap_buffer *buffer = dmabuf->priv;
+	struct dma_heap_attachment *a;
+
+	mutex_lock(&buffer->lock);
+
+	if (buffer->vmap_cnt)
+		invalidate_kernel_vmap_range(buffer->vaddr, buffer->len);
+
+	if (!buffer->uncached) {
+		list_for_each_entry(a, &buffer->attachments, list) {
+			if (!a->mapped)
+				continue;
+			dma_sync_sgtable_for_cpu(a->dev, a->table, direction);
+		}
+	}
+	mutex_unlock(&buffer->lock);
+
+	return 0;
+}
+
+static int system_heap_dma_buf_end_cpu_access(struct dma_buf *dmabuf,
+					      enum dma_data_direction direction)
+{
+	struct system_heap_buffer *buffer = dmabuf->priv;
+	struct dma_heap_attachment *a;
+
+	mutex_lock(&buffer->lock);
+
+	if (buffer->vmap_cnt)
+		flush_kernel_vmap_range(buffer->vaddr, buffer->len);
+
+	if (!buffer->uncached) {
+		list_for_each_entry(a, &buffer->attachments, list) {
+			if (!a->mapped)
+				continue;
+			dma_sync_sgtable_for_device(a->dev, a->table, direction);
+		}
+	}
+	mutex_unlock(&buffer->lock);
+
+	return 0;
+}
+
+static int system_heap_sgl_sync_range(struct device *dev,
+				      struct scatterlist *sgl,
+				      unsigned int nents,
+				      unsigned int offset,
+				      unsigned int length,
+				      enum dma_data_direction dir,
+				      bool for_cpu)
+{
+	struct scatterlist *sg;
+	unsigned int len = 0;
+	dma_addr_t sg_dma_addr;
+	int i;
+
+	for_each_sg(sgl, sg, nents, i) {
+		unsigned int sg_offset, sg_left, size = 0;
+
+		sg_dma_addr = sg_dma_address(sg);
+
+		len += sg->length;
+		if (len <= offset)
+			continue;
+
+		sg_left = len - offset;
+		sg_offset = sg->length - sg_left;
+
+		size = (length < sg_left) ? length : sg_left;
+		if (for_cpu)
+			dma_sync_single_range_for_cpu(dev, sg_dma_addr,
+						      sg_offset, size, dir);
+		else
+			dma_sync_single_range_for_device(dev, sg_dma_addr,
+							 sg_offset, size, dir);
+
+		offset += size;
+		length -= size;
+
+		if (length == 0)
+			break;
+	}
+
+	return 0;
+}
+
+static int
+system_heap_dma_buf_begin_cpu_access_partial(struct dma_buf *dmabuf,
+					     enum dma_data_direction direction,
+					     unsigned int offset,
+					     unsigned int len)
+{
+	struct system_heap_buffer *buffer = dmabuf->priv;
+	struct dma_heap_attachment *a;
+	int ret = 0;
+
+	if (direction == DMA_TO_DEVICE)
+		return 0;
+
+	mutex_lock(&buffer->lock);
+	if (IS_ENABLED(CONFIG_SYSTEM_HEAP_FORCE_DMA_SYNC)) {
+		struct dma_heap *heap = buffer->heap;
+		struct sg_table *table = &buffer->sg_table;
+
+		ret = system_heap_sgl_sync_range(dma_heap_get_dev(heap),
+						 table->sgl,
+						 table->nents,
+						 offset, len,
+						 direction, true);
+		goto unlock;
+	}
+
+	if (buffer->vmap_cnt)
+		invalidate_kernel_vmap_range(buffer->vaddr, buffer->len);
+
+	if (!buffer->uncached)
+		goto unlock;
+
+	list_for_each_entry(a, &buffer->attachments, list) {
+		if (!a->mapped)
+			continue;
+
+		ret = system_heap_sgl_sync_range(a->dev, a->table->sgl,
+						 a->table->nents,
+						 offset, len,
+						 direction, true);
+	}
+
+unlock:
+	mutex_unlock(&buffer->lock);
+
+	return ret;
+}
+
+static int
+system_heap_dma_buf_end_cpu_access_partial(struct dma_buf *dmabuf,
+					   enum dma_data_direction direction,
+					   unsigned int offset,
+					   unsigned int len)
+{
+	struct system_heap_buffer *buffer = dmabuf->priv;
+	struct dma_heap_attachment *a;
+	int ret = 0;
+
+	mutex_lock(&buffer->lock);
+	if (IS_ENABLED(CONFIG_SYSTEM_HEAP_FORCE_DMA_SYNC)) {
+		struct dma_heap *heap = buffer->heap;
+		struct sg_table *table = &buffer->sg_table;
+
+		ret = system_heap_sgl_sync_range(dma_heap_get_dev(heap),
+						 table->sgl,
+						 table->nents,
+						 offset, len,
+						 direction, false);
+		goto unlock;
+	}
+
+	if (buffer->vmap_cnt)
+		flush_kernel_vmap_range(buffer->vaddr, buffer->len);
+
+	if (!buffer->uncached)
+		goto unlock;
+
+	list_for_each_entry(a, &buffer->attachments, list) {
+		if (!a->mapped)
+			continue;
+
+		ret = system_heap_sgl_sync_range(a->dev, a->table->sgl,
+						 a->table->nents,
+						 offset, len,
+						 direction, false);
+	}
+unlock:
+	mutex_unlock(&buffer->lock);
+
+	return ret;
+}
+
+static int system_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
+{
+	struct system_heap_buffer *buffer = dmabuf->priv;
+	struct sg_table *table = &buffer->sg_table;
+	unsigned long addr = vma->vm_start;
+	struct sg_page_iter piter;
+	int ret;
+
+	if (buffer->uncached)
+		vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+	for_each_sgtable_page(table, &piter, vma->vm_pgoff) {
+		struct page *page = sg_page_iter_page(&piter);
+
+		ret = remap_pfn_range(vma, addr, page_to_pfn(page), PAGE_SIZE,
+				      vma->vm_page_prot);
+		if (ret)
+			return ret;
+		addr += PAGE_SIZE;
+		if (addr >= vma->vm_end)
+			return 0;
+	}
+	return 0;
+}
+
+static void *system_heap_do_vmap(struct system_heap_buffer *buffer)
+{
+	struct sg_table *table = &buffer->sg_table;
+	int npages = PAGE_ALIGN(buffer->len) / PAGE_SIZE;
+	struct page **pages = vmalloc(sizeof(struct page *) * npages);
+	struct page **tmp = pages;
+	struct sg_page_iter piter;
+	pgprot_t pgprot = PAGE_KERNEL;
+	void *vaddr;
+
+	if (!pages)
+		return ERR_PTR(-ENOMEM);
+
+	if (buffer->uncached)
+		pgprot = pgprot_writecombine(PAGE_KERNEL);
+
+	for_each_sgtable_page(table, &piter, 0) {
+		WARN_ON(tmp - pages >= npages);
+		*tmp++ = sg_page_iter_page(&piter);
+	}
 
-	init_heap_helper_buffer(helper_buffer, system_heap_free);
-	helper_buffer->heap = heap;
-	helper_buffer->size = len;
+	vaddr = vmap(pages, npages, VM_MAP, pgprot);
+	vfree(pages);
 
-	helper_buffer->pagecount = len / PAGE_SIZE;
-	helper_buffer->pages = kmalloc_array(helper_buffer->pagecount,
-					     sizeof(*helper_buffer->pages),
-					     GFP_KERNEL);
-	if (!helper_buffer->pages) {
-		ret = -ENOMEM;
-		goto err0;
+	if (!vaddr)
+		return ERR_PTR(-ENOMEM);
+
+	return vaddr;
+}
+
+static void *system_heap_vmap(struct dma_buf *dmabuf)
+{
+	struct system_heap_buffer *buffer = dmabuf->priv;
+	void *vaddr;
+
+	mutex_lock(&buffer->lock);
+	if (buffer->vmap_cnt) {
+		buffer->vmap_cnt++;
+		vaddr = buffer->vaddr;
+		goto out;
+	}
+
+	vaddr = system_heap_do_vmap(buffer);
+	if (IS_ERR(vaddr))
+		goto out;
+
+	buffer->vaddr = vaddr;
+	buffer->vmap_cnt++;
+out:
+	mutex_unlock(&buffer->lock);
+
+	return vaddr;
+}
+
+static void system_heap_vunmap(struct dma_buf *dmabuf, void *vaddr)
+{
+	struct system_heap_buffer *buffer = dmabuf->priv;
+
+	mutex_lock(&buffer->lock);
+	if (!--buffer->vmap_cnt) {
+		vunmap(buffer->vaddr);
+		buffer->vaddr = NULL;
+	}
+	mutex_unlock(&buffer->lock);
+}
+
+static int system_heap_zero_buffer(struct system_heap_buffer *buffer)
+{
+	struct sg_table *sgt = &buffer->sg_table;
+	struct sg_page_iter piter;
+	struct page *p;
+	void *vaddr;
+	int ret = 0;
+
+	for_each_sgtable_page(sgt, &piter, 0) {
+		p = sg_page_iter_page(&piter);
+		vaddr = kmap_atomic(p);
+		memset(vaddr, 0, PAGE_SIZE);
+		kunmap_atomic(vaddr);
+	}
+
+	return ret;
+}
+
+static void system_heap_buf_free(struct deferred_freelist_item *item,
+				 enum df_reason reason)
+{
+	struct system_heap_buffer *buffer;
+	struct sg_table *table;
+	struct scatterlist *sg;
+	int i, j;
+
+	buffer = container_of(item, struct system_heap_buffer, deferred_free);
+	/* Zero the buffer pages before adding back to the pool */
+	if (reason == DF_NORMAL)
+		if (system_heap_zero_buffer(buffer))
+			reason = DF_UNDER_PRESSURE; // On failure, just free
+
+	table = &buffer->sg_table;
+	for_each_sg(table->sgl, sg, table->nents, i) {
+		struct page *page = sg_page(sg);
+
+		if (reason == DF_UNDER_PRESSURE) {
+			__free_pages(page, compound_order(page));
+		} else {
+			for (j = 0; j < NUM_ORDERS; j++) {
+				if (compound_order(page) == orders[j])
+					break;
+			}
+			dmabuf_page_pool_free(pools[j], page);
+		}
 	}
+	sg_free_table(table);
+	kfree(buffer);
+}
+
+static void system_heap_dma_buf_release(struct dma_buf *dmabuf)
+{
+	struct system_heap_buffer *buffer = dmabuf->priv;
+	int npages = PAGE_ALIGN(buffer->len) / PAGE_SIZE;
+
+	deferred_free(&buffer->deferred_free, system_heap_buf_free, npages);
+}
+
+static const struct dma_buf_ops system_heap_buf_ops = {
+	.attach = system_heap_attach,
+	.detach = system_heap_detach,
+	.map_dma_buf = system_heap_map_dma_buf,
+	.unmap_dma_buf = system_heap_unmap_dma_buf,
+	.begin_cpu_access = system_heap_dma_buf_begin_cpu_access,
+	.end_cpu_access = system_heap_dma_buf_end_cpu_access,
+	.begin_cpu_access_partial = system_heap_dma_buf_begin_cpu_access_partial,
+	.end_cpu_access_partial = system_heap_dma_buf_end_cpu_access_partial,
+	.mmap = system_heap_mmap,
+	.vmap = system_heap_vmap,
+	.vunmap = system_heap_vunmap,
+	.release = system_heap_dma_buf_release,
+};
+
+static struct page *system_heap_alloc_largest_available(struct dma_heap *heap,
+							unsigned long size,
+							unsigned int max_order)
+{
+	struct page *page;
+	int i;
+	const char *name = dma_heap_get_name(heap);
+	struct dmabuf_page_pool **pool;
+
+	pool = strstr(name, "dma32") ? dma32_pools : pools;
+	for (i = 0; i < NUM_ORDERS; i++) {
+		if (size <  (PAGE_SIZE << orders[i]))
+			continue;
+		if (max_order < orders[i])
+			continue;
+		page = dmabuf_page_pool_alloc(pool[i]);
+		if (!page)
+			continue;
+		return page;
+	}
+	return NULL;
+}
+
+static struct dma_buf *system_heap_do_allocate(struct dma_heap *heap,
+					       unsigned long len,
+					       unsigned long fd_flags,
+					       unsigned long heap_flags,
+					       bool uncached)
+{
+	struct system_heap_buffer *buffer;
+	DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+	unsigned long size_remaining = len;
+	unsigned int max_order = orders[0];
+	struct dma_buf *dmabuf;
+	struct sg_table *table;
+	struct scatterlist *sg;
+	struct list_head pages;
+	struct page *page, *tmp_page;
+	int i, ret = -ENOMEM;
+	struct list_head lists[8];
+	unsigned int block_index[8] = {0};
+	unsigned int block_1M = 0;
+	unsigned int block_64K = 0;
+	unsigned int maximum;
+	int j;
+
+	buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
+	if (!buffer)
+		return ERR_PTR(-ENOMEM);
+
+	INIT_LIST_HEAD(&buffer->attachments);
+	mutex_init(&buffer->lock);
+	buffer->heap = heap;
+	buffer->len = len;
+	buffer->uncached = uncached;
 
-	for (pg = 0; pg < helper_buffer->pagecount; pg++) {
+	INIT_LIST_HEAD(&pages);
+	for (i = 0; i < 8; i++)
+		INIT_LIST_HEAD(&lists[i]);
+	i = 0;
+	while (size_remaining > 0) {
 		/*
 		 * Avoid trying to allocate memory if the process
-		 * has been killed by by SIGKILL
+		 * has been killed by SIGKILL
 		 */
 		if (fatal_signal_pending(current))
-			goto err1;
+			goto free_buffer;
+
+		page = system_heap_alloc_largest_available(heap, size_remaining, max_order);
+		if (!page)
+			goto free_buffer;
+
+		size_remaining -= page_size(page);
+		max_order = compound_order(page);
+		if (max_order) {
+			if (max_order == 8)
+				block_1M++;
+			if (max_order == 4)
+				block_64K++;
+			list_add_tail(&page->lru, &pages);
+		} else {
+			dma_addr_t phys = page_to_phys(page);
+			unsigned int bit_index = ((phys >> bank_bit_first) & bank_bit_mask) & 0x7;
+
+			list_add_tail(&page->lru, &lists[bit_index]);
+			block_index[bit_index]++;
+		}
+		i++;
+	}
+
+	table = &buffer->sg_table;
+	if (sg_alloc_table(table, i, GFP_KERNEL))
+		goto free_buffer;
 
-		helper_buffer->pages[pg] = alloc_page(GFP_KERNEL | __GFP_ZERO);
-		if (!helper_buffer->pages[pg])
-			goto err1;
+	maximum = block_index[0];
+	for (i = 1; i < 8; i++)
+		maximum = max(maximum, block_index[i]);
+	sg = table->sgl;
+	list_for_each_entry_safe(page, tmp_page, &pages, lru) {
+		sg_set_page(sg, page, page_size(page), 0);
+		sg = sg_next(sg);
+		list_del(&page->lru);
+	}
+	for (i = 0; i < maximum; i++) {
+		for (j = 0; j < 8; j++) {
+			if (!list_empty(&lists[j])) {
+				page = list_first_entry(&lists[j], struct page, lru);
+				sg_set_page(sg, page, PAGE_SIZE, 0);
+				sg = sg_next(sg);
+				list_del(&page->lru);
+			}
+		}
 	}
 
 	/* create the dmabuf */
-	dmabuf = heap_helper_export_dmabuf(helper_buffer, fd_flags);
+	exp_info.exp_name = dma_heap_get_name(heap);
+	exp_info.ops = &system_heap_buf_ops;
+	exp_info.size = buffer->len;
+	exp_info.flags = fd_flags;
+	exp_info.priv = buffer;
+	dmabuf = dma_buf_export(&exp_info);
 	if (IS_ERR(dmabuf)) {
 		ret = PTR_ERR(dmabuf);
-		goto err1;
+		goto free_pages;
 	}
 
-	helper_buffer->dmabuf = dmabuf;
+	/*
+	 * For uncached buffers, we need to initially flush cpu cache, since
+	 * the __GFP_ZERO on the allocation means the zeroing was done by the
+	 * cpu and thus it is likely cached. Map (and implicitly flush) and
+	 * unmap it now so we don't get corruption later on.
+	 */
+	if (buffer->uncached) {
+		dma_map_sgtable(dma_heap_get_dev(heap), table, DMA_BIDIRECTIONAL, 0);
+		dma_unmap_sgtable(dma_heap_get_dev(heap), table, DMA_BIDIRECTIONAL, 0);
+	}
 
-	ret = dma_buf_fd(dmabuf, fd_flags);
-	if (ret < 0) {
-		dma_buf_put(dmabuf);
-		/* just return, as put will call release and that will free */
-		return ret;
+	return dmabuf;
+
+free_pages:
+	for_each_sgtable_sg(table, sg, i) {
+		struct page *p = sg_page(sg);
+
+		__free_pages(p, compound_order(p));
+	}
+	sg_free_table(table);
+free_buffer:
+	list_for_each_entry_safe(page, tmp_page, &pages, lru)
+		__free_pages(page, compound_order(page));
+	for (i = 0; i < 8; i++) {
+		list_for_each_entry_safe(page, tmp_page, &lists[i], lru)
+			__free_pages(page, compound_order(page));
 	}
+	kfree(buffer);
 
-	return ret;
+	return ERR_PTR(ret);
+}
 
-err1:
-	while (pg > 0)
-		__free_page(helper_buffer->pages[--pg]);
-	kfree(helper_buffer->pages);
-err0:
-	kfree(helper_buffer);
+static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
+					    unsigned long len,
+					    unsigned long fd_flags,
+					    unsigned long heap_flags)
+{
+	return system_heap_do_allocate(heap, len, fd_flags, heap_flags, false);
+}
 
-	return ret;
+static long system_get_pool_size(struct dma_heap *heap)
+{
+	int i;
+	long num_pages = 0;
+	struct dmabuf_page_pool **pool;
+	const char *name = dma_heap_get_name(heap);
+
+	pool = pools;
+	if (!strcmp(name, "system-dma32") || !strcmp(name, "system-uncached-dma32"))
+		pool = dma32_pools;
+	for (i = 0; i < NUM_ORDERS; i++, pool++) {
+		num_pages += ((*pool)->count[POOL_LOWPAGE] +
+			      (*pool)->count[POOL_HIGHPAGE]) << (*pool)->order;
+	}
+
+	return num_pages << PAGE_SHIFT;
 }
 
 static const struct dma_heap_ops system_heap_ops = {
 	.allocate = system_heap_allocate,
+	.get_pool_size = system_get_pool_size,
+};
+
+static struct dma_buf *system_uncached_heap_allocate(struct dma_heap *heap,
+						     unsigned long len,
+						     unsigned long fd_flags,
+						     unsigned long heap_flags)
+{
+	return system_heap_do_allocate(heap, len, fd_flags, heap_flags, true);
+}
+
+/* Dummy function to be used until we can call coerce_mask_and_coherent */
+static struct dma_buf *system_uncached_heap_not_initialized(struct dma_heap *heap,
+							    unsigned long len,
+							    unsigned long fd_flags,
+							    unsigned long heap_flags)
+{
+	return ERR_PTR(-EBUSY);
+}
+
+static struct dma_heap_ops system_uncached_heap_ops = {
+	/* After system_heap_create is complete, we will swap this */
+	.allocate = system_uncached_heap_not_initialized,
 };
 
+static int set_heap_dev_dma(struct device *heap_dev)
+{
+	int err = 0;
+
+	if (!heap_dev)
+		return -EINVAL;
+
+	dma_coerce_mask_and_coherent(heap_dev, DMA_BIT_MASK(64));
+
+	if (!heap_dev->dma_parms) {
+		heap_dev->dma_parms = devm_kzalloc(heap_dev,
+						   sizeof(*heap_dev->dma_parms),
+						   GFP_KERNEL);
+		if (!heap_dev->dma_parms)
+			return -ENOMEM;
+
+		err = dma_set_max_seg_size(heap_dev, (unsigned int)DMA_BIT_MASK(64));
+		if (err) {
+			devm_kfree(heap_dev, heap_dev->dma_parms);
+			dev_err(heap_dev, "Failed to set DMA segment size, err:%d\n", err);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
 static int system_heap_create(void)
 {
 	struct dma_heap_export_info exp_info;
-	int ret = 0;
+	int i, err = 0;
+	struct dram_addrmap_info *ddr_map_info;
+
+	/*
+	 * Since swiotlb has memory size limitation, this will calculate
+	 * the maximum size locally.
+	 *
+	 * Once swiotlb_max_segment() return not '0', means that the totalram size
+	 * is larger than 4GiB and swiotlb is not force mode, in this case, system
+	 * heap should limit largest allocation.
+	 *
+	 * FIX: fix the orders[] as a workaround.
+	 */
+	if (swiotlb_max_segment()) {
+		unsigned int max_size = (1 << IO_TLB_SHIFT) * IO_TLB_SEGSIZE;
+		int max_order = MAX_ORDER;
+		int i;
+
+		max_size = max_t(unsigned int, max_size, PAGE_SIZE) >> PAGE_SHIFT;
+		max_order = min(max_order, ilog2(max_size));
+		for (i = 0; i < NUM_ORDERS; i++) {
+			if (max_order < orders[i])
+				orders[i] = max_order;
+			pr_info("system_heap: orders[%d] = %u\n", i, orders[i]);
+		}
+	}
+
+	for (i = 0; i < NUM_ORDERS; i++) {
+		pools[i] = dmabuf_page_pool_create(order_flags[i], orders[i]);
+
+		if (!pools[i]) {
+			int j;
+
+			pr_err("%s: page pool creation failed!\n", __func__);
+			for (j = 0; j < i; j++)
+				dmabuf_page_pool_destroy(pools[j]);
+			return -ENOMEM;
+		}
+	}
+
+	for (i = 0; i < NUM_ORDERS; i++) {
+		dma32_pools[i] = dmabuf_page_pool_create(order_flags[i] | GFP_DMA32, orders[i]);
+
+		if (!dma32_pools[i]) {
+			int j;
+
+			pr_err("%s: page dma32 pool creation failed!\n", __func__);
+			for (j = 0; j < i; j++)
+				dmabuf_page_pool_destroy(dma32_pools[j]);
+			goto err_dma32_pool;
+		}
+	}
 
 	exp_info.name = "system";
 	exp_info.ops = &system_heap_ops;
@@ -115,9 +824,56 @@ static int system_heap_create(void)
 
 	sys_heap = dma_heap_add(&exp_info);
 	if (IS_ERR(sys_heap))
-		ret = PTR_ERR(sys_heap);
+		return PTR_ERR(sys_heap);
 
-	return ret;
+	exp_info.name = "system-dma32";
+	exp_info.ops = &system_heap_ops;
+	exp_info.priv = NULL;
+
+	sys_dma32_heap = dma_heap_add(&exp_info);
+	if (IS_ERR(sys_dma32_heap))
+		return PTR_ERR(sys_dma32_heap);
+
+	exp_info.name = "system-uncached";
+	exp_info.ops = &system_uncached_heap_ops;
+	exp_info.priv = NULL;
+
+	sys_uncached_heap = dma_heap_add(&exp_info);
+	if (IS_ERR(sys_uncached_heap))
+		return PTR_ERR(sys_uncached_heap);
+
+	err = set_heap_dev_dma(dma_heap_get_dev(sys_uncached_heap));
+	if (err)
+		return err;
+
+	exp_info.name = "system-uncached-dma32";
+	exp_info.ops = &system_uncached_heap_ops;
+	exp_info.priv = NULL;
+
+	sys_uncached_dma32_heap = dma_heap_add(&exp_info);
+	if (IS_ERR(sys_uncached_dma32_heap))
+		return PTR_ERR(sys_uncached_dma32_heap);
+
+	err = set_heap_dev_dma(dma_heap_get_dev(sys_uncached_dma32_heap));
+	if (err)
+		return err;
+	dma_coerce_mask_and_coherent(dma_heap_get_dev(sys_uncached_dma32_heap), DMA_BIT_MASK(32));
+
+	mb(); /* make sure we only set allocate after dma_mask is set */
+	system_uncached_heap_ops.allocate = system_uncached_heap_allocate;
+
+	ddr_map_info = sip_smc_get_dram_map();
+	if (ddr_map_info) {
+		bank_bit_first = ddr_map_info->bank_bit_first;
+		bank_bit_mask = ddr_map_info->bank_bit_mask;
+	}
+
+	return 0;
+err_dma32_pool:
+	for (i = 0; i < NUM_ORDERS; i++)
+		dmabuf_page_pool_destroy(pools[i]);
+
+	return -ENOMEM;
 }
 module_init(system_heap_create);
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/dma-buf/sw_sync.c b/drivers/dma-buf/sw_sync.c
index 348b3a917..3daa6c76b 100644
--- a/drivers/dma-buf/sw_sync.c
+++ b/drivers/dma-buf/sw_sync.c
@@ -7,6 +7,8 @@
 
 #include <linux/file.h>
 #include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
 #include <linux/uaccess.h>
 #include <linux/slab.h>
 #include <linux/sync_file.h>
@@ -410,3 +412,13 @@ const struct file_operations sw_sync_debugfs_fops = {
 	.unlocked_ioctl = sw_sync_ioctl,
 	.compat_ioctl	= compat_ptr_ioctl,
 };
+
+static struct miscdevice sw_sync_dev = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "sw_sync",
+	.fops	= &sw_sync_debugfs_fops,
+};
+
+module_misc_device(sw_sync_dev);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/dma-buf/sync_debug.h b/drivers/dma-buf/sync_debug.h
index 6176e52ba..fb676da19 100644
--- a/drivers/dma-buf/sync_debug.h
+++ b/drivers/dma-buf/sync_debug.h
@@ -64,9 +64,16 @@ struct sync_pt {
 
 extern const struct file_operations sw_sync_debugfs_fops;
 
+#ifdef CONFIG_SW_SYNC_DEBUG
 void sync_timeline_debug_add(struct sync_timeline *obj);
 void sync_timeline_debug_remove(struct sync_timeline *obj);
 void sync_file_debug_add(struct sync_file *fence);
 void sync_file_debug_remove(struct sync_file *fence);
+#else
+static inline void sync_timeline_debug_add(struct sync_timeline *obj) {}
+static inline void sync_timeline_debug_remove(struct sync_timeline *obj) {}
+static inline void sync_file_debug_add(struct sync_file *fence) {}
+static inline void sync_file_debug_remove(struct sync_file *fence) {}
+#endif
 
 #endif /* _LINUX_SYNC_H */
diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c
index 2e63274a4..ab666917b 100644
--- a/drivers/gpio/gpiolib-of.c
+++ b/drivers/gpio/gpiolib-of.c
@@ -1046,3 +1046,14 @@ void of_gpiochip_remove(struct gpio_chip *chip)
 {
 	of_node_put(chip->of_node);
 }
+
+void of_gpio_dev_init(struct gpio_chip *gc, struct gpio_device *gdev)
+{
+	/* If the gpiochip has an assigned OF node this takes precedence */
+	if (gc->of_node)
+		gdev->dev.of_node = gc->of_node;
+	else
+		gc->of_node = gdev->dev.of_node;
+	if (gdev->dev.of_node)
+		gdev->dev.fwnode = of_fwnode_handle(gdev->dev.of_node);
+}
diff --git a/drivers/gpio/gpiolib-of.h b/drivers/gpio/gpiolib-of.h
index ed26664f1..8af2bc899 100644
--- a/drivers/gpio/gpiolib-of.h
+++ b/drivers/gpio/gpiolib-of.h
@@ -15,6 +15,7 @@ int of_gpiochip_add(struct gpio_chip *gc);
 void of_gpiochip_remove(struct gpio_chip *gc);
 int of_gpio_get_count(struct device *dev, const char *con_id);
 bool of_gpio_need_valid_mask(const struct gpio_chip *gc);
+void of_gpio_dev_init(struct gpio_chip *gc, struct gpio_device *gdev);
 #else
 static inline struct gpio_desc *of_find_gpio(struct device *dev,
 					     const char *con_id,
@@ -33,6 +34,10 @@ static inline bool of_gpio_need_valid_mask(const struct gpio_chip *gc)
 {
 	return false;
 }
+static inline void of_gpio_dev_init(struct gpio_chip *gc,
+				    struct gpio_device *gdev)
+{
+}
 #endif /* CONFIG_OF_GPIO */
 
 extern struct notifier_block gpio_of_notifier;
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 4e9b3a95f..95994151a 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -32,6 +32,10 @@ config DRM_MIPI_DBI
 	depends on DRM
 	select DRM_KMS_HELPER
 
+config DRM_IGNORE_IOTCL_PERMIT
+	bool "Ignore drm ioctl permission"
+	depends on DRM && ANDROID && NO_GKI
+
 config DRM_MIPI_DSI
 	bool
 	depends on DRM
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index e8baa0745..0bc97715e 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -13,6 +13,7 @@
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
+#include <linux/irq.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/phy/phy.h>
@@ -40,6 +41,20 @@ struct bridge_init {
 	struct device_node *node;
 };
 
+static bool analogix_dp_bandwidth_ok(struct analogix_dp_device *dp,
+				     const struct drm_display_mode *mode,
+				     unsigned int rate, unsigned int lanes)
+{
+	u32 max_bw, req_bw, bpp = 24;
+
+	req_bw = mode->clock * bpp / 8;
+	max_bw = lanes * rate;
+	if (req_bw > max_bw)
+		return false;
+
+	return true;
+}
+
 static int analogix_dp_init_dp(struct analogix_dp_device *dp)
 {
 	int ret;
@@ -64,6 +79,46 @@ static int analogix_dp_init_dp(struct analogix_dp_device *dp)
 	return 0;
 }
 
+static int analogix_dp_panel_prepare(struct analogix_dp_device *dp)
+{
+	int ret;
+
+	mutex_lock(&dp->panel_lock);
+
+	if (dp->panel_is_prepared)
+		goto out;
+
+	ret = drm_panel_prepare(dp->plat_data->panel);
+	if (ret)
+		goto out;
+
+	dp->panel_is_prepared = true;
+
+out:
+	mutex_unlock(&dp->panel_lock);
+	return 0;
+}
+
+static int analogix_dp_panel_unprepare(struct analogix_dp_device *dp)
+{
+	int ret;
+
+	mutex_lock(&dp->panel_lock);
+
+	if (!dp->panel_is_prepared)
+		goto out;
+
+	ret = drm_panel_unprepare(dp->plat_data->panel);
+	if (ret)
+		goto out;
+
+	dp->panel_is_prepared = false;
+
+out:
+	mutex_unlock(&dp->panel_lock);
+	return 0;
+}
+
 static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
 {
 	int timeout_loop = 0;
@@ -108,6 +163,9 @@ static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
 	unsigned char psr_version;
 	int ret;
 
+	if (!device_property_read_bool(dp->dev, "support-psr"))
+		return 0;
+
 	ret = drm_dp_dpcd_readb(&dp->aux, DP_PSR_SUPPORT, &psr_version);
 	if (ret != 1) {
 		dev_err(dp->dev, "failed to get PSR version, disable it\n");
@@ -216,8 +274,24 @@ static int analogix_dp_set_enhanced_mode(struct analogix_dp_device *dp)
 	if (ret < 0)
 		return ret;
 
+	if (!data) {
+		/*
+		 * A setting of 1 indicates that this is an eDP device that
+		 * uses only Enhanced Framing, independently of the setting by
+		 * the source of ENHANCED_FRAME_EN
+		 */
+		ret = drm_dp_dpcd_readb(&dp->aux, DP_EDP_CONFIGURATION_CAP,
+					&data);
+		if (ret < 0)
+			return ret;
+
+		data = !!(data & DP_FRAMING_CHANGE_CAP);
+	}
+
 	analogix_dp_enable_enhanced_mode(dp, data);
 
+	dp->link_train.enhanced_framing = data;
+
 	return 0;
 }
 
@@ -233,32 +307,10 @@ static int analogix_dp_training_pattern_dis(struct analogix_dp_device *dp)
 	return ret < 0 ? ret : 0;
 }
 
-static void
-analogix_dp_set_lane_lane_pre_emphasis(struct analogix_dp_device *dp,
-				       int pre_emphasis, int lane)
-{
-	switch (lane) {
-	case 0:
-		analogix_dp_set_lane0_pre_emphasis(dp, pre_emphasis);
-		break;
-	case 1:
-		analogix_dp_set_lane1_pre_emphasis(dp, pre_emphasis);
-		break;
-
-	case 2:
-		analogix_dp_set_lane2_pre_emphasis(dp, pre_emphasis);
-		break;
-
-	case 3:
-		analogix_dp_set_lane3_pre_emphasis(dp, pre_emphasis);
-		break;
-	}
-}
-
 static int analogix_dp_link_start(struct analogix_dp_device *dp)
 {
 	u8 buf[4];
-	int lane, lane_count, pll_tries, retval;
+	int lane, lane_count, retval;
 
 	lane_count = dp->link_train.lane_count;
 
@@ -278,6 +330,14 @@ static int analogix_dp_link_start(struct analogix_dp_device *dp)
 	retval = drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, buf, 2);
 	if (retval < 0)
 		return retval;
+
+	/* Spread AMP if required, enable 8b/10b coding */
+	buf[0] = analogix_dp_ssc_supported(dp) ? DP_SPREAD_AMP_0_5 : 0;
+	buf[1] = DP_SET_ANSI_8B10B;
+	retval = drm_dp_dpcd_write(&dp->aux, DP_DOWNSPREAD_CTRL, buf, 2);
+	if (retval < 0)
+		return retval;
+
 	/* set enhanced mode if available */
 	retval = analogix_dp_set_enhanced_mode(dp);
 	if (retval < 0) {
@@ -285,22 +345,12 @@ static int analogix_dp_link_start(struct analogix_dp_device *dp)
 		return retval;
 	}
 
-	/* Set TX pre-emphasis to minimum */
+	/* Set TX voltage-swing and pre-emphasis to minimum */
 	for (lane = 0; lane < lane_count; lane++)
-		analogix_dp_set_lane_lane_pre_emphasis(dp,
-			PRE_EMPHASIS_LEVEL_0, lane);
-
-	/* Wait for PLL lock */
-	pll_tries = 0;
-	while (analogix_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
-		if (pll_tries == DP_TIMEOUT_LOOP_COUNT) {
-			dev_err(dp->dev, "Wait for PLL lock timed out\n");
-			return -ETIMEDOUT;
-		}
-
-		pll_tries++;
-		usleep_range(90, 120);
-	}
+		dp->link_train.training_lane[lane] =
+					DP_TRAIN_VOLTAGE_SWING_LEVEL_0 |
+					DP_TRAIN_PRE_EMPH_LEVEL_0;
+	analogix_dp_set_lane_link_training(dp);
 
 	/* Set training pattern 1 */
 	analogix_dp_set_training_pattern(dp, TRAINING_PTN1);
@@ -383,54 +433,6 @@ static unsigned char analogix_dp_get_adjust_request_pre_emphasis(
 	return ((link_value >> shift) & 0xc) >> 2;
 }
 
-static void analogix_dp_set_lane_link_training(struct analogix_dp_device *dp,
-					       u8 training_lane_set, int lane)
-{
-	switch (lane) {
-	case 0:
-		analogix_dp_set_lane0_link_training(dp, training_lane_set);
-		break;
-	case 1:
-		analogix_dp_set_lane1_link_training(dp, training_lane_set);
-		break;
-
-	case 2:
-		analogix_dp_set_lane2_link_training(dp, training_lane_set);
-		break;
-
-	case 3:
-		analogix_dp_set_lane3_link_training(dp, training_lane_set);
-		break;
-	}
-}
-
-static unsigned int
-analogix_dp_get_lane_link_training(struct analogix_dp_device *dp,
-				   int lane)
-{
-	u32 reg;
-
-	switch (lane) {
-	case 0:
-		reg = analogix_dp_get_lane0_link_training(dp);
-		break;
-	case 1:
-		reg = analogix_dp_get_lane1_link_training(dp);
-		break;
-	case 2:
-		reg = analogix_dp_get_lane2_link_training(dp);
-		break;
-	case 3:
-		reg = analogix_dp_get_lane3_link_training(dp);
-		break;
-	default:
-		WARN_ON(1);
-		return 0;
-	}
-
-	return reg;
-}
-
 static void analogix_dp_reduce_link_rate(struct analogix_dp_device *dp)
 {
 	analogix_dp_training_pattern_dis(dp);
@@ -463,13 +465,27 @@ static void analogix_dp_get_adjust_training_lane(struct analogix_dp_device *dp,
 	}
 }
 
+static bool analogix_dp_tps3_supported(struct analogix_dp_device *dp)
+{
+	bool source_tps3_supported, sink_tps3_supported;
+	u8 dpcd = 0;
+
+	source_tps3_supported =
+		dp->video_info.max_link_rate == DP_LINK_BW_5_4;
+	drm_dp_dpcd_readb(&dp->aux, DP_MAX_LANE_COUNT, &dpcd);
+	sink_tps3_supported = dpcd & DP_TPS3_SUPPORTED;
+
+	return source_tps3_supported && sink_tps3_supported;
+}
+
 static int analogix_dp_process_clock_recovery(struct analogix_dp_device *dp)
 {
 	int lane, lane_count, retval;
 	u8 voltage_swing, pre_emphasis, training_lane;
 	u8 link_status[2], adjust_request[2];
+	u8 training_pattern = TRAINING_PTN2;
 
-	usleep_range(100, 101);
+	drm_dp_link_train_clock_recovery_delay(dp->dpcd);
 
 	lane_count = dp->link_train.lane_count;
 
@@ -483,12 +499,16 @@ static int analogix_dp_process_clock_recovery(struct analogix_dp_device *dp)
 		return retval;
 
 	if (analogix_dp_clock_recovery_ok(link_status, lane_count) == 0) {
-		/* set training pattern 2 for EQ */
-		analogix_dp_set_training_pattern(dp, TRAINING_PTN2);
+		if (analogix_dp_tps3_supported(dp))
+			training_pattern = TRAINING_PTN3;
+
+		/* set training pattern for EQ */
+		analogix_dp_set_training_pattern(dp, training_pattern);
 
 		retval = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
 					    DP_LINK_SCRAMBLING_DISABLE |
-						DP_TRAINING_PATTERN_2);
+					    (training_pattern == TRAINING_PTN3 ?
+					     DP_TRAINING_PATTERN_3 : DP_TRAINING_PATTERN_2));
 		if (retval < 0)
 			return retval;
 
@@ -522,10 +542,7 @@ static int analogix_dp_process_clock_recovery(struct analogix_dp_device *dp)
 	}
 
 	analogix_dp_get_adjust_training_lane(dp, adjust_request);
-
-	for (lane = 0; lane < lane_count; lane++)
-		analogix_dp_set_lane_link_training(dp,
-			dp->link_train.training_lane[lane], lane);
+	analogix_dp_set_lane_link_training(dp);
 
 	retval = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET,
 				   dp->link_train.training_lane, lane_count);
@@ -537,11 +554,11 @@ static int analogix_dp_process_clock_recovery(struct analogix_dp_device *dp)
 
 static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp)
 {
-	int lane, lane_count, retval;
+	int lane_count, retval;
 	u32 reg;
 	u8 link_align, link_status[2], adjust_request[2];
 
-	usleep_range(400, 401);
+	drm_dp_link_train_channel_eq_delay(dp->dpcd);
 
 	lane_count = dp->link_train.lane_count;
 
@@ -597,9 +614,7 @@ static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp)
 		return -EIO;
 	}
 
-	for (lane = 0; lane < lane_count; lane++)
-		analogix_dp_set_lane_link_training(dp,
-			dp->link_train.training_lane[lane], lane);
+	analogix_dp_set_lane_link_training(dp);
 
 	retval = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET,
 				   dp->link_train.training_lane, lane_count);
@@ -609,10 +624,11 @@ static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp)
 	return 0;
 }
 
-static void analogix_dp_get_max_rx_bandwidth(struct analogix_dp_device *dp,
-					     u8 *bandwidth)
+static int analogix_dp_get_max_rx_bandwidth(struct analogix_dp_device *dp,
+					    u8 *bandwidth)
 {
 	u8 data;
+	int ret;
 
 	/*
 	 * For DP rev.1.1, Maximum link rate of Main Link lanes
@@ -620,28 +636,41 @@ static void analogix_dp_get_max_rx_bandwidth(struct analogix_dp_device *dp,
 	 * For DP rev.1.2, Maximum link rate of Main Link lanes
 	 * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps, 0x14 = 5.4Gbps
 	 */
-	drm_dp_dpcd_readb(&dp->aux, DP_MAX_LINK_RATE, &data);
+	ret = drm_dp_dpcd_readb(&dp->aux, DP_MAX_LINK_RATE, &data);
+	if (ret < 0)
+		return ret;
+
 	*bandwidth = data;
+
+	return 0;
 }
 
-static void analogix_dp_get_max_rx_lane_count(struct analogix_dp_device *dp,
-					      u8 *lane_count)
+static int analogix_dp_get_max_rx_lane_count(struct analogix_dp_device *dp,
+					     u8 *lane_count)
 {
 	u8 data;
+	int ret;
 
 	/*
 	 * For DP rev.1.1, Maximum number of Main Link lanes
 	 * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes
 	 */
-	drm_dp_dpcd_readb(&dp->aux, DP_MAX_LANE_COUNT, &data);
+	ret = drm_dp_dpcd_readb(&dp->aux, DP_MAX_LANE_COUNT, &data);
+	if (ret < 0)
+		return ret;
+
 	*lane_count = DPCD_MAX_LANE_COUNT(data);
+
+	return 0;
 }
 
 static int analogix_dp_full_link_train(struct analogix_dp_device *dp,
 				       u32 max_lanes, u32 max_rate)
 {
+	struct video_info *video = &dp->video_info;
 	int retval = 0;
 	bool training_finished = false;
+	u8 dpcd;
 
 	/*
 	 * MACRO_RST must be applied after the PLL_LOCK to avoid
@@ -667,6 +696,16 @@ static int analogix_dp_full_link_train(struct analogix_dp_device *dp,
 		dp->link_train.lane_count = (u8)LANE_COUNT1;
 	}
 
+	if (!analogix_dp_bandwidth_ok(dp, &video->mode,
+				      drm_dp_bw_code_to_link_rate(dp->link_train.link_rate),
+				      dp->link_train.lane_count)) {
+		dev_err(dp->dev, "bandwidth overflow\n");
+		return -EINVAL;
+	}
+
+	drm_dp_dpcd_readb(&dp->aux, DP_MAX_DOWNSPREAD, &dpcd);
+	dp->link_train.ssc = !!(dpcd & DP_MAX_DOWNSPREAD_0_5);
+
 	/* Setup TX lane count & rate */
 	if (dp->link_train.lane_count > max_lanes)
 		dp->link_train.lane_count = max_lanes;
@@ -711,27 +750,15 @@ static int analogix_dp_full_link_train(struct analogix_dp_device *dp,
 
 static int analogix_dp_fast_link_train(struct analogix_dp_device *dp)
 {
-	int i, ret;
+	int ret;
 	u8 link_align, link_status[2];
-	enum pll_status status;
 
 	analogix_dp_reset_macro(dp);
 
 	analogix_dp_set_link_bandwidth(dp, dp->link_train.link_rate);
 	analogix_dp_set_lane_count(dp, dp->link_train.lane_count);
-
-	for (i = 0; i < dp->link_train.lane_count; i++) {
-		analogix_dp_set_lane_link_training(dp,
-			dp->link_train.training_lane[i], i);
-	}
-
-	ret = readx_poll_timeout(analogix_dp_get_pll_lock_status, dp, status,
-				 status != PLL_UNLOCKED, 120,
-				 120 * DP_TIMEOUT_LOOP_COUNT);
-	if (ret) {
-		DRM_DEV_ERROR(dp->dev, "Wait for pll lock failed %d\n", ret);
-		return ret;
-	}
+	analogix_dp_set_lane_link_training(dp);
+	analogix_dp_enable_enhanced_mode(dp, dp->link_train.enhanced_framing);
 
 	/* source Set training pattern 1 */
 	analogix_dp_set_training_pattern(dp, TRAINING_PTN1);
@@ -742,7 +769,6 @@ static int analogix_dp_fast_link_train(struct analogix_dp_device *dp)
 	/* From DP spec, pattern must be on-screen for a minimum 500us */
 	usleep_range(500, 600);
 
-	/* TODO: enhanced_mode?*/
 	analogix_dp_set_training_pattern(dp, DP_NONE);
 
 	/*
@@ -884,25 +910,44 @@ static int analogix_dp_enable_scramble(struct analogix_dp_device *dp,
 	return ret < 0 ? ret : 0;
 }
 
+static irqreturn_t analogix_dp_hpd_irq_handler(int irq, void *arg)
+{
+	struct analogix_dp_device *dp = arg;
+
+	if (dp->drm_dev)
+		drm_helper_hpd_irq_event(dp->drm_dev);
+
+	return IRQ_HANDLED;
+}
+
 static irqreturn_t analogix_dp_hardirq(int irq, void *arg)
 {
 	struct analogix_dp_device *dp = arg;
-	irqreturn_t ret = IRQ_NONE;
 	enum dp_irq_type irq_type;
+	int ret;
+
+	ret = pm_runtime_get_sync(dp->dev);
+	if (ret < 0)
+		return IRQ_NONE;
 
 	irq_type = analogix_dp_get_irq_type(dp);
-	if (irq_type != DP_IRQ_TYPE_UNKNOWN) {
+	if (irq_type != DP_IRQ_TYPE_UNKNOWN)
 		analogix_dp_mute_hpd_interrupt(dp);
-		ret = IRQ_WAKE_THREAD;
-	}
 
-	return ret;
+	pm_runtime_put_sync(dp->dev);
+
+	return IRQ_WAKE_THREAD;
 }
 
 static irqreturn_t analogix_dp_irq_thread(int irq, void *arg)
 {
 	struct analogix_dp_device *dp = arg;
 	enum dp_irq_type irq_type;
+	int ret;
+
+	ret = pm_runtime_get_sync(dp->dev);
+	if (ret < 0)
+		return IRQ_NONE;
 
 	irq_type = analogix_dp_get_irq_type(dp);
 	if (irq_type & DP_IRQ_TYPE_HP_CABLE_IN ||
@@ -917,6 +962,8 @@ static irqreturn_t analogix_dp_irq_thread(int irq, void *arg)
 		analogix_dp_unmute_hpd_interrupt(dp);
 	}
 
+	pm_runtime_put_sync(dp->dev);
+
 	return IRQ_HANDLED;
 }
 
@@ -936,16 +983,73 @@ static int analogix_dp_fast_link_train_detection(struct analogix_dp_device *dp)
 	return 0;
 }
 
+static int analogix_dp_link_power_up(struct analogix_dp_device *dp)
+{
+	u8 value;
+	int ret;
+
+	if (dp->dpcd[DP_DPCD_REV] < 0x11)
+		return 0;
+
+	ret = drm_dp_dpcd_readb(&dp->aux, DP_SET_POWER, &value);
+	if (ret < 0)
+		return ret;
+
+	value &= ~DP_SET_POWER_MASK;
+	value |= DP_SET_POWER_D0;
+
+	ret = drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, value);
+	if (ret < 0)
+		return ret;
+
+	usleep_range(1000, 2000);
+
+	return 0;
+}
+
+static int analogix_dp_link_power_down(struct analogix_dp_device *dp)
+{
+	u8 value;
+	int ret;
+
+	if (dp->dpcd[DP_DPCD_REV] < 0x11)
+		return 0;
+
+	ret = drm_dp_dpcd_readb(&dp->aux, DP_SET_POWER, &value);
+	if (ret < 0)
+		return ret;
+
+	value &= ~DP_SET_POWER_MASK;
+	value |= DP_SET_POWER_D3;
+
+	ret = drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, value);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
 static int analogix_dp_commit(struct analogix_dp_device *dp)
 {
+	struct video_info *video = &dp->video_info;
 	int ret;
 
-	/* Keep the panel disabled while we configure video */
-	if (dp->plat_data->panel) {
-		if (drm_panel_disable(dp->plat_data->panel))
-			DRM_ERROR("failed to disable the panel\n");
+	ret = drm_dp_read_dpcd_caps(&dp->aux, dp->dpcd);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to read dpcd caps: %d\n", ret);
+		return ret;
+	}
+
+	ret = analogix_dp_link_power_up(dp);
+	if (ret) {
+		dev_err(dp->dev, "failed to power up link: %d\n", ret);
+		return ret;
 	}
 
+	if (device_property_read_bool(dp->dev, "panel-self-test"))
+		return drm_dp_dpcd_writeb(&dp->aux, DP_EDP_CONFIGURATION_SET,
+					  DP_PANEL_SELF_TEST_ENABLE);
+
 	ret = analogix_dp_train_link(dp);
 	if (ret) {
 		dev_err(dp->dev, "unable to do link train, ret=%d\n", ret);
@@ -959,21 +1063,17 @@ static int analogix_dp_commit(struct analogix_dp_device *dp)
 	}
 
 	analogix_dp_init_video(dp);
+	analogix_dp_set_video_format(dp);
+
+	if (video->video_bist_enable)
+		analogix_dp_video_bist_enable(dp);
+
 	ret = analogix_dp_config_video(dp);
 	if (ret) {
 		dev_err(dp->dev, "unable to config video\n");
 		return ret;
 	}
 
-	/* Safe to enable the panel now */
-	if (dp->plat_data->panel) {
-		ret = drm_panel_enable(dp->plat_data->panel);
-		if (ret) {
-			DRM_ERROR("failed to enable the panel\n");
-			return ret;
-		}
-	}
-
 	/* Check whether panel supports fast training */
 	ret = analogix_dp_fast_link_train_detection(dp);
 	if (ret)
@@ -1058,66 +1158,18 @@ static int analogix_dp_disable_psr(struct analogix_dp_device *dp)
 	return analogix_dp_send_psr_spd(dp, &psr_vsc, true);
 }
 
-/*
- * This function is a bit of a catch-all for panel preparation, hopefully
- * simplifying the logic of functions that need to prepare/unprepare the panel
- * below.
- *
- * If @prepare is true, this function will prepare the panel. Conversely, if it
- * is false, the panel will be unprepared.
- *
- * If @is_modeset_prepare is true, the function will disregard the current state
- * of the panel and either prepare/unprepare the panel based on @prepare. Once
- * it finishes, it will update dp->panel_is_modeset to reflect the current state
- * of the panel.
- */
-static int analogix_dp_prepare_panel(struct analogix_dp_device *dp,
-				     bool prepare, bool is_modeset_prepare)
-{
-	int ret = 0;
-
-	if (!dp->plat_data->panel)
-		return 0;
-
-	mutex_lock(&dp->panel_lock);
-
-	/*
-	 * Exit early if this is a temporary prepare/unprepare and we're already
-	 * modeset (since we neither want to prepare twice or unprepare early).
-	 */
-	if (dp->panel_is_modeset && !is_modeset_prepare)
-		goto out;
-
-	if (prepare)
-		ret = drm_panel_prepare(dp->plat_data->panel);
-	else
-		ret = drm_panel_unprepare(dp->plat_data->panel);
-
-	if (ret)
-		goto out;
-
-	if (is_modeset_prepare)
-		dp->panel_is_modeset = prepare;
-
-out:
-	mutex_unlock(&dp->panel_lock);
-	return ret;
-}
-
 static int analogix_dp_get_modes(struct drm_connector *connector)
 {
 	struct analogix_dp_device *dp = to_dp(connector);
 	struct edid *edid;
-	int ret, num_modes = 0;
+	int num_modes = 0;
 
-	if (dp->plat_data->panel) {
+	if (dp->plat_data->panel)
 		num_modes += drm_panel_get_modes(dp->plat_data->panel, connector);
-	} else {
-		ret = analogix_dp_prepare_panel(dp, true, false);
-		if (ret) {
-			DRM_ERROR("Failed to prepare panel (%d)\n", ret);
-			return 0;
-		}
+
+	if (!num_modes) {
+		if (dp->plat_data->panel)
+			analogix_dp_panel_prepare(dp);
 
 		pm_runtime_get_sync(dp->dev);
 		edid = drm_get_edid(connector, &dp->aux.ddc);
@@ -1128,15 +1180,18 @@ static int analogix_dp_get_modes(struct drm_connector *connector)
 			num_modes += drm_add_edid_modes(&dp->connector, edid);
 			kfree(edid);
 		}
-
-		ret = analogix_dp_prepare_panel(dp, false, false);
-		if (ret)
-			DRM_ERROR("Failed to unprepare panel (%d)\n", ret);
 	}
 
 	if (dp->plat_data->get_modes)
 		num_modes += dp->plat_data->get_modes(dp->plat_data, connector);
 
+	if (num_modes > 0 && dp->plat_data->split_mode) {
+		struct drm_display_mode *mode;
+
+		list_for_each_entry(mode, &connector->probed_modes, head)
+			dp->plat_data->convert_to_split_mode(mode);
+	}
+
 	return num_modes;
 }
 
@@ -1182,34 +1237,52 @@ static const struct drm_connector_helper_funcs analogix_dp_connector_helper_func
 };
 
 static enum drm_connector_status
-analogix_dp_detect(struct drm_connector *connector, bool force)
+analogix_dp_detect(struct analogix_dp_device *dp)
 {
-	struct analogix_dp_device *dp = to_dp(connector);
 	enum drm_connector_status status = connector_status_disconnected;
 	int ret;
 
 	if (dp->plat_data->panel)
-		return connector_status_connected;
+		analogix_dp_panel_prepare(dp);
 
-	ret = analogix_dp_prepare_panel(dp, true, false);
-	if (ret) {
-		DRM_ERROR("Failed to prepare panel (%d)\n", ret);
-		return connector_status_disconnected;
-	}
+	pm_runtime_get_sync(dp->dev);
+
+	if (!analogix_dp_detect_hpd(dp)) {
+		ret = analogix_dp_get_max_rx_bandwidth(dp, &dp->link_train.link_rate);
+		if (ret) {
+			dev_err(dp->dev, "failed to read max link rate\n");
+			goto out;
+		}
+
+		ret = analogix_dp_get_max_rx_lane_count(dp, &dp->link_train.lane_count);
+		if (ret) {
+			dev_err(dp->dev, "failed to read max lane count\n");
+			goto out;
+		}
 
-	if (!analogix_dp_detect_hpd(dp))
 		status = connector_status_connected;
+	}
 
-	ret = analogix_dp_prepare_panel(dp, false, false);
-	if (ret)
-		DRM_ERROR("Failed to unprepare panel (%d)\n", ret);
+out:
+	pm_runtime_put(dp->dev);
 
 	return status;
 }
 
+static enum drm_connector_status
+analogix_dp_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct analogix_dp_device *dp = to_dp(connector);
+
+	if (dp->plat_data->right && analogix_dp_detect(dp->plat_data->right) != connector_status_connected)
+		return connector_status_disconnected;
+
+	return analogix_dp_detect(dp);
+}
+
 static const struct drm_connector_funcs analogix_dp_connector_funcs = {
 	.fill_modes = drm_helper_probe_single_connector_modes,
-	.detect = analogix_dp_detect,
+	.detect = analogix_dp_connector_detect,
 	.destroy = drm_connector_cleanup,
 	.reset = drm_atomic_helper_connector_reset,
 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
@@ -1224,10 +1297,8 @@ static int analogix_dp_bridge_attach(struct drm_bridge *bridge,
 	struct drm_connector *connector = NULL;
 	int ret = 0;
 
-	if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
-		DRM_ERROR("Fix bridge driver to make connector optional!");
-		return -EINVAL;
-	}
+	if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
+		return 0;
 
 	if (!bridge->encoder) {
 		DRM_ERROR("Parent encoder object not found");
@@ -1268,23 +1339,12 @@ static int analogix_dp_bridge_attach(struct drm_bridge *bridge,
 	return 0;
 }
 
-static
-struct drm_crtc *analogix_dp_get_old_crtc(struct analogix_dp_device *dp,
-					  struct drm_atomic_state *state)
+static void analogix_dp_bridge_detach(struct drm_bridge *bridge)
 {
-	struct drm_encoder *encoder = dp->encoder;
-	struct drm_connector *connector;
-	struct drm_connector_state *conn_state;
-
-	connector = drm_atomic_get_old_connector_for_encoder(state, encoder);
-	if (!connector)
-		return NULL;
-
-	conn_state = drm_atomic_get_old_connector_state(state, connector);
-	if (!conn_state)
-		return NULL;
+	struct analogix_dp_device *dp = bridge->driver_private;
 
-	return conn_state->crtc;
+	if (dp->plat_data->detach)
+		dp->plat_data->detach(dp->plat_data, bridge);
 }
 
 static
@@ -1314,20 +1374,20 @@ analogix_dp_bridge_atomic_pre_enable(struct drm_bridge *bridge,
 	struct analogix_dp_device *dp = bridge->driver_private;
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *old_crtc_state;
-	int ret;
 
-	crtc = analogix_dp_get_new_crtc(dp, old_state);
-	if (!crtc)
-		return;
+	if (dp->psr_supported) {
+		crtc = analogix_dp_get_new_crtc(dp, old_state);
+		if (!crtc)
+			return;
 
-	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
-	/* Don't touch the panel if we're coming back from PSR */
-	if (old_crtc_state && old_crtc_state->self_refresh_active)
-		return;
+		old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
+		/* Don't touch the panel if we're coming back from PSR */
+		if (old_crtc_state && old_crtc_state->self_refresh_active)
+			return;
+	}
 
-	ret = analogix_dp_prepare_panel(dp, true, true);
-	if (ret)
-		DRM_ERROR("failed to setup the panel ret = %d\n", ret);
+	if (dp->plat_data->panel)
+		analogix_dp_panel_prepare(dp);
 }
 
 static int analogix_dp_set_bridge(struct analogix_dp_device *dp)
@@ -1336,16 +1396,10 @@ static int analogix_dp_set_bridge(struct analogix_dp_device *dp)
 
 	pm_runtime_get_sync(dp->dev);
 
-	ret = clk_prepare_enable(dp->clock);
-	if (ret < 0) {
-		DRM_ERROR("Failed to prepare_enable the clock clk [%d]\n", ret);
-		goto out_dp_clk_pre;
-	}
-
 	if (dp->plat_data->power_on_start)
 		dp->plat_data->power_on_start(dp->plat_data);
 
-	phy_power_on(dp->phy);
+	analogix_dp_phy_power_on(dp);
 
 	ret = analogix_dp_init_dp(dp);
 	if (ret)
@@ -1363,11 +1417,14 @@ static int analogix_dp_set_bridge(struct analogix_dp_device *dp)
 	}
 
 	ret = analogix_dp_commit(dp);
-	if (ret) {
+	if (ret < 0) {
 		DRM_ERROR("dp commit error, ret = %d\n", ret);
 		goto out_dp_init;
 	}
 
+	if (dp->plat_data->panel)
+		drm_panel_enable(dp->plat_data->panel);
+
 	if (dp->plat_data->power_on_end)
 		dp->plat_data->power_on_end(dp->plat_data);
 
@@ -1375,11 +1432,9 @@ static int analogix_dp_set_bridge(struct analogix_dp_device *dp)
 	return 0;
 
 out_dp_init:
-	phy_power_off(dp->phy);
+	analogix_dp_phy_power_off(dp);
 	if (dp->plat_data->power_off)
 		dp->plat_data->power_off(dp->plat_data);
-	clk_disable_unprepare(dp->clock);
-out_dp_clk_pre:
 	pm_runtime_put_sync(dp->dev);
 
 	return ret;
@@ -1396,17 +1451,19 @@ analogix_dp_bridge_atomic_enable(struct drm_bridge *bridge,
 	int timeout_loop = 0;
 	int ret;
 
-	crtc = analogix_dp_get_new_crtc(dp, old_state);
-	if (!crtc)
-		return;
+	if (dp->psr_supported) {
+		crtc = analogix_dp_get_new_crtc(dp, old_state);
+		if (!crtc)
+			return;
 
-	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
-	/* Not a full enable, just disable PSR and continue */
-	if (old_crtc_state && old_crtc_state->self_refresh_active) {
-		ret = analogix_dp_disable_psr(dp);
-		if (ret)
-			DRM_ERROR("Failed to disable psr %d\n", ret);
-		return;
+		old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
+		/* Not a full enable, just disable PSR and continue */
+		if (old_crtc_state && old_crtc_state->self_refresh_active) {
+			ret = analogix_dp_disable_psr(dp);
+			if (ret)
+				DRM_ERROR("Failed to disable psr %d\n", ret);
+			return;
+		}
 	}
 
 	if (dp->dpms_mode == DRM_MODE_DPMS_ON)
@@ -1428,7 +1485,6 @@ analogix_dp_bridge_atomic_enable(struct drm_bridge *bridge,
 static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
 {
 	struct analogix_dp_device *dp = bridge->driver_private;
-	int ret;
 
 	if (dp->dpms_mode != DRM_MODE_DPMS_ON)
 		return;
@@ -1440,21 +1496,22 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
 		}
 	}
 
+	if (!analogix_dp_get_plug_in_status(dp))
+		analogix_dp_link_power_down(dp);
+
 	disable_irq(dp->irq);
 
 	if (dp->plat_data->power_off)
 		dp->plat_data->power_off(dp->plat_data);
 
+	analogix_dp_reset_aux(dp);
 	analogix_dp_set_analog_power_down(dp, POWER_ALL, 1);
-	phy_power_off(dp->phy);
-
-	clk_disable_unprepare(dp->clock);
+	analogix_dp_phy_power_off(dp);
 
 	pm_runtime_put_sync(dp->dev);
 
-	ret = analogix_dp_prepare_panel(dp, false, true);
-	if (ret)
-		DRM_ERROR("failed to setup the panel ret = %d\n", ret);
+	if (dp->plat_data->panel)
+		analogix_dp_panel_unprepare(dp);
 
 	dp->fast_train_enable = false;
 	dp->psr_supported = false;
@@ -1467,16 +1524,14 @@ analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge,
 {
 	struct drm_atomic_state *old_state = old_bridge_state->base.state;
 	struct analogix_dp_device *dp = bridge->driver_private;
-	struct drm_crtc *old_crtc, *new_crtc;
-	struct drm_crtc_state *old_crtc_state = NULL;
+	struct drm_crtc *crtc;
 	struct drm_crtc_state *new_crtc_state = NULL;
-	int ret;
 
-	new_crtc = analogix_dp_get_new_crtc(dp, old_state);
-	if (!new_crtc)
+	crtc = analogix_dp_get_new_crtc(dp, old_state);
+	if (!crtc)
 		goto out;
 
-	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, new_crtc);
+	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
 	if (!new_crtc_state)
 		goto out;
 
@@ -1485,19 +1540,6 @@ analogix_dp_bridge_atomic_disable(struct drm_bridge *bridge,
 		return;
 
 out:
-	old_crtc = analogix_dp_get_old_crtc(dp, old_state);
-	if (old_crtc) {
-		old_crtc_state = drm_atomic_get_old_crtc_state(old_state,
-							       old_crtc);
-
-		/* When moving from PSR to fully disabled, exit PSR first. */
-		if (old_crtc_state && old_crtc_state->self_refresh_active) {
-			ret = analogix_dp_disable_psr(dp);
-			if (ret)
-				DRM_ERROR("Failed to disable psr (%d)\n", ret);
-		}
-	}
-
 	analogix_dp_bridge_disable(bridge);
 }
 
@@ -1526,14 +1568,19 @@ analogix_dp_bridge_atomic_post_disable(struct drm_bridge *bridge,
 
 static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge,
 				const struct drm_display_mode *orig_mode,
-				const struct drm_display_mode *mode)
+				const struct drm_display_mode *adj_mode)
 {
 	struct analogix_dp_device *dp = bridge->driver_private;
 	struct drm_display_info *display_info = &dp->connector.display_info;
 	struct video_info *video = &dp->video_info;
+	struct drm_display_mode *mode = &video->mode;
 	struct device_node *dp_node = dp->dev->of_node;
 	int vic;
 
+	drm_mode_copy(mode, adj_mode);
+	if (dp->plat_data->split_mode)
+		dp->plat_data->convert_to_origin_mode(mode);
+
 	/* Input video interlaces & hsync pol & vsync pol */
 	video->interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
 	video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
@@ -1601,6 +1648,27 @@ static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge,
 		video->interlaced = true;
 }
 
+static enum drm_mode_status
+analogix_dp_bridge_mode_valid(struct drm_bridge *bridge,
+			      const struct drm_display_info *info,
+			      const struct drm_display_mode *mode)
+{
+	struct analogix_dp_device *dp = bridge->driver_private;
+	struct drm_display_mode m;
+
+	drm_mode_copy(&m, mode);
+
+	if (dp->plat_data->split_mode)
+		dp->plat_data->convert_to_origin_mode(&m);
+
+	if (!analogix_dp_bandwidth_ok(dp, &m,
+				      drm_dp_bw_code_to_link_rate(dp->link_train.link_rate),
+				      dp->link_train.lane_count))
+		return MODE_BAD;
+
+	return MODE_OK;
+}
+
 static const struct drm_bridge_funcs analogix_dp_bridge_funcs = {
 	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
@@ -1611,29 +1679,30 @@ static const struct drm_bridge_funcs analogix_dp_bridge_funcs = {
 	.atomic_post_disable = analogix_dp_bridge_atomic_post_disable,
 	.mode_set = analogix_dp_bridge_mode_set,
 	.attach = analogix_dp_bridge_attach,
+	.detach = analogix_dp_bridge_detach,
+	.mode_valid = analogix_dp_bridge_mode_valid,
 };
 
-static int analogix_dp_create_bridge(struct drm_device *drm_dev,
-				     struct analogix_dp_device *dp)
+static int analogix_dp_bridge_init(struct analogix_dp_device *dp)
 {
-	struct drm_bridge *bridge;
+	struct drm_bridge *bridge = &dp->bridge;
 	int ret;
 
-	bridge = devm_kzalloc(drm_dev->dev, sizeof(*bridge), GFP_KERNEL);
-	if (!bridge) {
-		DRM_ERROR("failed to allocate for drm bridge\n");
-		return -ENOMEM;
+	if (!dp->plat_data->left) {
+		ret = drm_bridge_attach(dp->encoder, bridge, NULL, 0);
+		if (ret) {
+			DRM_ERROR("failed to attach drm bridge\n");
+			return ret;
+		}
 	}
 
-	dp->bridge = bridge;
+	if (dp->plat_data->right) {
+		struct analogix_dp_device *secondary = dp->plat_data->right;
 
-	bridge->driver_private = dp;
-	bridge->funcs = &analogix_dp_bridge_funcs;
-
-	ret = drm_bridge_attach(dp->encoder, bridge, NULL, 0);
-	if (ret) {
-		DRM_ERROR("failed to attach drm bridge\n");
-		return -EINVAL;
+		ret = drm_bridge_attach(dp->encoder, &secondary->bridge, bridge,
+					DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+		if (ret)
+			return ret;
 	}
 
 	return 0;
@@ -1646,7 +1715,7 @@ static int analogix_dp_dt_parse_pdata(struct analogix_dp_device *dp)
 
 	switch (dp->plat_data->dev_type) {
 	case RK3288_DP:
-	case RK3399_EDP:
+	case RK3568_EDP:
 		/*
 		 * Like Rk3288 DisplayPort TRM indicate that "Main link
 		 * containing 4 physical lanes of 2.7/1.62 Gbps/lane".
@@ -1654,6 +1723,11 @@ static int analogix_dp_dt_parse_pdata(struct analogix_dp_device *dp)
 		video_info->max_link_rate = 0x0A;
 		video_info->max_lane_count = 0x04;
 		break;
+	case RK3399_EDP:
+	case RK3588_EDP:
+		video_info->max_link_rate = 0x14;
+		video_info->max_lane_count = 0x04;
+		break;
 	case EXYNOS_DP:
 		/*
 		 * NOTE: those property parseing code is used for
@@ -1666,6 +1740,9 @@ static int analogix_dp_dt_parse_pdata(struct analogix_dp_device *dp)
 		break;
 	}
 
+	video_info->video_bist_enable =
+		of_property_read_bool(dp_node, "analogix,video-bist-enable");
+
 	return 0;
 }
 
@@ -1673,20 +1750,69 @@ static ssize_t analogix_dpaux_transfer(struct drm_dp_aux *aux,
 				       struct drm_dp_aux_msg *msg)
 {
 	struct analogix_dp_device *dp = to_dp(aux);
+
+	return analogix_dp_transfer(dp, msg);
+}
+
+int analogix_dp_audio_hw_params(struct analogix_dp_device *dp,
+				struct hdmi_codec_daifmt *daifmt,
+				struct hdmi_codec_params *params)
+{
+	switch (daifmt->fmt) {
+	case HDMI_SPDIF:
+		analogix_dp_audio_config_spdif(dp);
+		break;
+	case HDMI_I2S:
+		analogix_dp_audio_config_i2s(dp);
+		break;
+	default:
+		DRM_DEV_ERROR(dp->dev, "invalid daifmt %d\n", daifmt->fmt);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(analogix_dp_audio_hw_params);
+
+void analogix_dp_audio_shutdown(struct analogix_dp_device *dp)
+{
+	analogix_dp_audio_disable(dp);
+}
+EXPORT_SYMBOL_GPL(analogix_dp_audio_shutdown);
+
+int analogix_dp_audio_startup(struct analogix_dp_device *dp)
+{
+	analogix_dp_audio_enable(dp);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(analogix_dp_audio_startup);
+
+int analogix_dp_audio_get_eld(struct analogix_dp_device *dp, u8 *buf, size_t len)
+{
+	memcpy(buf, dp->connector.eld, min(sizeof(dp->connector.eld), len));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(analogix_dp_audio_get_eld);
+
+int analogix_dp_loader_protect(struct analogix_dp_device *dp)
+{
 	int ret;
 
-	pm_runtime_get_sync(dp->dev);
+	ret = pm_runtime_resume_and_get(dp->dev);
+	if (ret) {
+		dev_err(dp->dev, "failed to get runtime PM: %d\n", ret);
+		return ret;
+	}
 
-	ret = analogix_dp_detect_hpd(dp);
-	if (ret)
-		goto out;
+	analogix_dp_phy_power_on(dp);
 
-	ret = analogix_dp_transfer(dp, msg);
-out:
-	pm_runtime_put(dp->dev);
+	dp->dpms_mode = DRM_MODE_DPMS_ON;
 
-	return ret;
+	return 0;
 }
+EXPORT_SYMBOL_GPL(analogix_dp_loader_protect);
 
 struct analogix_dp_device *
 analogix_dp_probe(struct device *dev, struct analogix_dp_plat_data *plat_data)
@@ -1694,7 +1820,6 @@ analogix_dp_probe(struct device *dev, struct analogix_dp_plat_data *plat_data)
 	struct platform_device *pdev = to_platform_device(dev);
 	struct analogix_dp_device *dp;
 	struct resource *res;
-	unsigned int irq_flags;
 	int ret;
 
 	if (!plat_data) {
@@ -1710,7 +1835,7 @@ analogix_dp_probe(struct device *dev, struct analogix_dp_plat_data *plat_data)
 	dp->dpms_mode = DRM_MODE_DPMS_OFF;
 
 	mutex_init(&dp->panel_lock);
-	dp->panel_is_modeset = false;
+	dp->panel_is_prepared = false;
 
 	/*
 	 * platform dp driver need containor_of the plat_data to get
@@ -1739,21 +1864,19 @@ analogix_dp_probe(struct device *dev, struct analogix_dp_plat_data *plat_data)
 		}
 	}
 
-	dp->clock = devm_clk_get(&pdev->dev, "dp");
-	if (IS_ERR(dp->clock)) {
-		dev_err(&pdev->dev, "failed to get clock\n");
-		return ERR_CAST(dp->clock);
+	ret = devm_clk_bulk_get_all(dev, &dp->clks);
+	if (ret < 0) {
+		dev_err(dev, "failed to get clocks %d\n", ret);
+		return ERR_PTR(ret);
 	}
 
-	clk_prepare_enable(dp->clock);
+	dp->nr_clks = ret;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
 	dp->reg_base = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(dp->reg_base)) {
-		ret = PTR_ERR(dp->reg_base);
-		goto err_disable_clk;
-	}
+	if (IS_ERR(dp->reg_base))
+		return ERR_CAST(dp->reg_base);
 
 	dp->force_hpd = of_property_read_bool(dev->of_node, "force-hpd");
 
@@ -1765,46 +1888,44 @@ analogix_dp_probe(struct device *dev, struct analogix_dp_plat_data *plat_data)
 	if (IS_ERR(dp->hpd_gpiod)) {
 		dev_err(dev, "error getting HDP GPIO: %ld\n",
 			PTR_ERR(dp->hpd_gpiod));
-		ret = PTR_ERR(dp->hpd_gpiod);
-		goto err_disable_clk;
+		return ERR_CAST(dp->hpd_gpiod);
 	}
 
 	if (dp->hpd_gpiod) {
-		/*
-		 * Set up the hotplug GPIO from the device tree as an interrupt.
-		 * Simply specifying a different interrupt in the device tree
-		 * doesn't work since we handle hotplug rather differently when
-		 * using a GPIO.  We also need the actual GPIO specifier so
-		 * that we can get the current state of the GPIO.
-		 */
-		dp->irq = gpiod_to_irq(dp->hpd_gpiod);
-		irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
-	} else {
-		dp->irq = platform_get_irq(pdev, 0);
-		irq_flags = 0;
+		ret = devm_request_threaded_irq(dev,
+						gpiod_to_irq(dp->hpd_gpiod),
+						NULL,
+						analogix_dp_hpd_irq_handler,
+						IRQF_TRIGGER_RISING |
+						IRQF_TRIGGER_FALLING |
+						IRQF_ONESHOT,
+						"analogix-hpd", dp);
+		if (ret) {
+			dev_err(dev, "failed to request hpd IRQ: %d\n", ret);
+			return ERR_PTR(ret);
+		}
 	}
 
+	dp->irq = platform_get_irq(pdev, 0);
 	if (dp->irq == -ENXIO) {
 		dev_err(&pdev->dev, "failed to get irq\n");
-		ret = -ENODEV;
-		goto err_disable_clk;
+		return ERR_PTR(-ENODEV);
 	}
 
+	irq_set_status_flags(dp->irq, IRQ_NOAUTOEN);
 	ret = devm_request_threaded_irq(&pdev->dev, dp->irq,
 					analogix_dp_hardirq,
 					analogix_dp_irq_thread,
-					irq_flags, "analogix-dp", dp);
+					0, "analogix-dp", dp);
 	if (ret) {
 		dev_err(&pdev->dev, "failed to request irq\n");
-		goto err_disable_clk;
+		return ERR_PTR(ret);
 	}
-	disable_irq(dp->irq);
 
-	return dp;
+	dp->bridge.driver_private = dp;
+	dp->bridge.funcs = &analogix_dp_bridge_funcs;
 
-err_disable_clk:
-	clk_disable_unprepare(dp->clock);
-	return ERR_PTR(ret);
+	return dp;
 }
 EXPORT_SYMBOL_GPL(analogix_dp_probe);
 
@@ -1825,9 +1946,9 @@ int analogix_dp_bind(struct analogix_dp_device *dp, struct drm_device *drm_dev)
 
 	pm_runtime_enable(dp->dev);
 
-	ret = analogix_dp_create_bridge(drm_dev, dp);
+	ret = analogix_dp_bridge_init(dp);
 	if (ret) {
-		DRM_ERROR("failed to create bridge (%d)\n", ret);
+		DRM_ERROR("failed to init bridge (%d)\n", ret);
 		goto err_disable_pm_runtime;
 	}
 
@@ -1842,14 +1963,7 @@ EXPORT_SYMBOL_GPL(analogix_dp_bind);
 
 void analogix_dp_unbind(struct analogix_dp_device *dp)
 {
-	analogix_dp_bridge_disable(dp->bridge);
 	dp->connector.funcs->destroy(&dp->connector);
-
-	if (dp->plat_data->panel) {
-		if (drm_panel_unprepare(dp->plat_data->panel))
-			DRM_ERROR("failed to turnoff the panel\n");
-	}
-
 	drm_dp_aux_unregister(&dp->aux);
 	pm_runtime_disable(dp->dev);
 }
@@ -1857,32 +1971,22 @@ EXPORT_SYMBOL_GPL(analogix_dp_unbind);
 
 void analogix_dp_remove(struct analogix_dp_device *dp)
 {
-	clk_disable_unprepare(dp->clock);
 }
 EXPORT_SYMBOL_GPL(analogix_dp_remove);
 
-#ifdef CONFIG_PM
-int analogix_dp_suspend(struct analogix_dp_device *dp)
+int analogix_dp_runtime_suspend(struct analogix_dp_device *dp)
 {
-	clk_disable_unprepare(dp->clock);
+	clk_bulk_disable_unprepare(dp->nr_clks, dp->clks);
+
 	return 0;
 }
-EXPORT_SYMBOL_GPL(analogix_dp_suspend);
+EXPORT_SYMBOL_GPL(analogix_dp_runtime_suspend);
 
-int analogix_dp_resume(struct analogix_dp_device *dp)
+int analogix_dp_runtime_resume(struct analogix_dp_device *dp)
 {
-	int ret;
-
-	ret = clk_prepare_enable(dp->clock);
-	if (ret < 0) {
-		DRM_ERROR("Failed to prepare_enable the clock clk [%d]\n", ret);
-		return ret;
-	}
-
-	return 0;
+	return clk_bulk_prepare_enable(dp->nr_clks, dp->clks);
 }
-EXPORT_SYMBOL_GPL(analogix_dp_resume);
-#endif
+EXPORT_SYMBOL_GPL(analogix_dp_runtime_resume);
 
 int analogix_dp_start_crc(struct drm_connector *connector)
 {
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
index c051502d7..804a87d59 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
@@ -10,6 +10,7 @@
 #define _ANALOGIX_DP_CORE_H
 
 #include <drm/drm_crtc.h>
+#include <drm/drm_bridge.h>
 #include <drm/drm_dp_helper.h>
 
 #define DP_TIMEOUT_LOOP_COUNT 100
@@ -69,6 +70,7 @@ enum pattern_set {
 	D10_2,
 	TRAINING_PTN1,
 	TRAINING_PTN2,
+	TRAINING_PTN3,
 	DP_NONE
 };
 
@@ -129,6 +131,7 @@ enum dp_irq_type {
 
 struct video_info {
 	char *name;
+	struct drm_display_mode mode;
 
 	bool h_sync_polarity;
 	bool v_sync_polarity;
@@ -141,6 +144,8 @@ struct video_info {
 
 	int max_link_rate;
 	enum link_lane_count_type max_lane_count;
+
+	bool video_bist_enable;
 };
 
 struct link_train {
@@ -150,6 +155,8 @@ struct link_train {
 	u8 link_rate;
 	u8 lane_count;
 	u8 training_lane[4];
+	bool ssc;
+	bool enhanced_framing;
 
 	enum link_training_state lt_state;
 };
@@ -159,15 +166,17 @@ struct analogix_dp_device {
 	struct device		*dev;
 	struct drm_device	*drm_dev;
 	struct drm_connector	connector;
-	struct drm_bridge	*bridge;
+	struct drm_bridge	bridge;
 	struct drm_dp_aux       aux;
-	struct clk		*clock;
+	struct clk_bulk_data	*clks;
+	int			nr_clks;
 	unsigned int		irq;
 	void __iomem		*reg_base;
 
 	struct video_info	video_info;
 	struct link_train	link_train;
 	struct phy		*phy;
+	bool			phy_enabled;
 	int			dpms_mode;
 	struct gpio_desc	*hpd_gpiod;
 	bool                    force_hpd;
@@ -175,8 +184,9 @@ struct analogix_dp_device {
 	bool			psr_supported;
 
 	struct mutex		panel_lock;
-	bool			panel_is_modeset;
+	bool			panel_is_prepared;
 
+	u8 dpcd[DP_RECEIVER_CAP_SIZE];
 	struct analogix_dp_plat_data *plat_data;
 };
 
@@ -213,26 +223,8 @@ void analogix_dp_enable_enhanced_mode(struct analogix_dp_device *dp,
 				      bool enable);
 void analogix_dp_set_training_pattern(struct analogix_dp_device *dp,
 				      enum pattern_set pattern);
-void analogix_dp_set_lane0_pre_emphasis(struct analogix_dp_device *dp,
-					u32 level);
-void analogix_dp_set_lane1_pre_emphasis(struct analogix_dp_device *dp,
-					u32 level);
-void analogix_dp_set_lane2_pre_emphasis(struct analogix_dp_device *dp,
-					u32 level);
-void analogix_dp_set_lane3_pre_emphasis(struct analogix_dp_device *dp,
-					u32 level);
-void analogix_dp_set_lane0_link_training(struct analogix_dp_device *dp,
-					 u32 training_lane);
-void analogix_dp_set_lane1_link_training(struct analogix_dp_device *dp,
-					 u32 training_lane);
-void analogix_dp_set_lane2_link_training(struct analogix_dp_device *dp,
-					 u32 training_lane);
-void analogix_dp_set_lane3_link_training(struct analogix_dp_device *dp,
-					 u32 training_lane);
-u32 analogix_dp_get_lane0_link_training(struct analogix_dp_device *dp);
-u32 analogix_dp_get_lane1_link_training(struct analogix_dp_device *dp);
-u32 analogix_dp_get_lane2_link_training(struct analogix_dp_device *dp);
-u32 analogix_dp_get_lane3_link_training(struct analogix_dp_device *dp);
+void analogix_dp_set_lane_link_training(struct analogix_dp_device *dp);
+u32 analogix_dp_get_lane_link_training(struct analogix_dp_device *dp, u8 lane);
 void analogix_dp_reset_macro(struct analogix_dp_device *dp);
 void analogix_dp_init_video(struct analogix_dp_device *dp);
 
@@ -255,5 +247,14 @@ int analogix_dp_send_psr_spd(struct analogix_dp_device *dp,
 			     struct dp_sdp *vsc, bool blocking);
 ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
 			     struct drm_dp_aux_msg *msg);
+void analogix_dp_set_video_format(struct analogix_dp_device *dp);
+void analogix_dp_video_bist_enable(struct analogix_dp_device *dp);
+bool analogix_dp_ssc_supported(struct analogix_dp_device *dp);
+void analogix_dp_phy_power_on(struct analogix_dp_device *dp);
+void analogix_dp_phy_power_off(struct analogix_dp_device *dp);
+void analogix_dp_audio_config_spdif(struct analogix_dp_device *dp);
+void analogix_dp_audio_config_i2s(struct analogix_dp_device *dp);
+void analogix_dp_audio_enable(struct analogix_dp_device *dp);
+void analogix_dp_audio_disable(struct analogix_dp_device *dp);
 
 #endif /* _ANALOGIX_DP_CORE_H */
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
index cab3f5c4e..e76c66c7c 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
@@ -11,6 +11,7 @@
 #include <linux/gpio/consumer.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
+#include <linux/phy/phy.h>
 
 #include <drm/bridge/analogix_dp.h>
 
@@ -21,20 +22,37 @@
 #define COMMON_INT_MASK_2	0
 #define COMMON_INT_MASK_3	0
 #define COMMON_INT_MASK_4	(HOTPLUG_CHG | HPD_LOST | PLUG)
-#define INT_STA_MASK		INT_HPD
+
+static void analogix_dp_write(struct analogix_dp_device *dp, u32 reg, u32 val)
+{
+	if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) {
+		readl(dp->reg_base);
+		writel(val, dp->reg_base + reg);
+	}
+
+	writel(val, dp->reg_base + reg);
+}
+
+static u32 analogix_dp_read(struct analogix_dp_device *dp, u32 reg)
+{
+	if (dp->plat_data && is_rockchip(dp->plat_data->dev_type))
+		readl(dp->reg_base + reg);
+
+	return readl(dp->reg_base + reg);
+}
 
 void analogix_dp_enable_video_mute(struct analogix_dp_device *dp, bool enable)
 {
 	u32 reg;
 
 	if (enable) {
-		reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+		reg = analogix_dp_read(dp, ANALOGIX_DP_VIDEO_CTL_1);
 		reg |= HDCP_VIDEO_MUTE;
-		writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+		analogix_dp_write(dp, ANALOGIX_DP_VIDEO_CTL_1, reg);
 	} else {
-		reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+		reg = analogix_dp_read(dp, ANALOGIX_DP_VIDEO_CTL_1);
 		reg &= ~HDCP_VIDEO_MUTE;
-		writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+		analogix_dp_write(dp, ANALOGIX_DP_VIDEO_CTL_1, reg);
 	}
 }
 
@@ -42,9 +60,9 @@ void analogix_dp_stop_video(struct analogix_dp_device *dp)
 {
 	u32 reg;
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_VIDEO_CTL_1);
 	reg &= ~VIDEO_EN;
-	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+	analogix_dp_write(dp, ANALOGIX_DP_VIDEO_CTL_1, reg);
 }
 
 void analogix_dp_lane_swap(struct analogix_dp_device *dp, bool enable)
@@ -58,7 +76,7 @@ void analogix_dp_lane_swap(struct analogix_dp_device *dp, bool enable)
 		reg = LANE3_MAP_LOGIC_LANE_3 | LANE2_MAP_LOGIC_LANE_2 |
 		      LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0;
 
-	writel(reg, dp->reg_base + ANALOGIX_DP_LANE_MAP);
+	analogix_dp_write(dp, ANALOGIX_DP_LANE_MAP, reg);
 }
 
 void analogix_dp_init_analog_param(struct analogix_dp_device *dp)
@@ -66,53 +84,54 @@ void analogix_dp_init_analog_param(struct analogix_dp_device *dp)
 	u32 reg;
 
 	reg = TX_TERMINAL_CTRL_50_OHM;
-	writel(reg, dp->reg_base + ANALOGIX_DP_ANALOG_CTL_1);
+	analogix_dp_write(dp, ANALOGIX_DP_ANALOG_CTL_1, reg);
 
 	reg = SEL_24M | TX_DVDD_BIT_1_0625V;
-	writel(reg, dp->reg_base + ANALOGIX_DP_ANALOG_CTL_2);
+	analogix_dp_write(dp, ANALOGIX_DP_ANALOG_CTL_2, reg);
 
 	if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) {
 		reg = REF_CLK_24M;
 		if (dp->plat_data->dev_type == RK3288_DP)
 			reg ^= REF_CLK_MASK;
 
-		writel(reg, dp->reg_base + ANALOGIX_DP_PLL_REG_1);
-		writel(0x95, dp->reg_base + ANALOGIX_DP_PLL_REG_2);
-		writel(0x40, dp->reg_base + ANALOGIX_DP_PLL_REG_3);
-		writel(0x58, dp->reg_base + ANALOGIX_DP_PLL_REG_4);
-		writel(0x22, dp->reg_base + ANALOGIX_DP_PLL_REG_5);
+		analogix_dp_write(dp, ANALOGIX_DP_PLL_REG_1, reg);
+		analogix_dp_write(dp, ANALOGIX_DP_PLL_REG_2, 0x99);
+		analogix_dp_write(dp, ANALOGIX_DP_PLL_REG_3, 0x40);
+		analogix_dp_write(dp, ANALOGIX_DP_PLL_REG_4, 0x58);
+		analogix_dp_write(dp, ANALOGIX_DP_PLL_REG_5, 0x22);
+		analogix_dp_write(dp, ANALOGIX_DP_BIAS, 0x44);
 	}
 
 	reg = DRIVE_DVDD_BIT_1_0625V | VCO_BIT_600_MICRO;
-	writel(reg, dp->reg_base + ANALOGIX_DP_ANALOG_CTL_3);
+	analogix_dp_write(dp, ANALOGIX_DP_ANALOG_CTL_3, reg);
 
 	reg = PD_RING_OSC | AUX_TERMINAL_CTRL_50_OHM |
 		TX_CUR1_2X | TX_CUR_16_MA;
-	writel(reg, dp->reg_base + ANALOGIX_DP_PLL_FILTER_CTL_1);
+	analogix_dp_write(dp, ANALOGIX_DP_PLL_FILTER_CTL_1, reg);
 
 	reg = CH3_AMP_400_MV | CH2_AMP_400_MV |
 		CH1_AMP_400_MV | CH0_AMP_400_MV;
-	writel(reg, dp->reg_base + ANALOGIX_DP_TX_AMP_TUNING_CTL);
+	analogix_dp_write(dp, ANALOGIX_DP_TX_AMP_TUNING_CTL, reg);
 }
 
 void analogix_dp_init_interrupt(struct analogix_dp_device *dp)
 {
 	/* Set interrupt pin assertion polarity as high */
-	writel(INT_POL1 | INT_POL0, dp->reg_base + ANALOGIX_DP_INT_CTL);
+	analogix_dp_write(dp, ANALOGIX_DP_INT_CTL, INT_POL1 | INT_POL0);
 
 	/* Clear pending regisers */
-	writel(0xff, dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_1);
-	writel(0x4f, dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_2);
-	writel(0xe0, dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_3);
-	writel(0xe7, dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_4);
-	writel(0x63, dp->reg_base + ANALOGIX_DP_INT_STA);
+	analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_STA_1, 0xff);
+	analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_STA_2, 0x4f);
+	analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_STA_3, 0xe0);
+	analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_STA_4, 0xe7);
+	analogix_dp_write(dp, ANALOGIX_DP_INT_STA, 0x63);
 
 	/* 0:mask,1: unmask */
-	writel(0x00, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_1);
-	writel(0x00, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_2);
-	writel(0x00, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_3);
-	writel(0x00, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_4);
-	writel(0x00, dp->reg_base + ANALOGIX_DP_INT_STA_MASK);
+	analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_MASK_1, 0x00);
+	analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_MASK_2, 0x00);
+	analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_MASK_3, 0x00);
+	analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_MASK_4, 0x00);
+	analogix_dp_write(dp, ANALOGIX_DP_INT_STA_MASK, 0x00);
 }
 
 void analogix_dp_reset(struct analogix_dp_device *dp)
@@ -130,44 +149,44 @@ void analogix_dp_reset(struct analogix_dp_device *dp)
 			AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N |
 			HDCP_FUNC_EN_N | SW_FUNC_EN_N;
 
-	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
+	analogix_dp_write(dp, ANALOGIX_DP_FUNC_EN_1, reg);
 
 	reg = SSC_FUNC_EN_N | AUX_FUNC_EN_N |
 		SERDES_FIFO_FUNC_EN_N |
 		LS_CLK_DOMAIN_FUNC_EN_N;
-	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+	analogix_dp_write(dp, ANALOGIX_DP_FUNC_EN_2, reg);
 
 	usleep_range(20, 30);
 
 	analogix_dp_lane_swap(dp, 0);
 
-	writel(0x0, dp->reg_base + ANALOGIX_DP_SYS_CTL_1);
-	writel(0x40, dp->reg_base + ANALOGIX_DP_SYS_CTL_2);
-	writel(0x0, dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
-	writel(0x0, dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+	analogix_dp_write(dp, ANALOGIX_DP_SYS_CTL_1, 0x0);
+	analogix_dp_write(dp, ANALOGIX_DP_SYS_CTL_2, 0x40);
+	analogix_dp_write(dp, ANALOGIX_DP_SYS_CTL_3, 0x0);
+	analogix_dp_write(dp, ANALOGIX_DP_SYS_CTL_4, 0x0);
 
-	writel(0x0, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
-	writel(0x0, dp->reg_base + ANALOGIX_DP_HDCP_CTL);
+	analogix_dp_write(dp, ANALOGIX_DP_PKT_SEND_CTL, 0x0);
+	analogix_dp_write(dp, ANALOGIX_DP_HDCP_CTL, 0x0);
 
-	writel(0x5e, dp->reg_base + ANALOGIX_DP_HPD_DEGLITCH_L);
-	writel(0x1a, dp->reg_base + ANALOGIX_DP_HPD_DEGLITCH_H);
+	analogix_dp_write(dp, ANALOGIX_DP_HPD_DEGLITCH_L, 0x5e);
+	analogix_dp_write(dp, ANALOGIX_DP_HPD_DEGLITCH_H, 0x1a);
 
-	writel(0x10, dp->reg_base + ANALOGIX_DP_LINK_DEBUG_CTL);
+	analogix_dp_write(dp, ANALOGIX_DP_LINK_DEBUG_CTL, 0x10);
 
-	writel(0x0, dp->reg_base + ANALOGIX_DP_PHY_TEST);
+	analogix_dp_write(dp, ANALOGIX_DP_PHY_TEST, 0x0);
 
-	writel(0x0, dp->reg_base + ANALOGIX_DP_VIDEO_FIFO_THRD);
-	writel(0x20, dp->reg_base + ANALOGIX_DP_AUDIO_MARGIN);
+	analogix_dp_write(dp, ANALOGIX_DP_VIDEO_FIFO_THRD, 0x0);
+	analogix_dp_write(dp, ANALOGIX_DP_AUDIO_MARGIN, 0x20);
 
-	writel(0x4, dp->reg_base + ANALOGIX_DP_M_VID_GEN_FILTER_TH);
-	writel(0x2, dp->reg_base + ANALOGIX_DP_M_AUD_GEN_FILTER_TH);
+	analogix_dp_write(dp, ANALOGIX_DP_M_VID_GEN_FILTER_TH, 0x4);
+	analogix_dp_write(dp, ANALOGIX_DP_M_AUD_GEN_FILTER_TH, 0x2);
 
-	writel(0x00000101, dp->reg_base + ANALOGIX_DP_SOC_GENERAL_CTL);
+	analogix_dp_write(dp, ANALOGIX_DP_SOC_GENERAL_CTL, 0x00000101);
 }
 
 void analogix_dp_swreset(struct analogix_dp_device *dp)
 {
-	writel(RESET_DP_TX, dp->reg_base + ANALOGIX_DP_TX_SW_RESET);
+	analogix_dp_write(dp, ANALOGIX_DP_TX_SW_RESET, RESET_DP_TX);
 }
 
 void analogix_dp_config_interrupt(struct analogix_dp_device *dp)
@@ -176,19 +195,18 @@ void analogix_dp_config_interrupt(struct analogix_dp_device *dp)
 
 	/* 0: mask, 1: unmask */
 	reg = COMMON_INT_MASK_1;
-	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_1);
+	analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_MASK_1, reg);
 
 	reg = COMMON_INT_MASK_2;
-	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_2);
+	analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_MASK_2, reg);
 
 	reg = COMMON_INT_MASK_3;
-	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_3);
+	analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_MASK_3, reg);
 
-	reg = COMMON_INT_MASK_4;
-	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_4);
-
-	reg = INT_STA_MASK;
-	writel(reg, dp->reg_base + ANALOGIX_DP_INT_STA_MASK);
+	if (dp->force_hpd || dp->hpd_gpiod)
+		analogix_dp_mute_hpd_interrupt(dp);
+	else
+		analogix_dp_unmute_hpd_interrupt(dp);
 }
 
 void analogix_dp_mute_hpd_interrupt(struct analogix_dp_device *dp)
@@ -196,13 +214,13 @@ void analogix_dp_mute_hpd_interrupt(struct analogix_dp_device *dp)
 	u32 reg;
 
 	/* 0: mask, 1: unmask */
-	reg = readl(dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_4);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_COMMON_INT_MASK_4);
 	reg &= ~COMMON_INT_MASK_4;
-	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_4);
+	analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_MASK_4, reg);
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA_MASK);
-	reg &= ~INT_STA_MASK;
-	writel(reg, dp->reg_base + ANALOGIX_DP_INT_STA_MASK);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_INT_STA_MASK);
+	reg &= ~INT_HPD;
+	analogix_dp_write(dp, ANALOGIX_DP_INT_STA_MASK, reg);
 }
 
 void analogix_dp_unmute_hpd_interrupt(struct analogix_dp_device *dp)
@@ -211,17 +229,18 @@ void analogix_dp_unmute_hpd_interrupt(struct analogix_dp_device *dp)
 
 	/* 0: mask, 1: unmask */
 	reg = COMMON_INT_MASK_4;
-	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_MASK_4);
+	analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_MASK_4, reg);
 
-	reg = INT_STA_MASK;
-	writel(reg, dp->reg_base + ANALOGIX_DP_INT_STA_MASK);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_INT_STA_MASK);
+	reg |= INT_HPD;
+	analogix_dp_write(dp, ANALOGIX_DP_INT_STA_MASK, reg);
 }
 
 enum pll_status analogix_dp_get_pll_lock_status(struct analogix_dp_device *dp)
 {
 	u32 reg;
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_DEBUG_CTL);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_DEBUG_CTL);
 	if (reg & PLL_LOCK)
 		return PLL_LOCKED;
 	else
@@ -239,12 +258,12 @@ void analogix_dp_set_pll_power_down(struct analogix_dp_device *dp, bool enable)
 		mask = RK_PLL_PD;
 	}
 
-	reg = readl(dp->reg_base + pd_addr);
+	reg = analogix_dp_read(dp, pd_addr);
 	if (enable)
 		reg |= mask;
 	else
 		reg &= ~mask;
-	writel(reg, dp->reg_base + pd_addr);
+	analogix_dp_write(dp, pd_addr, reg);
 }
 
 void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
@@ -265,52 +284,54 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
 		else
 			mask = AUX_PD;
 
-		reg = readl(dp->reg_base + phy_pd_addr);
-		if (enable)
+		reg = analogix_dp_read(dp, phy_pd_addr);
+		if (enable) {
+			reg &= ~(DP_INC_BG | DP_EXP_BG);
 			reg |= mask;
-		else
+		} else {
 			reg &= ~mask;
-		writel(reg, dp->reg_base + phy_pd_addr);
+		}
+		analogix_dp_write(dp, phy_pd_addr, reg);
 		break;
 	case CH0_BLOCK:
 		mask = CH0_PD;
-		reg = readl(dp->reg_base + phy_pd_addr);
+		reg = analogix_dp_read(dp, phy_pd_addr);
 
 		if (enable)
 			reg |= mask;
 		else
 			reg &= ~mask;
-		writel(reg, dp->reg_base + phy_pd_addr);
+		analogix_dp_write(dp, phy_pd_addr, reg);
 		break;
 	case CH1_BLOCK:
 		mask = CH1_PD;
-		reg = readl(dp->reg_base + phy_pd_addr);
+		reg = analogix_dp_read(dp, phy_pd_addr);
 
 		if (enable)
 			reg |= mask;
 		else
 			reg &= ~mask;
-		writel(reg, dp->reg_base + phy_pd_addr);
+		analogix_dp_write(dp, phy_pd_addr, reg);
 		break;
 	case CH2_BLOCK:
 		mask = CH2_PD;
-		reg = readl(dp->reg_base + phy_pd_addr);
+		reg = analogix_dp_read(dp, phy_pd_addr);
 
 		if (enable)
 			reg |= mask;
 		else
 			reg &= ~mask;
-		writel(reg, dp->reg_base + phy_pd_addr);
+		analogix_dp_write(dp, phy_pd_addr, reg);
 		break;
 	case CH3_BLOCK:
 		mask = CH3_PD;
-		reg = readl(dp->reg_base + phy_pd_addr);
+		reg = analogix_dp_read(dp, phy_pd_addr);
 
 		if (enable)
 			reg |= mask;
 		else
 			reg &= ~mask;
-		writel(reg, dp->reg_base + phy_pd_addr);
+		analogix_dp_write(dp, phy_pd_addr, reg);
 		break;
 	case ANALOG_TOTAL:
 		/*
@@ -323,29 +344,29 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
 		else
 			mask = DP_PHY_PD;
 
-		reg = readl(dp->reg_base + phy_pd_addr);
+		reg = analogix_dp_read(dp, phy_pd_addr);
 		if (enable)
 			reg |= mask;
 		else
 			reg &= ~mask;
 
-		writel(reg, dp->reg_base + phy_pd_addr);
+		analogix_dp_write(dp, phy_pd_addr, reg);
 		if (dp->plat_data && is_rockchip(dp->plat_data->dev_type))
 			usleep_range(10, 15);
 		break;
 	case POWER_ALL:
 		if (enable) {
 			reg = DP_ALL_PD;
-			writel(reg, dp->reg_base + phy_pd_addr);
+			analogix_dp_write(dp, phy_pd_addr, reg);
 		} else {
 			reg = DP_ALL_PD;
-			writel(reg, dp->reg_base + phy_pd_addr);
+			analogix_dp_write(dp, phy_pd_addr, reg);
 			usleep_range(10, 15);
 			reg &= ~DP_INC_BG;
-			writel(reg, dp->reg_base + phy_pd_addr);
+			analogix_dp_write(dp, phy_pd_addr, reg);
 			usleep_range(10, 15);
 
-			writel(0x00, dp->reg_base + phy_pd_addr);
+			analogix_dp_write(dp, phy_pd_addr, 0x00);
 		}
 		break;
 	default:
@@ -356,36 +377,24 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp,
 int analogix_dp_init_analog_func(struct analogix_dp_device *dp)
 {
 	u32 reg;
-	int timeout_loop = 0;
 
 	analogix_dp_set_analog_power_down(dp, POWER_ALL, 0);
 
 	reg = PLL_LOCK_CHG;
-	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_1);
+	analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_STA_1, reg);
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_DEBUG_CTL);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_DEBUG_CTL);
 	reg &= ~(F_PLL_LOCK | PLL_LOCK_CTRL);
-	writel(reg, dp->reg_base + ANALOGIX_DP_DEBUG_CTL);
+	analogix_dp_write(dp, ANALOGIX_DP_DEBUG_CTL, reg);
 
 	/* Power up PLL */
-	if (analogix_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
-		analogix_dp_set_pll_power_down(dp, 0);
-
-		while (analogix_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
-			timeout_loop++;
-			if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
-				dev_err(dp->dev, "failed to get pll lock status\n");
-				return -ETIMEDOUT;
-			}
-			usleep_range(10, 20);
-		}
-	}
+	analogix_dp_set_pll_power_down(dp, 0);
 
 	/* Enable Serdes FIFO function and Link symbol clock domain module */
-	reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_FUNC_EN_2);
 	reg &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N
 		| AUX_FUNC_EN_N);
-	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+	analogix_dp_write(dp, ANALOGIX_DP_FUNC_EN_2, reg);
 	return 0;
 }
 
@@ -397,10 +406,10 @@ void analogix_dp_clear_hotplug_interrupts(struct analogix_dp_device *dp)
 		return;
 
 	reg = HOTPLUG_CHG | HPD_LOST | PLUG;
-	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_4);
+	analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_STA_4, reg);
 
 	reg = INT_HPD;
-	writel(reg, dp->reg_base + ANALOGIX_DP_INT_STA);
+	analogix_dp_write(dp, ANALOGIX_DP_INT_STA, reg);
 }
 
 void analogix_dp_init_hpd(struct analogix_dp_device *dp)
@@ -412,45 +421,37 @@ void analogix_dp_init_hpd(struct analogix_dp_device *dp)
 
 	analogix_dp_clear_hotplug_interrupts(dp);
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_SYS_CTL_3);
 	reg &= ~(F_HPD | HPD_CTRL);
-	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+	analogix_dp_write(dp, ANALOGIX_DP_SYS_CTL_3, reg);
 }
 
 void analogix_dp_force_hpd(struct analogix_dp_device *dp)
 {
 	u32 reg;
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
-	reg = (F_HPD | HPD_CTRL);
-	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_SYS_CTL_3);
+	reg |= (F_HPD | HPD_CTRL);
+	analogix_dp_write(dp, ANALOGIX_DP_SYS_CTL_3, reg);
 }
 
 enum dp_irq_type analogix_dp_get_irq_type(struct analogix_dp_device *dp)
 {
 	u32 reg;
 
-	if (dp->hpd_gpiod) {
-		reg = gpiod_get_value(dp->hpd_gpiod);
-		if (reg)
-			return DP_IRQ_TYPE_HP_CABLE_IN;
-		else
-			return DP_IRQ_TYPE_HP_CABLE_OUT;
-	} else {
-		/* Parse hotplug interrupt status register */
-		reg = readl(dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_4);
+	/* Parse hotplug interrupt status register */
+	reg = analogix_dp_read(dp, ANALOGIX_DP_COMMON_INT_STA_4);
 
-		if (reg & PLUG)
-			return DP_IRQ_TYPE_HP_CABLE_IN;
+	if (reg & PLUG)
+		return DP_IRQ_TYPE_HP_CABLE_IN;
 
-		if (reg & HPD_LOST)
-			return DP_IRQ_TYPE_HP_CABLE_OUT;
+	if (reg & HPD_LOST)
+		return DP_IRQ_TYPE_HP_CABLE_OUT;
 
-		if (reg & HOTPLUG_CHG)
-			return DP_IRQ_TYPE_HP_CHANGE;
+	if (reg & HOTPLUG_CHG)
+		return DP_IRQ_TYPE_HP_CHANGE;
 
-		return DP_IRQ_TYPE_UNKNOWN;
-	}
+	return DP_IRQ_TYPE_UNKNOWN;
 }
 
 void analogix_dp_reset_aux(struct analogix_dp_device *dp)
@@ -458,9 +459,9 @@ void analogix_dp_reset_aux(struct analogix_dp_device *dp)
 	u32 reg;
 
 	/* Disable AUX channel module */
-	reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_FUNC_EN_2);
 	reg |= AUX_FUNC_EN_N;
-	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+	analogix_dp_write(dp, ANALOGIX_DP_FUNC_EN_2, reg);
 }
 
 void analogix_dp_init_aux(struct analogix_dp_device *dp)
@@ -469,7 +470,7 @@ void analogix_dp_init_aux(struct analogix_dp_device *dp)
 
 	/* Clear inerrupts related to AUX channel */
 	reg = RPLY_RECEIV | AUX_ERR;
-	writel(reg, dp->reg_base + ANALOGIX_DP_INT_STA);
+	analogix_dp_write(dp, ANALOGIX_DP_INT_STA, reg);
 
 	analogix_dp_set_analog_power_down(dp, AUX_BLOCK, true);
 	usleep_range(10, 11);
@@ -487,16 +488,17 @@ void analogix_dp_init_aux(struct analogix_dp_device *dp)
 	reg |= AUX_HW_RETRY_COUNT_SEL(0) |
 	       AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
 
-	writel(reg, dp->reg_base + ANALOGIX_DP_AUX_HW_RETRY_CTL);
+	analogix_dp_write(dp, ANALOGIX_DP_AUX_HW_RETRY_CTL, reg);
 
 	/* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
 	reg = DEFER_CTRL_EN | DEFER_COUNT(1);
-	writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_DEFER_CTL);
+	analogix_dp_write(dp, ANALOGIX_DP_AUX_CH_DEFER_CTL, reg);
 
 	/* Enable AUX channel module */
-	reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+	analogix_dp_enable_sw_function(dp);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_FUNC_EN_2);
 	reg &= ~AUX_FUNC_EN_N;
-	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+	analogix_dp_write(dp, ANALOGIX_DP_FUNC_EN_2, reg);
 }
 
 int analogix_dp_get_plug_in_status(struct analogix_dp_device *dp)
@@ -507,7 +509,7 @@ int analogix_dp_get_plug_in_status(struct analogix_dp_device *dp)
 		if (gpiod_get_value(dp->hpd_gpiod))
 			return 0;
 	} else {
-		reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+		reg = analogix_dp_read(dp, ANALOGIX_DP_SYS_CTL_3);
 		if (reg & HPD_STATUS)
 			return 0;
 	}
@@ -519,145 +521,181 @@ void analogix_dp_enable_sw_function(struct analogix_dp_device *dp)
 {
 	u32 reg;
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_FUNC_EN_1);
 	reg &= ~SW_FUNC_EN_N;
-	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
+	analogix_dp_write(dp, ANALOGIX_DP_FUNC_EN_1, reg);
 }
 
-int analogix_dp_start_aux_transaction(struct analogix_dp_device *dp)
+static void analogix_dp_ssc_enable(struct analogix_dp_device *dp)
 {
-	int reg;
-	int retval = 0;
-	int timeout_loop = 0;
-
-	/* Enable AUX CH operation */
-	reg = readl(dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2);
-	reg |= AUX_EN;
-	writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2);
-
-	/* Is AUX CH command reply received? */
-	reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
-	while (!(reg & RPLY_RECEIV)) {
-		timeout_loop++;
-		if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
-			dev_err(dp->dev, "AUX CH command reply failed!\n");
-			return -ETIMEDOUT;
-		}
-		reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
-		usleep_range(10, 11);
-	}
-
-	/* Clear interrupt source for AUX CH command reply */
-	writel(RPLY_RECEIV, dp->reg_base + ANALOGIX_DP_INT_STA);
-
-	/* Clear interrupt source for AUX CH access error */
-	reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
-	if (reg & AUX_ERR) {
-		writel(AUX_ERR, dp->reg_base + ANALOGIX_DP_INT_STA);
-		return -EREMOTEIO;
-	}
-
-	/* Check AUX CH error access status */
-	reg = readl(dp->reg_base + ANALOGIX_DP_AUX_CH_STA);
-	if ((reg & AUX_STATUS_MASK) != 0) {
-		dev_err(dp->dev, "AUX CH error happens: %d\n\n",
-			reg & AUX_STATUS_MASK);
-		return -EREMOTEIO;
-	}
+	u32 reg;
 
-	return retval;
+	/* 4500ppm */
+	writel(0x19, dp->reg_base + ANALOIGX_DP_SSC_REG);
+	/*
+	 * To apply updated SSC parameters into SSC operation,
+	 * firmware must disable and enable this bit.
+	 */
+	reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+	reg |= SSC_FUNC_EN_N;
+	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+	reg &= ~SSC_FUNC_EN_N;
+	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
 }
 
-int analogix_dp_write_byte_to_dpcd(struct analogix_dp_device *dp,
-				   unsigned int reg_addr,
-				   unsigned char data)
+static void analogix_dp_ssc_disable(struct analogix_dp_device *dp)
 {
 	u32 reg;
-	int i;
-	int retval;
-
-	for (i = 0; i < 3; i++) {
-		/* Clear AUX CH data buffer */
-		reg = BUF_CLR;
-		writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
-
-		/* Select DPCD device address */
-		reg = AUX_ADDR_7_0(reg_addr);
-		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0);
-		reg = AUX_ADDR_15_8(reg_addr);
-		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8);
-		reg = AUX_ADDR_19_16(reg_addr);
-		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16);
-
-		/* Write data buffer */
-		reg = (unsigned int)data;
-		writel(reg, dp->reg_base + ANALOGIX_DP_BUF_DATA_0);
 
-		/*
-		 * Set DisplayPort transaction and write 1 byte
-		 * If bit 3 is 1, DisplayPort transaction.
-		 * If Bit 3 is 0, I2C transaction.
-		 */
-		reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
-		writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1);
-
-		/* Start AUX transaction */
-		retval = analogix_dp_start_aux_transaction(dp);
-		if (retval == 0)
-			break;
-
-		dev_dbg(dp->dev, "%s: Aux Transaction fail!\n", __func__);
-	}
+	reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+	reg |= SSC_FUNC_EN_N;
+	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_2);
+}
 
-	return retval;
+bool analogix_dp_ssc_supported(struct analogix_dp_device *dp)
+{
+	/* Check if SSC is supported by both sides */
+	return dp->plat_data->ssc && dp->link_train.ssc;
 }
 
 void analogix_dp_set_link_bandwidth(struct analogix_dp_device *dp, u32 bwtype)
 {
-	u32 reg;
+	u32 status;
+	int ret;
 
-	reg = bwtype;
-	if ((bwtype == DP_LINK_BW_2_7) || (bwtype == DP_LINK_BW_1_62))
-		writel(reg, dp->reg_base + ANALOGIX_DP_LINK_BW_SET);
+	analogix_dp_write(dp, ANALOGIX_DP_LINK_BW_SET, bwtype);
+
+	if (dp->phy) {
+		union phy_configure_opts phy_cfg = {0};
+
+		phy_cfg.dp.lanes = dp->link_train.lane_count;
+		phy_cfg.dp.link_rate =
+			drm_dp_bw_code_to_link_rate(dp->link_train.link_rate) / 100;
+		phy_cfg.dp.ssc = analogix_dp_ssc_supported(dp);
+		phy_cfg.dp.set_lanes = false;
+		phy_cfg.dp.set_rate = true;
+		phy_cfg.dp.set_voltages = false;
+		ret = phy_configure(dp->phy, &phy_cfg);
+		if (ret && ret != -EOPNOTSUPP) {
+			dev_err(dp->dev, "%s: phy_configure failed: %d\n",
+				__func__, ret);
+			return;
+		}
+	} else {
+		if (analogix_dp_ssc_supported(dp))
+			analogix_dp_ssc_enable(dp);
+		else
+			analogix_dp_ssc_disable(dp);
+	}
+
+	ret = readx_poll_timeout(analogix_dp_get_pll_lock_status, dp, status,
+				 status != PLL_UNLOCKED, 120,
+				 120 * DP_TIMEOUT_LOOP_COUNT);
+	if (ret) {
+		dev_err(dp->dev, "Wait for pll lock failed %d\n", ret);
+		return;
+	}
 }
 
 void analogix_dp_get_link_bandwidth(struct analogix_dp_device *dp, u32 *bwtype)
 {
 	u32 reg;
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_LINK_BW_SET);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_LINK_BW_SET);
 	*bwtype = reg;
 }
 
 void analogix_dp_set_lane_count(struct analogix_dp_device *dp, u32 count)
 {
 	u32 reg;
+	int ret;
 
 	reg = count;
-	writel(reg, dp->reg_base + ANALOGIX_DP_LANE_COUNT_SET);
+	analogix_dp_write(dp, ANALOGIX_DP_LANE_COUNT_SET, reg);
+
+	if (dp->phy) {
+		union phy_configure_opts phy_cfg = {0};
+
+		phy_cfg.dp.lanes = dp->link_train.lane_count;
+		phy_cfg.dp.set_lanes = true;
+		phy_cfg.dp.set_rate = false;
+		phy_cfg.dp.set_voltages = false;
+		ret = phy_configure(dp->phy, &phy_cfg);
+		if (ret && ret != -EOPNOTSUPP) {
+			dev_err(dp->dev, "%s: phy_configure() failed: %d\n",
+				__func__, ret);
+			return;
+		}
+	}
 }
 
 void analogix_dp_get_lane_count(struct analogix_dp_device *dp, u32 *count)
 {
 	u32 reg;
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_LANE_COUNT_SET);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_LANE_COUNT_SET);
 	*count = reg;
 }
 
+void analogix_dp_set_lane_link_training(struct analogix_dp_device *dp)
+{
+	u8 lane;
+	int ret;
+
+	for (lane = 0; lane < dp->link_train.lane_count; lane++)
+		analogix_dp_write(dp,
+				  ANALOGIX_DP_LN0_LINK_TRAINING_CTL + 4 * lane,
+				  dp->link_train.training_lane[lane]);
+
+	if (dp->phy) {
+		union phy_configure_opts phy_cfg = {0};
+
+		for (lane = 0; lane < dp->link_train.lane_count; lane++) {
+			u8 training_lane = dp->link_train.training_lane[lane];
+			u8 vs, pe;
+
+			vs = (training_lane & DP_TRAIN_VOLTAGE_SWING_MASK) >>
+			     DP_TRAIN_VOLTAGE_SWING_SHIFT;
+			pe = (training_lane & DP_TRAIN_PRE_EMPHASIS_MASK) >>
+			     DP_TRAIN_PRE_EMPHASIS_SHIFT;
+			phy_cfg.dp.voltage[lane] = vs;
+			phy_cfg.dp.pre[lane] = pe;
+		}
+
+		phy_cfg.dp.lanes = dp->link_train.lane_count;
+		phy_cfg.dp.link_rate =
+			drm_dp_bw_code_to_link_rate(dp->link_train.link_rate) / 100;
+		phy_cfg.dp.set_lanes = false;
+		phy_cfg.dp.set_rate = false;
+		phy_cfg.dp.set_voltages = true;
+		ret = phy_configure(dp->phy, &phy_cfg);
+		if (ret && ret != -EOPNOTSUPP) {
+			dev_err(dp->dev, "%s: phy_configure() failed: %d\n",
+				__func__, ret);
+			return;
+		}
+	}
+}
+
+u32 analogix_dp_get_lane_link_training(struct analogix_dp_device *dp, u8 lane)
+{
+	return analogix_dp_read(dp,
+				ANALOGIX_DP_LN0_LINK_TRAINING_CTL + 4 * lane);
+}
+
 void analogix_dp_enable_enhanced_mode(struct analogix_dp_device *dp,
 				      bool enable)
 {
 	u32 reg;
 
 	if (enable) {
-		reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+		reg = analogix_dp_read(dp, ANALOGIX_DP_SYS_CTL_4);
 		reg |= ENHANCED;
-		writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+		analogix_dp_write(dp, ANALOGIX_DP_SYS_CTL_4, reg);
 	} else {
-		reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+		reg = analogix_dp_read(dp, ANALOGIX_DP_SYS_CTL_4);
 		reg &= ~ENHANCED;
-		writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+		analogix_dp_write(dp, ANALOGIX_DP_SYS_CTL_4, reg);
 	}
 }
 
@@ -669,144 +707,48 @@ void analogix_dp_set_training_pattern(struct analogix_dp_device *dp,
 	switch (pattern) {
 	case PRBS7:
 		reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_PRBS7;
-		writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+		analogix_dp_write(dp, ANALOGIX_DP_TRAINING_PTN_SET, reg);
 		break;
 	case D10_2:
 		reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_D10_2;
-		writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+		analogix_dp_write(dp, ANALOGIX_DP_TRAINING_PTN_SET, reg);
 		break;
 	case TRAINING_PTN1:
 		reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1;
-		writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+		analogix_dp_write(dp, ANALOGIX_DP_TRAINING_PTN_SET, reg);
 		break;
 	case TRAINING_PTN2:
 		reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2;
-		writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+		analogix_dp_write(dp, ANALOGIX_DP_TRAINING_PTN_SET, reg);
+		break;
+	case TRAINING_PTN3:
+		reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN3;
+		analogix_dp_write(dp, ANALOGIX_DP_TRAINING_PTN_SET, reg);
 		break;
 	case DP_NONE:
 		reg = SCRAMBLING_ENABLE |
 			LINK_QUAL_PATTERN_SET_DISABLE |
 			SW_TRAINING_PATTERN_SET_NORMAL;
-		writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+		analogix_dp_write(dp, ANALOGIX_DP_TRAINING_PTN_SET, reg);
 		break;
 	default:
 		break;
 	}
 }
 
-void analogix_dp_set_lane0_pre_emphasis(struct analogix_dp_device *dp,
-					u32 level)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL);
-	reg &= ~PRE_EMPHASIS_SET_MASK;
-	reg |= level << PRE_EMPHASIS_SET_SHIFT;
-	writel(reg, dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL);
-}
-
-void analogix_dp_set_lane1_pre_emphasis(struct analogix_dp_device *dp,
-					u32 level)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + ANALOGIX_DP_LN1_LINK_TRAINING_CTL);
-	reg &= ~PRE_EMPHASIS_SET_MASK;
-	reg |= level << PRE_EMPHASIS_SET_SHIFT;
-	writel(reg, dp->reg_base + ANALOGIX_DP_LN1_LINK_TRAINING_CTL);
-}
-
-void analogix_dp_set_lane2_pre_emphasis(struct analogix_dp_device *dp,
-					u32 level)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + ANALOGIX_DP_LN2_LINK_TRAINING_CTL);
-	reg &= ~PRE_EMPHASIS_SET_MASK;
-	reg |= level << PRE_EMPHASIS_SET_SHIFT;
-	writel(reg, dp->reg_base + ANALOGIX_DP_LN2_LINK_TRAINING_CTL);
-}
-
-void analogix_dp_set_lane3_pre_emphasis(struct analogix_dp_device *dp,
-					u32 level)
-{
-	u32 reg;
-
-	reg = readl(dp->reg_base + ANALOGIX_DP_LN3_LINK_TRAINING_CTL);
-	reg &= ~PRE_EMPHASIS_SET_MASK;
-	reg |= level << PRE_EMPHASIS_SET_SHIFT;
-	writel(reg, dp->reg_base + ANALOGIX_DP_LN3_LINK_TRAINING_CTL);
-}
-
-void analogix_dp_set_lane0_link_training(struct analogix_dp_device *dp,
-					 u32 training_lane)
-{
-	u32 reg;
-
-	reg = training_lane;
-	writel(reg, dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL);
-}
-
-void analogix_dp_set_lane1_link_training(struct analogix_dp_device *dp,
-					 u32 training_lane)
-{
-	u32 reg;
-
-	reg = training_lane;
-	writel(reg, dp->reg_base + ANALOGIX_DP_LN1_LINK_TRAINING_CTL);
-}
-
-void analogix_dp_set_lane2_link_training(struct analogix_dp_device *dp,
-					 u32 training_lane)
-{
-	u32 reg;
-
-	reg = training_lane;
-	writel(reg, dp->reg_base + ANALOGIX_DP_LN2_LINK_TRAINING_CTL);
-}
-
-void analogix_dp_set_lane3_link_training(struct analogix_dp_device *dp,
-					 u32 training_lane)
-{
-	u32 reg;
-
-	reg = training_lane;
-	writel(reg, dp->reg_base + ANALOGIX_DP_LN3_LINK_TRAINING_CTL);
-}
-
-u32 analogix_dp_get_lane0_link_training(struct analogix_dp_device *dp)
-{
-	return readl(dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL);
-}
-
-u32 analogix_dp_get_lane1_link_training(struct analogix_dp_device *dp)
-{
-	return readl(dp->reg_base + ANALOGIX_DP_LN1_LINK_TRAINING_CTL);
-}
-
-u32 analogix_dp_get_lane2_link_training(struct analogix_dp_device *dp)
-{
-	return readl(dp->reg_base + ANALOGIX_DP_LN2_LINK_TRAINING_CTL);
-}
-
-u32 analogix_dp_get_lane3_link_training(struct analogix_dp_device *dp)
-{
-	return readl(dp->reg_base + ANALOGIX_DP_LN3_LINK_TRAINING_CTL);
-}
-
 void analogix_dp_reset_macro(struct analogix_dp_device *dp)
 {
 	u32 reg;
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_PHY_TEST);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_PHY_TEST);
 	reg |= MACRO_RST;
-	writel(reg, dp->reg_base + ANALOGIX_DP_PHY_TEST);
+	analogix_dp_write(dp, ANALOGIX_DP_PHY_TEST, reg);
 
 	/* 10 us is the minimum reset time. */
 	usleep_range(10, 20);
 
 	reg &= ~MACRO_RST;
-	writel(reg, dp->reg_base + ANALOGIX_DP_PHY_TEST);
+	analogix_dp_write(dp, ANALOGIX_DP_PHY_TEST, reg);
 }
 
 void analogix_dp_init_video(struct analogix_dp_device *dp)
@@ -814,19 +756,19 @@ void analogix_dp_init_video(struct analogix_dp_device *dp)
 	u32 reg;
 
 	reg = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG;
-	writel(reg, dp->reg_base + ANALOGIX_DP_COMMON_INT_STA_1);
+	analogix_dp_write(dp, ANALOGIX_DP_COMMON_INT_STA_1, reg);
 
 	reg = 0x0;
-	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_1);
+	analogix_dp_write(dp, ANALOGIX_DP_SYS_CTL_1, reg);
 
 	reg = CHA_CRI(4) | CHA_CTRL;
-	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_2);
+	analogix_dp_write(dp, ANALOGIX_DP_SYS_CTL_2, reg);
 
 	reg = 0x0;
-	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+	analogix_dp_write(dp, ANALOGIX_DP_SYS_CTL_3, reg);
 
 	reg = VID_HRES_TH(2) | VID_VRES_TH(0);
-	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_8);
+	analogix_dp_write(dp, ANALOGIX_DP_VIDEO_CTL_8, reg);
 }
 
 void analogix_dp_set_video_color_format(struct analogix_dp_device *dp)
@@ -837,36 +779,36 @@ void analogix_dp_set_video_color_format(struct analogix_dp_device *dp)
 	reg = (dp->video_info.dynamic_range << IN_D_RANGE_SHIFT) |
 		(dp->video_info.color_depth << IN_BPC_SHIFT) |
 		(dp->video_info.color_space << IN_COLOR_F_SHIFT);
-	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_2);
+	analogix_dp_write(dp, ANALOGIX_DP_VIDEO_CTL_2, reg);
 
 	/* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */
-	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_VIDEO_CTL_3);
 	reg &= ~IN_YC_COEFFI_MASK;
 	if (dp->video_info.ycbcr_coeff)
 		reg |= IN_YC_COEFFI_ITU709;
 	else
 		reg |= IN_YC_COEFFI_ITU601;
-	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+	analogix_dp_write(dp, ANALOGIX_DP_VIDEO_CTL_3, reg);
 }
 
 int analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp)
 {
 	u32 reg;
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_1);
-	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_1);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_SYS_CTL_1);
+	analogix_dp_write(dp, ANALOGIX_DP_SYS_CTL_1, reg);
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_1);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_SYS_CTL_1);
 
 	if (!(reg & DET_STA)) {
 		dev_dbg(dp->dev, "Input stream clock not detected.\n");
 		return -EINVAL;
 	}
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_2);
-	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_2);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_SYS_CTL_2);
+	analogix_dp_write(dp, ANALOGIX_DP_SYS_CTL_2, reg);
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_2);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_SYS_CTL_2);
 	dev_dbg(dp->dev, "wait SYS_CTL_2.\n");
 
 	if (reg & CHA_STA) {
@@ -884,30 +826,30 @@ void analogix_dp_set_video_cr_mn(struct analogix_dp_device *dp,
 	u32 reg;
 
 	if (type == REGISTER_M) {
-		reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+		reg = analogix_dp_read(dp, ANALOGIX_DP_SYS_CTL_4);
 		reg |= FIX_M_VID;
-		writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+		analogix_dp_write(dp, ANALOGIX_DP_SYS_CTL_4, reg);
 		reg = m_value & 0xff;
-		writel(reg, dp->reg_base + ANALOGIX_DP_M_VID_0);
+		analogix_dp_write(dp, ANALOGIX_DP_M_VID_0, reg);
 		reg = (m_value >> 8) & 0xff;
-		writel(reg, dp->reg_base + ANALOGIX_DP_M_VID_1);
+		analogix_dp_write(dp, ANALOGIX_DP_M_VID_1, reg);
 		reg = (m_value >> 16) & 0xff;
-		writel(reg, dp->reg_base + ANALOGIX_DP_M_VID_2);
+		analogix_dp_write(dp, ANALOGIX_DP_M_VID_2, reg);
 
 		reg = n_value & 0xff;
-		writel(reg, dp->reg_base + ANALOGIX_DP_N_VID_0);
+		analogix_dp_write(dp, ANALOGIX_DP_N_VID_0, reg);
 		reg = (n_value >> 8) & 0xff;
-		writel(reg, dp->reg_base + ANALOGIX_DP_N_VID_1);
+		analogix_dp_write(dp, ANALOGIX_DP_N_VID_1, reg);
 		reg = (n_value >> 16) & 0xff;
-		writel(reg, dp->reg_base + ANALOGIX_DP_N_VID_2);
+		analogix_dp_write(dp, ANALOGIX_DP_N_VID_2, reg);
 	} else  {
-		reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+		reg = analogix_dp_read(dp, ANALOGIX_DP_SYS_CTL_4);
 		reg &= ~FIX_M_VID;
-		writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_4);
+		analogix_dp_write(dp, ANALOGIX_DP_SYS_CTL_4, reg);
 
-		writel(0x00, dp->reg_base + ANALOGIX_DP_N_VID_0);
-		writel(0x80, dp->reg_base + ANALOGIX_DP_N_VID_1);
-		writel(0x00, dp->reg_base + ANALOGIX_DP_N_VID_2);
+		analogix_dp_write(dp, ANALOGIX_DP_N_VID_0, 0x00);
+		analogix_dp_write(dp, ANALOGIX_DP_N_VID_1, 0x80);
+		analogix_dp_write(dp, ANALOGIX_DP_N_VID_2, 0x00);
 	}
 }
 
@@ -916,13 +858,13 @@ void analogix_dp_set_video_timing_mode(struct analogix_dp_device *dp, u32 type)
 	u32 reg;
 
 	if (type == VIDEO_TIMING_FROM_CAPTURE) {
-		reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+		reg = analogix_dp_read(dp, ANALOGIX_DP_VIDEO_CTL_10);
 		reg &= ~FORMAT_SEL;
-		writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+		analogix_dp_write(dp, ANALOGIX_DP_VIDEO_CTL_10, reg);
 	} else {
-		reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+		reg = analogix_dp_read(dp, ANALOGIX_DP_VIDEO_CTL_10);
 		reg |= FORMAT_SEL;
-		writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+		analogix_dp_write(dp, ANALOGIX_DP_VIDEO_CTL_10, reg);
 	}
 }
 
@@ -931,15 +873,15 @@ void analogix_dp_enable_video_master(struct analogix_dp_device *dp, bool enable)
 	u32 reg;
 
 	if (enable) {
-		reg = readl(dp->reg_base + ANALOGIX_DP_SOC_GENERAL_CTL);
+		reg = analogix_dp_read(dp, ANALOGIX_DP_SOC_GENERAL_CTL);
 		reg &= ~VIDEO_MODE_MASK;
 		reg |= VIDEO_MASTER_MODE_EN | VIDEO_MODE_MASTER_MODE;
-		writel(reg, dp->reg_base + ANALOGIX_DP_SOC_GENERAL_CTL);
+		analogix_dp_write(dp, ANALOGIX_DP_SOC_GENERAL_CTL, reg);
 	} else {
-		reg = readl(dp->reg_base + ANALOGIX_DP_SOC_GENERAL_CTL);
+		reg = analogix_dp_read(dp, ANALOGIX_DP_SOC_GENERAL_CTL);
 		reg &= ~VIDEO_MODE_MASK;
 		reg |= VIDEO_MODE_SLAVE_MODE;
-		writel(reg, dp->reg_base + ANALOGIX_DP_SOC_GENERAL_CTL);
+		analogix_dp_write(dp, ANALOGIX_DP_SOC_GENERAL_CTL, reg);
 	}
 }
 
@@ -947,19 +889,19 @@ void analogix_dp_start_video(struct analogix_dp_device *dp)
 {
 	u32 reg;
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_VIDEO_CTL_1);
 	reg |= VIDEO_EN;
-	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1);
+	analogix_dp_write(dp, ANALOGIX_DP_VIDEO_CTL_1, reg);
 }
 
 int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp)
 {
 	u32 reg;
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
-	writel(reg, dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_SYS_CTL_3);
+	analogix_dp_write(dp, ANALOGIX_DP_SYS_CTL_3, reg);
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_3);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_SYS_CTL_3);
 	if (!(reg & STRM_VALID)) {
 		dev_dbg(dp->dev, "Input video stream is not detected.\n");
 		return -EINVAL;
@@ -972,55 +914,55 @@ void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp)
 {
 	u32 reg;
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_FUNC_EN_1);
 	if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) {
 		reg &= ~(RK_VID_CAP_FUNC_EN_N | RK_VID_FIFO_FUNC_EN_N);
 	} else {
 		reg &= ~(MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N);
 		reg |= MASTER_VID_FUNC_EN_N;
 	}
-	writel(reg, dp->reg_base + ANALOGIX_DP_FUNC_EN_1);
+	analogix_dp_write(dp, ANALOGIX_DP_FUNC_EN_1, reg);
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_VIDEO_CTL_10);
 	reg &= ~INTERACE_SCAN_CFG;
 	reg |= (dp->video_info.interlaced << 2);
-	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+	analogix_dp_write(dp, ANALOGIX_DP_VIDEO_CTL_10, reg);
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_VIDEO_CTL_10);
 	reg &= ~VSYNC_POLARITY_CFG;
 	reg |= (dp->video_info.v_sync_polarity << 1);
-	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+	analogix_dp_write(dp, ANALOGIX_DP_VIDEO_CTL_10, reg);
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_VIDEO_CTL_10);
 	reg &= ~HSYNC_POLARITY_CFG;
 	reg |= (dp->video_info.h_sync_polarity << 0);
-	writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_10);
+	analogix_dp_write(dp, ANALOGIX_DP_VIDEO_CTL_10, reg);
 
 	reg = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE;
-	writel(reg, dp->reg_base + ANALOGIX_DP_SOC_GENERAL_CTL);
+	analogix_dp_write(dp, ANALOGIX_DP_SOC_GENERAL_CTL, reg);
 }
 
 void analogix_dp_enable_scrambling(struct analogix_dp_device *dp)
 {
 	u32 reg;
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_TRAINING_PTN_SET);
 	reg &= ~SCRAMBLING_DISABLE;
-	writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+	analogix_dp_write(dp, ANALOGIX_DP_TRAINING_PTN_SET, reg);
 }
 
 void analogix_dp_disable_scrambling(struct analogix_dp_device *dp)
 {
 	u32 reg;
 
-	reg = readl(dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_TRAINING_PTN_SET);
 	reg |= SCRAMBLING_DISABLE;
-	writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
+	analogix_dp_write(dp, ANALOGIX_DP_TRAINING_PTN_SET, reg);
 }
 
 void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
 {
-	writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON);
+	analogix_dp_write(dp, ANALOGIX_DP_CRC_CON, PSR_VID_CRC_ENABLE);
 }
 
 static ssize_t analogix_dp_get_psr_status(struct analogix_dp_device *dp)
@@ -1044,63 +986,53 @@ int analogix_dp_send_psr_spd(struct analogix_dp_device *dp,
 	ssize_t psr_status;
 
 	/* don't send info frame */
-	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	val = analogix_dp_read(dp, ANALOGIX_DP_PKT_SEND_CTL);
 	val &= ~IF_EN;
-	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	analogix_dp_write(dp, ANALOGIX_DP_PKT_SEND_CTL, val);
 
 	/* configure single frame update mode */
-	writel(PSR_FRAME_UP_TYPE_BURST | PSR_CRC_SEL_HARDWARE,
-	       dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
+	analogix_dp_write(dp, ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL,
+			  PSR_FRAME_UP_TYPE_BURST | PSR_CRC_SEL_HARDWARE);
 
 	/* configure VSC HB0~HB3 */
-	writel(vsc->sdp_header.HB0, dp->reg_base + ANALOGIX_DP_SPD_HB0);
-	writel(vsc->sdp_header.HB1, dp->reg_base + ANALOGIX_DP_SPD_HB1);
-	writel(vsc->sdp_header.HB2, dp->reg_base + ANALOGIX_DP_SPD_HB2);
-	writel(vsc->sdp_header.HB3, dp->reg_base + ANALOGIX_DP_SPD_HB3);
+	analogix_dp_write(dp, ANALOGIX_DP_SPD_HB0, vsc->sdp_header.HB0);
+	analogix_dp_write(dp, ANALOGIX_DP_SPD_HB1, vsc->sdp_header.HB1);
+	analogix_dp_write(dp, ANALOGIX_DP_SPD_HB2, vsc->sdp_header.HB2);
+	analogix_dp_write(dp, ANALOGIX_DP_SPD_HB3, vsc->sdp_header.HB3);
 
 	/* configure reused VSC PB0~PB3, magic number from vendor */
-	writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0);
-	writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1);
-	writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2);
-	writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3);
+	analogix_dp_write(dp, ANALOGIX_DP_SPD_PB0, 0x00);
+	analogix_dp_write(dp, ANALOGIX_DP_SPD_PB1, 0x16);
+	analogix_dp_write(dp, ANALOGIX_DP_SPD_PB2, 0xCE);
+	analogix_dp_write(dp, ANALOGIX_DP_SPD_PB3, 0x5D);
 
 	/* configure DB0 / DB1 values */
-	writel(vsc->db[0], dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);
-	writel(vsc->db[1], dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
+	analogix_dp_write(dp, ANALOGIX_DP_VSC_SHADOW_DB0, vsc->db[0]);
+	analogix_dp_write(dp, ANALOGIX_DP_VSC_SHADOW_DB1, vsc->db[1]);
 
 	/* set reuse spd inforframe */
-	val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+	val = analogix_dp_read(dp, ANALOGIX_DP_VIDEO_CTL_3);
 	val |= REUSE_SPD_EN;
-	writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+	analogix_dp_write(dp, ANALOGIX_DP_VIDEO_CTL_3, val);
 
 	/* mark info frame update */
-	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	val = analogix_dp_read(dp, ANALOGIX_DP_PKT_SEND_CTL);
 	val = (val | IF_UP) & ~IF_EN;
-	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	analogix_dp_write(dp, ANALOGIX_DP_PKT_SEND_CTL, val);
 
 	/* send info frame */
-	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	val = analogix_dp_read(dp, ANALOGIX_DP_PKT_SEND_CTL);
 	val |= IF_EN;
-	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	analogix_dp_write(dp, ANALOGIX_DP_PKT_SEND_CTL, val);
 
 	if (!blocking)
 		return 0;
 
-	/*
-	 * db[1]!=0: entering PSR, wait for fully active remote frame buffer.
-	 * db[1]==0: exiting PSR, wait for either
-	 *  (a) ACTIVE_RESYNC - the sink "must display the
-	 *      incoming active frames from the Source device with no visible
-	 *      glitches and/or artifacts", even though timings may still be
-	 *      re-synchronizing; or
-	 *  (b) INACTIVE - the transition is fully complete.
-	 */
 	ret = readx_poll_timeout(analogix_dp_get_psr_status, dp, psr_status,
 		psr_status >= 0 &&
 		((vsc->db[1] && psr_status == DP_PSR_SINK_ACTIVE_RFB) ||
-		(!vsc->db[1] && (psr_status == DP_PSR_SINK_ACTIVE_RESYNC ||
-				 psr_status == DP_PSR_SINK_INACTIVE))),
-		1500, DP_TIMEOUT_PSR_LOOP_MS * 1000);
+		(!vsc->db[1] && psr_status == DP_PSR_SINK_INACTIVE)), 1500,
+		DP_TIMEOUT_PSR_LOOP_MS * 1000);
 	if (ret) {
 		dev_warn(dp->dev, "Failed to apply PSR %d\n", ret);
 		return ret;
@@ -1108,11 +1040,43 @@ int analogix_dp_send_psr_spd(struct analogix_dp_device *dp,
 	return 0;
 }
 
+void analogix_dp_phy_power_on(struct analogix_dp_device *dp)
+{
+	if (dp->phy_enabled)
+		return;
+
+	phy_set_mode(dp->phy, PHY_MODE_DP);
+	phy_power_on(dp->phy);
+
+	dp->phy_enabled = true;
+}
+
+void analogix_dp_phy_power_off(struct analogix_dp_device *dp)
+{
+	if (!dp->phy_enabled)
+		return;
+
+	phy_power_off(dp->phy);
+
+	dp->phy_enabled = false;
+}
+
+enum {
+	AUX_STATUS_OK,
+	AUX_STATUS_NACK_ERROR,
+	AUX_STATUS_TIMEOUT_ERROR,
+	AUX_STATUS_UNKNOWN_ERROR,
+	AUX_STATUS_MUCH_DEFER_ERROR,
+	AUX_STATUS_TX_SHORT_ERROR,
+	AUX_STATUS_RX_SHORT_ERROR,
+	AUX_STATUS_NACK_WITHOUT_M_ERROR,
+	AUX_STATUS_I2C_NACK_ERROR
+};
+
 ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
 			     struct drm_dp_aux_msg *msg)
 {
 	u32 reg;
-	u32 status_reg;
 	u8 *buffer = msg->buffer;
 	unsigned int i;
 	int num_transferred = 0;
@@ -1122,9 +1086,15 @@ ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
 	if (WARN_ON(msg->size > 16))
 		return -E2BIG;
 
+	reg = analogix_dp_read(dp, ANALOGIX_DP_FUNC_EN_2);
+	if (reg & AUX_FUNC_EN_N) {
+		analogix_dp_phy_power_on(dp);
+		analogix_dp_init_aux(dp);
+	}
+
 	/* Clear AUX CH data buffer */
 	reg = BUF_CLR;
-	writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
+	analogix_dp_write(dp, ANALOGIX_DP_BUFFER_DATA_CTL, reg);
 
 	switch (msg->request & ~DP_AUX_I2C_MOT) {
 	case DP_AUX_I2C_WRITE:
@@ -1152,21 +1122,21 @@ ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
 	}
 
 	reg |= AUX_LENGTH(msg->size);
-	writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1);
+	analogix_dp_write(dp, ANALOGIX_DP_AUX_CH_CTL_1, reg);
 
 	/* Select DPCD device address */
 	reg = AUX_ADDR_7_0(msg->address);
-	writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0);
+	analogix_dp_write(dp, ANALOGIX_DP_AUX_ADDR_7_0, reg);
 	reg = AUX_ADDR_15_8(msg->address);
-	writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8);
+	analogix_dp_write(dp, ANALOGIX_DP_AUX_ADDR_15_8, reg);
 	reg = AUX_ADDR_19_16(msg->address);
-	writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16);
+	analogix_dp_write(dp, ANALOGIX_DP_AUX_ADDR_19_16, reg);
 
 	if (!(msg->request & DP_AUX_I2C_READ)) {
 		for (i = 0; i < msg->size; i++) {
 			reg = buffer[i];
-			writel(reg, dp->reg_base + ANALOGIX_DP_BUF_DATA_0 +
-			       4 * i);
+			analogix_dp_write(dp, ANALOGIX_DP_BUF_DATA_0 + 4 * i,
+					  reg);
 			num_transferred++;
 		}
 	}
@@ -1178,7 +1148,7 @@ ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
 	if (msg->size < 1)
 		reg |= ADDR_ONLY;
 
-	writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2);
+	analogix_dp_write(dp, ANALOGIX_DP_AUX_CH_CTL_2, reg);
 
 	ret = readx_poll_timeout(readl, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2,
 				 reg, !(reg & AUX_EN), 25, 500 * 1000);
@@ -1197,30 +1167,31 @@ ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
 	}
 
 	/* Clear interrupt source for AUX CH command reply */
-	writel(RPLY_RECEIV, dp->reg_base + ANALOGIX_DP_INT_STA);
-
-	/* Clear interrupt source for AUX CH access error */
-	reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
-	status_reg = readl(dp->reg_base + ANALOGIX_DP_AUX_CH_STA);
-	if ((reg & AUX_ERR) || (status_reg & AUX_STATUS_MASK)) {
-		writel(AUX_ERR, dp->reg_base + ANALOGIX_DP_INT_STA);
+	analogix_dp_write(dp, ANALOGIX_DP_INT_STA, RPLY_RECEIV);
 
-		dev_warn(dp->dev, "AUX CH error happened: %#x (%d)\n",
-			 status_reg & AUX_STATUS_MASK, !!(reg & AUX_ERR));
-		goto aux_error;
-	}
+	reg = analogix_dp_read(dp, ANALOGIX_DP_AUX_CH_STA);
+	if ((reg & AUX_STATUS_MASK) == AUX_STATUS_TIMEOUT_ERROR)
+		return -ETIMEDOUT;
 
 	if (msg->request & DP_AUX_I2C_READ) {
+		size_t buf_data_count;
+
+		reg = analogix_dp_read(dp, ANALOGIX_DP_BUFFER_DATA_CTL);
+		buf_data_count = BUF_DATA_COUNT(reg);
+
+		if (buf_data_count != msg->size)
+			return -EBUSY;
+
 		for (i = 0; i < msg->size; i++) {
-			reg = readl(dp->reg_base + ANALOGIX_DP_BUF_DATA_0 +
-				    4 * i);
+			reg = analogix_dp_read(dp, ANALOGIX_DP_BUF_DATA_0 +
+					       4 * i);
 			buffer[i] = (unsigned char)reg;
 			num_transferred++;
 		}
 	}
 
 	/* Check if Rx sends defer */
-	reg = readl(dp->reg_base + ANALOGIX_DP_AUX_RX_COMM);
+	reg = analogix_dp_read(dp, ANALOGIX_DP_AUX_RX_COMM);
 	if (reg == AUX_RX_COMM_AUX_DEFER)
 		msg->reply = DP_AUX_NATIVE_REPLY_DEFER;
 	else if (reg == AUX_RX_COMM_I2C_DEFER)
@@ -1232,7 +1203,7 @@ ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
 		 (msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_NATIVE_READ)
 		msg->reply = DP_AUX_NATIVE_REPLY_ACK;
 
-	return num_transferred > 0 ? num_transferred : -EBUSY;
+	return (num_transferred == msg->size) ? num_transferred : -EBUSY;
 
 aux_error:
 	/* if aux err happen, reset aux */
@@ -1240,3 +1211,119 @@ ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
 
 	return -EREMOTEIO;
 }
+
+void analogix_dp_set_video_format(struct analogix_dp_device *dp)
+{
+	struct video_info *video = &dp->video_info;
+	const struct drm_display_mode *mode = &video->mode;
+	unsigned int hsw, hfp, hbp, vsw, vfp, vbp;
+
+	hsw = mode->hsync_end - mode->hsync_start;
+	hfp = mode->hsync_start - mode->hdisplay;
+	hbp = mode->htotal - mode->hsync_end;
+	vsw = mode->vsync_end - mode->vsync_start;
+	vfp = mode->vsync_start - mode->vdisplay;
+	vbp = mode->vtotal - mode->vsync_end;
+
+	/* Set Video Format Parameters */
+	analogix_dp_write(dp, ANALOGIX_DP_TOTAL_LINE_CFG_L,
+			  TOTAL_LINE_CFG_L(mode->vtotal));
+	analogix_dp_write(dp, ANALOGIX_DP_TOTAL_LINE_CFG_H,
+			  TOTAL_LINE_CFG_H(mode->vtotal >> 8));
+	analogix_dp_write(dp, ANALOGIX_DP_ACTIVE_LINE_CFG_L,
+			  ACTIVE_LINE_CFG_L(mode->vdisplay));
+	analogix_dp_write(dp, ANALOGIX_DP_ACTIVE_LINE_CFG_H,
+			  ACTIVE_LINE_CFG_H(mode->vdisplay >> 8));
+	analogix_dp_write(dp, ANALOGIX_DP_V_F_PORCH_CFG,
+			  V_F_PORCH_CFG(vfp));
+	analogix_dp_write(dp, ANALOGIX_DP_V_SYNC_WIDTH_CFG,
+			  V_SYNC_WIDTH_CFG(vsw));
+	analogix_dp_write(dp, ANALOGIX_DP_V_B_PORCH_CFG,
+			  V_B_PORCH_CFG(vbp));
+	analogix_dp_write(dp, ANALOGIX_DP_TOTAL_PIXEL_CFG_L,
+			  TOTAL_PIXEL_CFG_L(mode->htotal));
+	analogix_dp_write(dp, ANALOGIX_DP_TOTAL_PIXEL_CFG_H,
+			  TOTAL_PIXEL_CFG_H(mode->htotal >> 8));
+	analogix_dp_write(dp, ANALOGIX_DP_ACTIVE_PIXEL_CFG_L,
+			  ACTIVE_PIXEL_CFG_L(mode->hdisplay));
+	analogix_dp_write(dp, ANALOGIX_DP_ACTIVE_PIXEL_CFG_H,
+			  ACTIVE_PIXEL_CFG_H(mode->hdisplay >> 8));
+	analogix_dp_write(dp, ANALOGIX_DP_H_F_PORCH_CFG_L,
+			  H_F_PORCH_CFG_L(hfp));
+	analogix_dp_write(dp, ANALOGIX_DP_H_F_PORCH_CFG_H,
+			  H_F_PORCH_CFG_H(hfp >> 8));
+	analogix_dp_write(dp, ANALOGIX_DP_H_SYNC_CFG_L,
+			  H_SYNC_CFG_L(hsw));
+	analogix_dp_write(dp, ANALOGIX_DP_H_SYNC_CFG_H,
+			  H_SYNC_CFG_H(hsw >> 8));
+	analogix_dp_write(dp, ANALOGIX_DP_H_B_PORCH_CFG_L,
+			  H_B_PORCH_CFG_L(hbp));
+	analogix_dp_write(dp, ANALOGIX_DP_H_B_PORCH_CFG_H,
+			  H_B_PORCH_CFG_H(hbp >> 8));
+}
+
+void analogix_dp_video_bist_enable(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	/* Enable Video BIST */
+	analogix_dp_write(dp, ANALOGIX_DP_VIDEO_CTL_4, BIST_EN);
+
+	/*
+	 * Note that if BIST_EN is set to 1, F_SEL must be cleared to 0
+	 * although video format information comes from registers set by user.
+	 */
+	reg = analogix_dp_read(dp, ANALOGIX_DP_VIDEO_CTL_10);
+	reg &= ~FORMAT_SEL;
+	analogix_dp_write(dp, ANALOGIX_DP_VIDEO_CTL_10, reg);
+}
+
+void analogix_dp_audio_config_i2s(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = analogix_dp_read(dp, ANALOGIX_DP_SYS_CTL_4);
+	reg &= ~FIX_M_AUD;
+	analogix_dp_write(dp, ANALOGIX_DP_SYS_CTL_4, reg);
+
+	reg = analogix_dp_read(dp, ANALOGIX_DP_I2S_CTRL);
+	reg |= I2S_EN;
+	analogix_dp_write(dp, ANALOGIX_DP_I2S_CTRL, reg);
+}
+
+void analogix_dp_audio_config_spdif(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = analogix_dp_read(dp, ANALOGIX_DP_SYS_CTL_4);
+	reg &= ~FIX_M_AUD;
+	analogix_dp_write(dp, ANALOGIX_DP_SYS_CTL_4, reg);
+
+	reg = analogix_dp_read(dp, ANALOGIX_DP_SPDIF_AUDIO_CTL_0);
+	reg |= AUD_SPDIF_EN;
+	analogix_dp_write(dp, ANALOGIX_DP_SPDIF_AUDIO_CTL_0, reg);
+}
+
+void analogix_dp_audio_enable(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	reg = analogix_dp_read(dp, ANALOGIX_DP_FUNC_EN_1);
+	reg &= ~(AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N);
+	analogix_dp_write(dp, ANALOGIX_DP_FUNC_EN_1, reg);
+
+	reg = analogix_dp_read(dp, ANALOGIX_DP_AUD_CTL);
+	reg |= MISC_CTRL_RESET | DP_AUDIO_EN;
+	analogix_dp_write(dp, ANALOGIX_DP_AUD_CTL, reg);
+}
+
+void analogix_dp_audio_disable(struct analogix_dp_device *dp)
+{
+	u32 reg;
+
+	analogix_dp_write(dp, ANALOGIX_DP_AUD_CTL, 0);
+
+	reg = analogix_dp_read(dp, ANALOGIX_DP_FUNC_EN_1);
+	reg |= AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N;
+	analogix_dp_write(dp, ANALOGIX_DP_FUNC_EN_1, reg);
+}
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
index e284ee8da..df88f1ad0 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
@@ -15,9 +15,27 @@
 #define ANALOGIX_DP_VIDEO_CTL_1			0x20
 #define ANALOGIX_DP_VIDEO_CTL_2			0x24
 #define ANALOGIX_DP_VIDEO_CTL_3			0x28
+#define ANALOGIX_DP_VIDEO_CTL_4			0x2C
 
 #define ANALOGIX_DP_VIDEO_CTL_8			0x3C
 #define ANALOGIX_DP_VIDEO_CTL_10		0x44
+#define ANALOGIX_DP_TOTAL_LINE_CFG_L		0x48
+#define ANALOGIX_DP_TOTAL_LINE_CFG_H		0x4C
+#define ANALOGIX_DP_ACTIVE_LINE_CFG_L		0x50
+#define ANALOGIX_DP_ACTIVE_LINE_CFG_H		0x54
+#define ANALOGIX_DP_V_F_PORCH_CFG		0x58
+#define ANALOGIX_DP_V_SYNC_WIDTH_CFG		0x5C
+#define ANALOGIX_DP_V_B_PORCH_CFG		0x60
+#define ANALOGIX_DP_TOTAL_PIXEL_CFG_L		0x64
+#define ANALOGIX_DP_TOTAL_PIXEL_CFG_H		0x68
+#define ANALOGIX_DP_ACTIVE_PIXEL_CFG_L		0x6C
+#define ANALOGIX_DP_ACTIVE_PIXEL_CFG_H		0x70
+#define ANALOGIX_DP_H_F_PORCH_CFG_L		0x74
+#define ANALOGIX_DP_H_F_PORCH_CFG_H		0x78
+#define ANALOGIX_DP_H_SYNC_CFG_L		0x7C
+#define ANALOGIX_DP_H_SYNC_CFG_H		0x80
+#define ANALOGIX_DP_H_B_PORCH_CFG_L		0x84
+#define ANALOGIX_DP_H_B_PORCH_CFG_H		0x88
 
 #define ANALOGIX_DP_SPDIF_AUDIO_CTL_0		0xD8
 
@@ -27,6 +45,8 @@
 #define ANALOGIX_DP_PLL_REG_4			0x9ec
 #define ANALOGIX_DP_PLL_REG_5			0xa00
 
+#define ANALOIGX_DP_SSC_REG			0x104
+#define ANALOGIX_DP_BIAS			0x124
 #define ANALOGIX_DP_PD				0x12c
 
 #define ANALOGIX_DP_IF_TYPE			0x244
@@ -70,7 +90,7 @@
 #define ANALOGIX_DP_SYS_CTL_2			0x604
 #define ANALOGIX_DP_SYS_CTL_3			0x608
 #define ANALOGIX_DP_SYS_CTL_4			0x60C
-
+#define ANALOGIX_DP_AUD_CTL			0x618
 #define ANALOGIX_DP_PKT_SEND_CTL		0x640
 #define ANALOGIX_DP_HDCP_CTL			0x648
 
@@ -116,8 +136,9 @@
 #define ANALOGIX_DP_BUF_DATA_0			0x7C0
 
 #define ANALOGIX_DP_SOC_GENERAL_CTL		0x800
-
+#define ANALOGIX_DP_AUD_CHANNEL_CTL		0x834
 #define ANALOGIX_DP_CRC_CON			0x890
+#define ANALOGIX_DP_I2S_CTRL			0x9C8
 
 /* ANALOGIX_DP_TX_SW_RESET */
 #define RESET_DP_TX				(0x1 << 0)
@@ -171,6 +192,11 @@
 #define VID_CHK_UPDATE_TYPE_0			(0x0 << 4)
 #define REUSE_SPD_EN				(0x1 << 3)
 
+/* ANALOGIX_DP_VIDEO_CTL_4 */
+#define BIST_EN					(0x1 << 3)
+#define BIST_WIDTH(x)				(((x) & 0x1) << 2)
+#define BIST_TYPE(x)				(((x) & 0x3) << 0)
+
 /* ANALOGIX_DP_VIDEO_CTL_8 */
 #define VID_HRES_TH(x)				(((x) & 0xf) << 4)
 #define VID_VRES_TH(x)				(((x) & 0xf) << 0)
@@ -181,6 +207,60 @@
 #define VSYNC_POLARITY_CFG			(0x1 << 1)
 #define HSYNC_POLARITY_CFG			(0x1 << 0)
 
+/* ANALOGIX_DP_TOTAL_LINE_CFG_L */
+#define TOTAL_LINE_CFG_L(x)			(((x) & 0xff) << 0)
+
+/* ANALOGIX_DP_TOTAL_LINE_CFG_H */
+#define TOTAL_LINE_CFG_H(x)			(((x) & 0xf) << 0)
+
+/* ANALOGIX_DP_ACTIVE_LINE_CFG_L */
+#define ACTIVE_LINE_CFG_L(x)			(((x) & 0xff) << 0)
+
+/* ANALOGIX_DP_ACTIVE_LINE_CFG_H */
+#define ACTIVE_LINE_CFG_H(x)			(((x) & 0xf) << 0)
+
+/* ANALOGIX_DP_V_F_PORCH_CFG */
+#define V_F_PORCH_CFG(x)			(((x) & 0xff) << 0)
+
+/* ANALOGIX_DP_V_SYNC_WIDTH_CFG */
+#define V_SYNC_WIDTH_CFG(x)			(((x) & 0xff) << 0)
+
+/* ANALOGIX_DP_V_B_PORCH_CFG */
+#define V_B_PORCH_CFG(x)			(((x) & 0xff) << 0)
+
+/* ANALOGIX_DP_TOTAL_PIXEL_CFG_L */
+#define TOTAL_PIXEL_CFG_L(x)			(((x) & 0xff) << 0)
+
+/* ANALOGIX_DP_TOTAL_PIXEL_CFG_H */
+#define TOTAL_PIXEL_CFG_H(x)			(((x) & 0x3f) << 0)
+
+/* ANALOGIX_DP_ACTIVE_PIXEL_CFG_L */
+#define ACTIVE_PIXEL_CFG_L(x)			(((x) & 0xff) << 0)
+
+/* ANALOGIX_DP_ACTIVE_PIXEL_CFG_H */
+#define ACTIVE_PIXEL_CFG_H(x)			(((x) & 0x3f) << 0)
+
+/* ANALOGIX_DP_H_F_PORCH_CFG_L */
+#define H_F_PORCH_CFG_L(x)			(((x) & 0xff) << 0)
+
+/* ANALOGIX_DP_H_F_PORCH_CFG_H */
+#define H_F_PORCH_CFG_H(x)			(((x) & 0xf) << 0)
+
+/* ANALOGIX_DP_H_SYNC_CFG_L */
+#define H_SYNC_CFG_L(x)				(((x) & 0xff) << 0)
+
+/* ANALOGIX_DP_H_SYNC_CFG_H */
+#define H_SYNC_CFG_H(x)				(((x) & 0xf) << 0)
+
+/* ANALOGIX_DP_H_B_PORCH_CFG_L */
+#define H_B_PORCH_CFG_L(x)			(((x) & 0xff) << 0)
+
+/* ANALOGIX_DP_H_B_PORCH_CFG_H */
+#define H_B_PORCH_CFG_H(x)			(((x) & 0xf) << 0)
+
+/* ANALOGIX_DP_SPDIF_AUDIO_CTL_0 */
+#define AUD_SPDIF_EN				(0x1 << 7)
+
 /* ANALOGIX_DP_PLL_REG_1 */
 #define REF_CLK_24M				(0x1 << 0)
 #define REF_CLK_27M				(0x0 << 0)
@@ -309,6 +389,10 @@
 #define FIX_M_VID				(0x1 << 2)
 #define M_VID_UPDATE_CTRL			(0x3 << 0)
 
+/* ANALOGIX_DP_AUD_CTL */
+#define MISC_CTRL_RESET				(0x1 << 4)
+#define DP_AUDIO_EN				(0x1 << 0)
+
 /* ANALOGIX_DP_TRAINING_PTN_SET */
 #define SCRAMBLER_TYPE				(0x1 << 9)
 #define HW_LINK_TRAINING_PATTERN		(0x1 << 8)
@@ -319,6 +403,7 @@
 #define LINK_QUAL_PATTERN_SET_D10_2		(0x1 << 2)
 #define LINK_QUAL_PATTERN_SET_DISABLE		(0x0 << 2)
 #define SW_TRAINING_PATTERN_SET_MASK		(0x3 << 0)
+#define SW_TRAINING_PATTERN_SET_PTN3		(0x3 << 0)
 #define SW_TRAINING_PATTERN_SET_PTN2		(0x2 << 0)
 #define SW_TRAINING_PATTERN_SET_PTN1		(0x1 << 0)
 #define SW_TRAINING_PATTERN_SET_NORMAL		(0x0 << 0)
@@ -406,6 +491,11 @@
 #define VIDEO_MODE_SLAVE_MODE			(0x1 << 0)
 #define VIDEO_MODE_MASTER_MODE			(0x0 << 0)
 
+/* ANALOGIX_DP_AUD_CHANNEL_CTL */
+#define AUD_CHANNEL_COUNT_6			(0x5 << 0)
+#define AUD_CHANNEL_COUNT_4			(0x3 << 0)
+#define AUD_CHANNEL_COUNT_2			(0x1 << 0)
+
 /* ANALOGIX_DP_PKT_SEND_CTL */
 #define IF_UP					(0x1 << 4)
 #define IF_EN					(0x1 << 0)
@@ -414,4 +504,7 @@
 #define PSR_VID_CRC_FLUSH			(0x1 << 2)
 #define PSR_VID_CRC_ENABLE			(0x1 << 0)
 
+/* ANALOGIX_DP_I2S_CTRL */
+#define I2S_EN					(0x1 << 4)
+
 #endif /* _ANALOGIX_DP_REG_H */
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h
index f72d27208..20c818225 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-audio.h
@@ -10,10 +10,12 @@ struct dw_hdmi_audio_data {
 	int irq;
 	struct dw_hdmi *hdmi;
 	u8 *(*get_eld)(struct dw_hdmi *hdmi);
+	u8 *eld;
 };
 
 struct dw_hdmi_i2s_audio_data {
 	struct dw_hdmi *hdmi;
+	u8 *eld;
 
 	void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);
 	u8 (*read)(struct dw_hdmi *hdmi, int offset);
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c
index 70ab4fbdc..48fc36d56 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c
@@ -12,6 +12,7 @@
 #include <linux/slab.h>
 
 #include <drm/drm_edid.h>
+#include <drm/bridge/dw_hdmi.h>
 
 #include <media/cec.h>
 #include <media/cec-notifier.h>
@@ -262,6 +263,8 @@ static int dw_hdmi_cec_probe(struct platform_device *pdev)
 	if (IS_ERR(cec->adap))
 		return PTR_ERR(cec->adap);
 
+	dw_hdmi_set_cec_adap(cec->hdmi, cec->adap);
+
 	/* override the module pointer */
 	cec->adap->owner = THIS_MODULE;
 
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 2c3c743df..268ecdf3c 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -9,6 +9,8 @@
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/err.h>
+#include <linux/extcon.h>
+#include <linux/extcon-provider.h>
 #include <linux/hdmi.h>
 #include <linux/irq.h>
 #include <linux/module.h>
@@ -18,6 +20,7 @@
 #include <linux/regmap.h>
 #include <linux/dma-mapping.h>
 #include <linux/spinlock.h>
+#include <linux/pinctrl/consumer.h>
 
 #include <media/cec-notifier.h>
 
@@ -36,6 +39,7 @@
 
 #include "dw-hdmi-audio.h"
 #include "dw-hdmi-cec.h"
+#include "dw-hdmi-hdcp.h"
 #include "dw-hdmi.h"
 
 #define DDC_CI_ADDR		0x37
@@ -48,6 +52,11 @@
 
 #define HDMI14_MAX_TMDSCLK	340000000
 
+static const unsigned int dw_hdmi_cable[] = {
+	EXTCON_DISP_HDMI,
+	EXTCON_NONE,
+};
+
 enum hdmi_datamap {
 	RGB444_8B = 0x01,
 	RGB444_10B = 0x03,
@@ -62,6 +71,61 @@ enum hdmi_datamap {
 	YCbCr422_12B = 0x12,
 };
 
+/*
+ * Unless otherwise noted, entries in this table are 100% optimization.
+ * Values can be obtained from hdmi_compute_n() but that function is
+ * slow so we pre-compute values we expect to see.
+ *
+ * All 32k and 48k values are expected to be the same (due to the way
+ * the math works) for any rate that's an exact kHz.
+ */
+static const struct dw_hdmi_audio_tmds_n common_tmds_n_table[] = {
+	{ .tmds = 25175000, .n_32k = 4096, .n_44k1 = 12854, .n_48k = 6144, },
+	{ .tmds = 25200000, .n_32k = 4096, .n_44k1 = 5656, .n_48k = 6144, },
+	{ .tmds = 27000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
+	{ .tmds = 28320000, .n_32k = 4096, .n_44k1 = 5586, .n_48k = 6144, },
+	{ .tmds = 30240000, .n_32k = 4096, .n_44k1 = 5642, .n_48k = 6144, },
+	{ .tmds = 31500000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, },
+	{ .tmds = 32000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, },
+	{ .tmds = 33750000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
+	{ .tmds = 36000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
+	{ .tmds = 40000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, },
+	{ .tmds = 49500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
+	{ .tmds = 50000000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, },
+	{ .tmds = 54000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
+	{ .tmds = 65000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
+	{ .tmds = 68250000, .n_32k = 4096, .n_44k1 = 5376, .n_48k = 6144, },
+	{ .tmds = 71000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
+	{ .tmds = 72000000, .n_32k = 4096, .n_44k1 = 5635, .n_48k = 6144, },
+	{ .tmds = 73250000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
+	{ .tmds = 74250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
+	{ .tmds = 75000000, .n_32k = 4096, .n_44k1 = 5880, .n_48k = 6144, },
+	{ .tmds = 78750000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, },
+	{ .tmds = 78800000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, },
+	{ .tmds = 79500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, },
+	{ .tmds = 83500000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
+	{ .tmds = 85500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
+	{ .tmds = 88750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
+	{ .tmds = 97750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, },
+	{ .tmds = 101000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, },
+	{ .tmds = 106500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, },
+	{ .tmds = 108000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
+	{ .tmds = 115500000, .n_32k = 4096, .n_44k1 = 5712, .n_48k = 6144, },
+	{ .tmds = 119000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, },
+	{ .tmds = 135000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
+	{ .tmds = 146250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, },
+	{ .tmds = 148500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, },
+	{ .tmds = 154000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, },
+	{ .tmds = 162000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
+
+	/* For 297 MHz+ HDMI spec have some other rule for setting N */
+	{ .tmds = 297000000, .n_32k = 3073, .n_44k1 = 4704, .n_48k = 5120, },
+	{ .tmds = 594000000, .n_32k = 3073, .n_44k1 = 9408, .n_48k = 10240, },
+
+	/* End of table */
+	{ .tmds = 0,         .n_32k = 0,    .n_44k1 = 0,    .n_48k = 0, },
+};
+
 static const u16 csc_coeff_default[3][4] = {
 	{ 0x2000, 0x0000, 0x0000, 0x0000 },
 	{ 0x0000, 0x2000, 0x0000, 0x0000 },
@@ -98,12 +162,47 @@ static const u16 csc_coeff_rgb_full_to_rgb_limited[3][4] = {
 	{ 0x0000, 0x0000, 0x1b7c, 0x0020 }
 };
 
+static const struct drm_display_mode dw_hdmi_default_modes[] = {
+	/* 4 - 1280x720@60Hz 16:9 */
+	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
+		   1430, 1650, 0, 720, 725, 730, 750, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+	/* 16 - 1920x1080@60Hz 16:9 */
+	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
+		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+	/* 31 - 1920x1080@50Hz 16:9 */
+	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
+		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+	/* 19 - 1280x720@50Hz 16:9 */
+	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,
+		   1760, 1980, 0, 720, 725, 730, 750, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+	/* 17 - 720x576@50Hz 4:3 */
+	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,
+		   796, 864, 0, 576, 581, 586, 625, 0,
+		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
+	/* 2 - 720x480@60Hz 4:3 */
+	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,
+		   798, 858, 0, 480, 489, 495, 525, 0,
+		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
+};
+
 struct hdmi_vmode {
 	bool mdataenablepolarity;
 
+	unsigned int previous_pixelclock;
 	unsigned int mpixelclock;
 	unsigned int mpixelrepetitioninput;
 	unsigned int mpixelrepetitionoutput;
+	unsigned int previous_tmdsclock;
 	unsigned int mtmdsclock;
 };
 
@@ -112,8 +211,8 @@ struct hdmi_data_info {
 	unsigned int enc_out_bus_format;
 	unsigned int enc_in_encoding;
 	unsigned int enc_out_encoding;
+	unsigned int quant_range;
 	unsigned int pix_repet_factor;
-	unsigned int hdcp_enable;
 	struct hdmi_vmode video_mode;
 	bool rgb_limited_range;
 };
@@ -128,6 +227,9 @@ struct dw_hdmi_i2c {
 	u8			slave_reg;
 	bool			is_regaddr;
 	bool			is_segment;
+
+	unsigned int		scl_high_ns;
+	unsigned int		scl_low_ns;
 };
 
 struct dw_hdmi_phy_data {
@@ -143,6 +245,8 @@ struct dw_hdmi_phy_data {
 struct dw_hdmi {
 	struct drm_connector connector;
 	struct drm_bridge bridge;
+	struct drm_bridge *next_bridge;
+	struct platform_device *hdcp_dev;
 
 	unsigned int version;
 
@@ -156,8 +260,10 @@ struct dw_hdmi {
 
 	struct hdmi_data_info hdmi_data;
 	const struct dw_hdmi_plat_data *plat_data;
+	struct dw_hdcp *hdcp;
 
 	int vic;
+	int irq;
 
 	u8 edid[HDMI_EDID_LEN];
 
@@ -174,6 +280,13 @@ struct dw_hdmi {
 	void __iomem *regs;
 	bool sink_is_hdmi;
 	bool sink_has_audio;
+	bool hpd_state;
+	bool support_hdmi;
+	bool force_logo;
+	int force_output;
+
+	struct delayed_work work;
+	struct workqueue_struct *workqueue;
 
 	struct pinctrl *pinctrl;
 	struct pinctrl_state *default_state;
@@ -190,10 +303,14 @@ struct dw_hdmi {
 
 	spinlock_t audio_lock;
 	struct mutex audio_mutex;
+	struct dentry *debugfs_dir;
 	unsigned int sample_rate;
 	unsigned int audio_cts;
 	unsigned int audio_n;
 	bool audio_enable;
+	bool scramble_low_rates;
+
+	struct extcon_dev *extcon;
 
 	unsigned int reg_shift;
 	struct regmap *regm;
@@ -202,10 +319,12 @@ struct dw_hdmi {
 
 	struct mutex cec_notifier_mutex;
 	struct cec_notifier *cec_notifier;
+	struct cec_adapter *cec_adap;
 
 	hdmi_codec_plugged_cb plugged_cb;
 	struct device *codec_dev;
 	enum drm_connector_status last_connector_result;
+	bool initialized;		/* hdmi is enabled before bind */
 };
 
 #define HDMI_IH_PHY_STAT0_RX_SENSE \
@@ -263,6 +382,124 @@ static void hdmi_mask_writeb(struct dw_hdmi *hdmi, u8 data, unsigned int reg,
 	hdmi_modb(hdmi, data << shift, mask, reg);
 }
 
+static bool dw_hdmi_check_output_type_changed(struct dw_hdmi *hdmi)
+{
+	bool sink_hdmi;
+
+	sink_hdmi = hdmi->sink_is_hdmi;
+
+	if (hdmi->force_output == 1)
+		hdmi->sink_is_hdmi = true;
+	else if (hdmi->force_output == 2)
+		hdmi->sink_is_hdmi = false;
+	else
+		hdmi->sink_is_hdmi = hdmi->support_hdmi;
+
+	if (sink_hdmi != hdmi->sink_is_hdmi)
+		return true;
+
+	return false;
+}
+
+static void repo_hpd_event(struct work_struct *p_work)
+{
+	struct dw_hdmi *hdmi = container_of(p_work, struct dw_hdmi, work.work);
+	enum drm_connector_status status = hdmi->hpd_state ?
+		connector_status_connected : connector_status_disconnected;
+	u8 phy_stat = hdmi_readb(hdmi, HDMI_PHY_STAT0);
+
+	mutex_lock(&hdmi->mutex);
+	if (!(phy_stat & HDMI_PHY_RX_SENSE))
+		hdmi->rxsense = false;
+	if (phy_stat & HDMI_PHY_HPD)
+		hdmi->rxsense = true;
+	mutex_unlock(&hdmi->mutex);
+
+	if (hdmi->bridge.dev) {
+		bool change;
+
+		change = drm_helper_hpd_irq_event(hdmi->bridge.dev);
+		if (change && hdmi->cec_adap &&
+		    hdmi->cec_adap->devnode.registered)
+			cec_queue_pin_hpd_event(hdmi->cec_adap,
+						hdmi->hpd_state,
+						ktime_get());
+		drm_bridge_hpd_notify(&hdmi->bridge, status);
+	}
+}
+
+static bool check_hdmi_irq(struct dw_hdmi *hdmi, int intr_stat,
+			   int phy_int_pol)
+{
+	int msecs;
+
+	/* To determine whether interrupt type is HPD */
+	if (!(intr_stat & HDMI_IH_PHY_STAT0_HPD))
+		return false;
+
+	if (phy_int_pol & HDMI_PHY_HPD) {
+		dev_dbg(hdmi->dev, "dw hdmi plug in\n");
+		msecs = 150;
+		hdmi->hpd_state = true;
+	} else {
+		dev_dbg(hdmi->dev, "dw hdmi plug out\n");
+		msecs = 20;
+		hdmi->hpd_state = false;
+	}
+	mod_delayed_work(hdmi->workqueue, &hdmi->work, msecs_to_jiffies(msecs));
+
+	return true;
+}
+
+static void init_hpd_work(struct dw_hdmi *hdmi)
+{
+	hdmi->workqueue = create_workqueue("hpd_queue");
+	INIT_DELAYED_WORK(&hdmi->work, repo_hpd_event);
+}
+
+static void dw_hdmi_i2c_set_divs(struct dw_hdmi *hdmi)
+{
+	unsigned long clk_rate_khz;
+	unsigned long low_ns, high_ns;
+	unsigned long div_low, div_high;
+
+	/* Standard-mode */
+	if (hdmi->i2c->scl_high_ns < 4000)
+		high_ns = 4708;
+	else
+		high_ns = hdmi->i2c->scl_high_ns;
+
+	if (hdmi->i2c->scl_low_ns < 4700)
+		low_ns = 4916;
+	else
+		low_ns = hdmi->i2c->scl_low_ns;
+
+	/* Adjust to avoid overflow */
+	clk_rate_khz = DIV_ROUND_UP(clk_get_rate(hdmi->isfr_clk), 1000);
+
+	div_low = (clk_rate_khz * low_ns) / 1000000;
+	if ((clk_rate_khz * low_ns) % 1000000)
+		div_low++;
+
+	div_high = (clk_rate_khz * high_ns) / 1000000;
+	if ((clk_rate_khz * high_ns) % 1000000)
+		div_high++;
+
+	/* Maximum divider supported by hw is 0xffff */
+	if (div_low > 0xffff)
+		div_low = 0xffff;
+
+	if (div_high > 0xffff)
+		div_high = 0xffff;
+
+	hdmi_writeb(hdmi, div_high & 0xff, HDMI_I2CM_SS_SCL_HCNT_0_ADDR);
+	hdmi_writeb(hdmi, (div_high >> 8) & 0xff,
+		    HDMI_I2CM_SS_SCL_HCNT_1_ADDR);
+	hdmi_writeb(hdmi, div_low & 0xff, HDMI_I2CM_SS_SCL_LCNT_0_ADDR);
+	hdmi_writeb(hdmi, (div_low >> 8) & 0xff,
+		    HDMI_I2CM_SS_SCL_LCNT_1_ADDR);
+}
+
 static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi)
 {
 	hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
@@ -276,7 +513,8 @@ static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi)
 	hdmi_writeb(hdmi, 0x00, HDMI_I2CM_SOFTRSTZ);
 
 	/* Set Standard Mode speed (determined to be 100KHz on iMX6) */
-	hdmi_writeb(hdmi, 0x00, HDMI_I2CM_DIV);
+	hdmi_modb(hdmi, HDMI_I2CM_DIV_STD_MODE,
+		  HDMI_I2CM_DIV_FAST_STD_MODE, HDMI_I2CM_DIV);
 
 	/* Set done, not acknowledged and arbitration interrupt polarities */
 	hdmi_writeb(hdmi, HDMI_I2CM_INT_DONE_POL, HDMI_I2CM_INT);
@@ -290,6 +528,11 @@ static void dw_hdmi_i2c_init(struct dw_hdmi *hdmi)
 	/* Mute DONE and ERROR interrupts */
 	hdmi_writeb(hdmi, HDMI_IH_I2CM_STAT0_ERROR | HDMI_IH_I2CM_STAT0_DONE,
 		    HDMI_IH_MUTE_I2CM_STAT0);
+
+	/* set SDA high level holding time */
+	hdmi_writeb(hdmi, 0x48, HDMI_I2CM_SDA_HOLD);
+
+	dw_hdmi_i2c_set_divs(hdmi);
 }
 
 static bool dw_hdmi_i2c_unwedge(struct dw_hdmi *hdmi)
@@ -461,6 +704,8 @@ static int dw_hdmi_i2c_xfer(struct i2c_adapter *adap,
 	hdmi_writeb(hdmi, 0x00, HDMI_IH_MUTE_I2CM_STAT0);
 
 	/* Set slave device address taken from the first I2C message */
+	if (addr == DDC_SEGMENT_ADDR && msgs[0].len == 1)
+		addr = DDC_ADDR;
 	hdmi_writeb(hdmi, addr, HDMI_I2CM_SLAVE);
 
 	/* Set slave device register address on transfer */
@@ -570,60 +815,117 @@ static void hdmi_set_cts_n(struct dw_hdmi *hdmi, unsigned int cts,
 	hdmi_writeb(hdmi, n & 0xff, HDMI_AUD_N1);
 }
 
-static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)
+static int hdmi_match_tmds_n_table(struct dw_hdmi *hdmi,
+				   unsigned long pixel_clk,
+				   unsigned long freq)
 {
-	unsigned int n = (128 * freq) / 1000;
-	unsigned int mult = 1;
+	const struct dw_hdmi_plat_data *plat_data = hdmi->plat_data;
+	const struct dw_hdmi_audio_tmds_n *tmds_n = NULL;
+	int i;
+
+	if (plat_data->tmds_n_table) {
+		for (i = 0; plat_data->tmds_n_table[i].tmds != 0; i++) {
+			if (pixel_clk == plat_data->tmds_n_table[i].tmds) {
+				tmds_n = &plat_data->tmds_n_table[i];
+				break;
+			}
+		}
+	}
 
-	while (freq > 48000) {
-		mult *= 2;
-		freq /= 2;
+	if (tmds_n == NULL) {
+		for (i = 0; common_tmds_n_table[i].tmds != 0; i++) {
+			if (pixel_clk == common_tmds_n_table[i].tmds) {
+				tmds_n = &common_tmds_n_table[i];
+				break;
+			}
+		}
 	}
 
+	if (tmds_n == NULL)
+		return -ENOENT;
+
 	switch (freq) {
 	case 32000:
-		if (pixel_clk == 25175000)
-			n = 4576;
-		else if (pixel_clk == 27027000)
-			n = 4096;
-		else if (pixel_clk == 74176000 || pixel_clk == 148352000)
-			n = 11648;
-		else
-			n = 4096;
-		n *= mult;
-		break;
-
+		return tmds_n->n_32k;
 	case 44100:
-		if (pixel_clk == 25175000)
-			n = 7007;
-		else if (pixel_clk == 74176000)
-			n = 17836;
-		else if (pixel_clk == 148352000)
-			n = 8918;
-		else
-			n = 6272;
-		n *= mult;
-		break;
-
+	case 88200:
+	case 176400:
+		return (freq / 44100) * tmds_n->n_44k1;
 	case 48000:
-		if (pixel_clk == 25175000)
-			n = 6864;
-		else if (pixel_clk == 27027000)
-			n = 6144;
-		else if (pixel_clk == 74176000)
-			n = 11648;
-		else if (pixel_clk == 148352000)
-			n = 5824;
-		else
-			n = 6144;
-		n *= mult;
-		break;
-
+	case 96000:
+	case 192000:
+		return (freq / 48000) * tmds_n->n_48k;
 	default:
-		break;
+		return -ENOENT;
+	}
+}
+
+static u64 hdmi_audio_math_diff(unsigned int freq, unsigned int n,
+				unsigned int pixel_clk)
+{
+	u64 final, diff;
+	u64 cts;
+
+	final = (u64)pixel_clk * n;
+
+	cts = final;
+	do_div(cts, 128 * freq);
+
+	diff = final - (u64)cts * (128 * freq);
+
+	return diff;
+}
+
+static unsigned int hdmi_compute_n(struct dw_hdmi *hdmi,
+				   unsigned long pixel_clk,
+				   unsigned long freq)
+{
+	unsigned int min_n = DIV_ROUND_UP((128 * freq), 1500);
+	unsigned int max_n = (128 * freq) / 300;
+	unsigned int ideal_n = (128 * freq) / 1000;
+	unsigned int best_n_distance = ideal_n;
+	unsigned int best_n = 0;
+	u64 best_diff = U64_MAX;
+	int n;
+
+	/* If the ideal N could satisfy the audio math, then just take it */
+	if (hdmi_audio_math_diff(freq, ideal_n, pixel_clk) == 0)
+		return ideal_n;
+
+	for (n = min_n; n <= max_n; n++) {
+		u64 diff = hdmi_audio_math_diff(freq, n, pixel_clk);
+
+		if (diff < best_diff || (diff == best_diff &&
+		    abs(n - ideal_n) < best_n_distance)) {
+			best_n = n;
+			best_diff = diff;
+			best_n_distance = abs(best_n - ideal_n);
+		}
+
+		/*
+		 * The best N already satisfy the audio math, and also be
+		 * the closest value to ideal N, so just cut the loop.
+		 */
+		if ((best_diff == 0) && (abs(n - ideal_n) > best_n_distance))
+			break;
 	}
 
-	return n;
+	return best_n;
+}
+
+static unsigned int hdmi_find_n(struct dw_hdmi *hdmi, unsigned long pixel_clk,
+				unsigned long sample_rate)
+{
+	int n;
+
+	n = hdmi_match_tmds_n_table(hdmi, pixel_clk, sample_rate);
+	if (n > 0)
+		return n;
+
+	dev_warn(hdmi->dev, "Rate %lu missing; compute N dynamically\n",
+		 pixel_clk);
+
+	return hdmi_compute_n(hdmi, pixel_clk, sample_rate);
 }
 
 /*
@@ -654,7 +956,7 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
 	u8 config3;
 	u64 tmp;
 
-	n = hdmi_compute_n(sample_rate, pixel_clk);
+	n = hdmi_find_n(hdmi, pixel_clk, sample_rate);
 
 	config3 = hdmi_readb(hdmi, HDMI_CONFIG3_ID);
 
@@ -756,14 +1058,6 @@ static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi, bool enable)
 	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 }
 
-static u8 *hdmi_audio_get_eld(struct dw_hdmi *hdmi)
-{
-	if (!hdmi->curr_conn)
-		return NULL;
-
-	return hdmi->curr_conn->eld;
-}
-
 static void dw_hdmi_ahb_audio_enable(struct dw_hdmi *hdmi)
 {
 	hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
@@ -1013,6 +1307,15 @@ static bool is_csc_needed(struct dw_hdmi *hdmi)
 	       is_color_space_interpolation(hdmi);
 }
 
+static bool is_rgb_full_to_limited_needed(struct dw_hdmi *hdmi)
+{
+	if (hdmi->hdmi_data.quant_range == HDMI_QUANTIZATION_RANGE_LIMITED ||
+	    (!hdmi->hdmi_data.quant_range && hdmi->hdmi_data.rgb_limited_range))
+		return true;
+
+	return false;
+}
+
 static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi)
 {
 	const u16 (*csc_coeff)[3][4] = &csc_coeff_default;
@@ -1035,7 +1338,7 @@ static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi)
 			csc_coeff = &csc_coeff_rgb_in_eitu709;
 		csc_scale = 0;
 	} else if (is_input_rgb && is_output_rgb &&
-		   hdmi->hdmi_data.rgb_limited_range) {
+		   is_rgb_full_to_limited_needed(hdmi)) {
 		csc_coeff = &csc_coeff_rgb_full_to_rgb_limited;
 	}
 
@@ -1067,7 +1370,7 @@ static void hdmi_video_csc(struct dw_hdmi *hdmi)
 	if (is_color_space_interpolation(hdmi))
 		interpolation = HDMI_CSC_CFG_INTMODE_CHROMA_INT_FORMULA1;
 	else if (is_color_space_decimation(hdmi))
-		decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3;
+		decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA1;
 
 	switch (hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format)) {
 	case 8:
@@ -1114,7 +1417,7 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)
 		switch (hdmi_bus_fmt_color_depth(
 					hdmi->hdmi_data.enc_out_bus_format)) {
 		case 8:
-			color_depth = 4;
+			color_depth = 0;
 			output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
 			break;
 		case 10:
@@ -1152,18 +1455,15 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)
 	}
 
 	/* set the packetizer registers */
-	val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) &
-		HDMI_VP_PR_CD_COLOR_DEPTH_MASK) |
-		((hdmi_data->pix_repet_factor <<
-		HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET) &
-		HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK);
+	val = (color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) &
+	      HDMI_VP_PR_CD_COLOR_DEPTH_MASK;
 	hdmi_writeb(hdmi, val, HDMI_VP_PR_CD);
 
 	hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE,
 		  HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF);
 
 	/* Data from pixel repeater block */
-	if (hdmi_data->pix_repet_factor > 1) {
+	if (hdmi_data->pix_repet_factor > 0) {
 		vp_conf = HDMI_VP_CONF_PR_EN_ENABLE |
 			  HDMI_VP_CONF_BYPASS_SELECT_PIX_REPEATER;
 	} else { /* data from packetizer block */
@@ -1175,8 +1475,13 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)
 		  HDMI_VP_CONF_PR_EN_MASK |
 		  HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF);
 
-	hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET,
-		  HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF);
+	if ((color_depth == 5 && hdmi->previous_mode.htotal % 4) ||
+	    (color_depth == 6 && hdmi->previous_mode.htotal % 2))
+		hdmi_modb(hdmi, 0, HDMI_VP_STUFF_IDEFAULT_PHASE_MASK,
+			  HDMI_VP_STUFF);
+	else
+		hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET,
+			HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF);
 
 	hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP);
 
@@ -1277,6 +1582,23 @@ static bool dw_hdmi_support_scdc(struct dw_hdmi *hdmi,
 	return true;
 }
 
+static int hdmi_phy_i2c_read(struct dw_hdmi *hdmi, unsigned char addr)
+{
+	int val;
+
+	hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
+	hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
+	hdmi_writeb(hdmi, 0, HDMI_PHY_I2CM_DATAI_1_ADDR);
+	hdmi_writeb(hdmi, 0, HDMI_PHY_I2CM_DATAI_0_ADDR);
+	hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_READ,
+		    HDMI_PHY_I2CM_OPERATION_ADDR);
+	hdmi_phy_wait_i2c_done(hdmi, 1000);
+	val = hdmi_readb(hdmi, HDMI_PHY_I2CM_DATAI_1_ADDR);
+	val = (val & 0xff) << 8;
+	val += hdmi_readb(hdmi, HDMI_PHY_I2CM_DATAI_0_ADDR) & 0xff;
+	return val;
+}
+
 /*
  * HDMI2.0 Specifies the following procedure for High TMDS Bit Rates:
  * - The Source shall suspend transmission of the TMDS clock and data
@@ -1454,6 +1776,13 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
 	const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
 	const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
 	const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
+	unsigned int tmdsclock = hdmi->hdmi_data.video_mode.mtmdsclock;
+	unsigned int depth =
+		hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
+
+	if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format) &&
+	    pdata->mpll_cfg_420)
+		mpll_config = pdata->mpll_cfg_420;
 
 	/* TOFIX Will need 420 specific PHY configuration tables */
 
@@ -1463,11 +1792,11 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
 			break;
 
 	for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++)
-		if (mpixelclock <= curr_ctrl->mpixelclock)
+		if (tmdsclock <= curr_ctrl->mpixelclock)
 			break;
 
 	for (; phy_config->mpixelclock != ~0UL; phy_config++)
-		if (mpixelclock <= phy_config->mpixelclock)
+		if (tmdsclock <= phy_config->mpixelclock)
 			break;
 
 	if (mpll_config->mpixelclock == ~0UL ||
@@ -1475,11 +1804,18 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
 	    phy_config->mpixelclock == ~0UL)
 		return -EINVAL;
 
-	dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[0].cpce,
+	if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
+		depth = fls(depth - 8);
+	else
+		depth = 0;
+	if (depth)
+		depth--;
+
+	dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].cpce,
 			      HDMI_3D_TX_PHY_CPCE_CTRL);
-	dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[0].gmp,
+	dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[depth].gmp,
 			      HDMI_3D_TX_PHY_GMPCTRL);
-	dw_hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[0],
+	dw_hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[depth],
 			      HDMI_3D_TX_PHY_CURRCTRL);
 
 	dw_hdmi_phy_i2c_write(hdmi, 0, HDMI_3D_TX_PHY_PLLPHBYCTRL);
@@ -1492,10 +1828,6 @@ static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
 	dw_hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr,
 			      HDMI_3D_TX_PHY_VLEVCTRL);
 
-	/* Override and disable clock termination. */
-	dw_hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_CKCALCTRL_OVERRIDE,
-			      HDMI_3D_TX_PHY_CKCALCTRL);
-
 	return 0;
 }
 
@@ -1597,14 +1929,16 @@ void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data)
 	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
 		    HDMI_IH_PHY_STAT0);
 
-	/* Enable cable hot plug irq. */
-	hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
+	if (!hdmi->next_bridge) {
+		/* Enable cable hot plug irq. */
+		hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
 
-	/* Clear and unmute interrupts. */
-	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
-		    HDMI_IH_PHY_STAT0);
-	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
-		    HDMI_IH_MUTE_PHY_STAT0);
+		/* Clear and unmute interrupts. */
+		hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE,
+			    HDMI_IH_PHY_STAT0);
+		hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
+			    HDMI_IH_MUTE_PHY_STAT0);
+	}
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_phy_setup_hpd);
 
@@ -1620,23 +1954,36 @@ static const struct dw_hdmi_phy_ops dw_hdmi_synopsys_phy_ops = {
  * HDMI TX Setup
  */
 
-static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi)
+static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi,
+				const struct drm_display_mode *mode)
 {
-	u8 de;
-
-	if (hdmi->hdmi_data.video_mode.mdataenablepolarity)
-		de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH;
-	else
-		de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW;
-
-	/* disable rx detect */
-	hdmi_modb(hdmi, HDMI_A_HDCPCFG0_RXDETECT_DISABLE,
-		  HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0);
-
-	hdmi_modb(hdmi, de, HDMI_A_VIDPOLCFG_DATAENPOL_MASK, HDMI_A_VIDPOLCFG);
-
-	hdmi_modb(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE,
-		  HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK, HDMI_A_HDCPCFG1);
+	struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
+	u8 vsync_pol, hsync_pol, data_pol, hdmi_dvi;
+
+	/* Configure the video polarity */
+	vsync_pol = mode->flags & DRM_MODE_FLAG_PVSYNC ?
+		    HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_HIGH :
+		    HDMI_A_VIDPOLCFG_VSYNCPOL_ACTIVE_LOW;
+	hsync_pol = mode->flags & DRM_MODE_FLAG_PHSYNC ?
+		    HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_HIGH :
+		    HDMI_A_VIDPOLCFG_HSYNCPOL_ACTIVE_LOW;
+	data_pol = vmode->mdataenablepolarity ?
+		    HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_HIGH :
+		    HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW;
+	hdmi_modb(hdmi, vsync_pol | hsync_pol | data_pol,
+		  HDMI_A_VIDPOLCFG_VSYNCPOL_MASK |
+		  HDMI_A_VIDPOLCFG_HSYNCPOL_MASK |
+		  HDMI_A_VIDPOLCFG_DATAENPOL_MASK,
+		  HDMI_A_VIDPOLCFG);
+
+	/* Config the display mode */
+	hdmi_dvi = hdmi->sink_is_hdmi ? HDMI_A_HDCPCFG0_HDMIDVI_HDMI :
+		   HDMI_A_HDCPCFG0_HDMIDVI_DVI;
+	hdmi_modb(hdmi, hdmi_dvi, HDMI_A_HDCPCFG0_HDMIDVI_MASK,
+		  HDMI_A_HDCPCFG0);
+
+	if (hdmi->hdcp && hdmi->hdcp->hdcp_start)
+		hdmi->hdcp->hdcp_start(hdmi->hdcp);
 }
 
 static void hdmi_config_AVI(struct dw_hdmi *hdmi,
@@ -1650,10 +1997,15 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi,
 	drm_hdmi_avi_infoframe_from_display_mode(&frame, connector, mode);
 
 	if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) {
-		drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode,
-						   hdmi->hdmi_data.rgb_limited_range ?
-						   HDMI_QUANTIZATION_RANGE_LIMITED :
-						   HDMI_QUANTIZATION_RANGE_FULL);
+		/* default range */
+		if (!hdmi->hdmi_data.quant_range)
+			drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode,
+							   hdmi->hdmi_data.rgb_limited_range ?
+							   HDMI_QUANTIZATION_RANGE_LIMITED :
+							   HDMI_QUANTIZATION_RANGE_FULL);
+		else
+			drm_hdmi_avi_infoframe_quant_range(&frame, connector, mode,
+							   hdmi->hdmi_data.quant_range);
 	} else {
 		frame.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
 		frame.ycc_quantization_range =
@@ -1688,6 +2040,14 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi,
 			frame.extended_colorimetry =
 					HDMI_EXTENDED_COLORIMETRY_XV_YCC_709;
 			break;
+		case V4L2_YCBCR_ENC_BT2020:
+			if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_BT2020)
+				frame.colorimetry = HDMI_COLORIMETRY_EXTENDED;
+			else
+				frame.colorimetry = HDMI_COLORIMETRY_ITU_709;
+			frame.extended_colorimetry =
+				HDMI_EXTENDED_COLORIMETRY_BT2020;
+		break;
 		default: /* Carries no data */
 			frame.colorimetry = HDMI_COLORIMETRY_ITU_601;
 			frame.extended_colorimetry =
@@ -1824,17 +2184,44 @@ static void hdmi_config_drm_infoframe(struct dw_hdmi *hdmi,
 				      const struct drm_connector *connector)
 {
 	const struct drm_connector_state *conn_state = connector->state;
+	struct hdr_output_metadata *hdr_metadata;
 	struct hdmi_drm_infoframe frame;
 	u8 buffer[30];
 	ssize_t err;
 	int i;
 
+	/* Dynamic Range and Mastering Infoframe is introduced in v2.11a. */
+	if (hdmi->version < 0x211a) {
+		DRM_ERROR("Not support DRM Infoframe\n");
+		return;
+	}
+
 	if (!hdmi->plat_data->use_drm_infoframe)
 		return;
 
 	hdmi_modb(hdmi, HDMI_FC_PACKET_TX_EN_DRM_DISABLE,
 		  HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_EN);
 
+	if (!hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf) {
+		DRM_DEBUG("No need to set HDR metadata in infoframe\n");
+		return;
+	}
+
+	if (!conn_state->hdr_output_metadata) {
+		DRM_DEBUG("source metadata not set yet\n");
+		return;
+	}
+
+	hdr_metadata = (struct hdr_output_metadata *)
+		conn_state->hdr_output_metadata->data;
+
+	if (!(hdmi->connector.hdr_sink_metadata.hdmi_type1.eotf &
+	    BIT(hdr_metadata->hdmi_metadata_type1.eotf))) {
+		DRM_ERROR("Not support EOTF %d\n",
+			  hdr_metadata->hdmi_metadata_type1.eotf);
+		return;
+	}
+
 	err = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state);
 	if (err < 0)
 		return;
@@ -1854,51 +2241,66 @@ static void hdmi_config_drm_infoframe(struct dw_hdmi *hdmi,
 	hdmi_writeb(hdmi, 1, HDMI_FC_DRM_UP);
 	hdmi_modb(hdmi, HDMI_FC_PACKET_TX_EN_DRM_ENABLE,
 		  HDMI_FC_PACKET_TX_EN_DRM_MASK, HDMI_FC_PACKET_TX_EN);
+
+	DRM_DEBUG("%s eotf %d end\n", __func__,
+		  hdr_metadata->hdmi_metadata_type1.eotf);
 }
 
-static void hdmi_av_composer(struct dw_hdmi *hdmi,
-			     const struct drm_display_info *display,
-			     const struct drm_display_mode *mode)
+static unsigned int
+hdmi_get_tmdsclock(struct dw_hdmi *hdmi, unsigned long mpixelclock)
 {
-	u8 inv_val, bytes;
-	const struct drm_hdmi_info *hdmi_info = &display->hdmi;
-	struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
-	int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
-	unsigned int vdisplay, hdisplay;
-
-	vmode->mpixelclock = mode->clock * 1000;
-
-	dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock);
-
-	vmode->mtmdsclock = vmode->mpixelclock;
+	unsigned int tmdsclock = mpixelclock;
+	unsigned int depth =
+		hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
 
 	if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) {
-		switch (hdmi_bus_fmt_color_depth(
-				hdmi->hdmi_data.enc_out_bus_format)) {
+		switch (depth) {
 		case 16:
-			vmode->mtmdsclock = vmode->mpixelclock * 2;
+			tmdsclock = mpixelclock * 2;
 			break;
 		case 12:
-			vmode->mtmdsclock = vmode->mpixelclock * 3 / 2;
+			tmdsclock = mpixelclock * 3 / 2;
 			break;
 		case 10:
-			vmode->mtmdsclock = vmode->mpixelclock * 5 / 4;
+			tmdsclock = mpixelclock * 5 / 4;
+			break;
+		default:
 			break;
 		}
 	}
 
+	return tmdsclock;
+}
+
+static void hdmi_av_composer(struct dw_hdmi *hdmi,
+			     const struct drm_display_info *display,
+			     const struct drm_display_mode *mode)
+{
+	u8 inv_val, bytes;
+	const struct drm_hdmi_info *hdmi_info = &display->hdmi;
+	struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
+	int hblank, vblank, h_de_hs, v_de_vs, hsync_len, vsync_len;
+	unsigned int vdisplay, hdisplay;
+
+	vmode->previous_pixelclock = vmode->mpixelclock;
+	vmode->mpixelclock = mode->crtc_clock * 1000;
+	if ((mode->flags & DRM_MODE_FLAG_3D_MASK) ==
+		DRM_MODE_FLAG_3D_FRAME_PACKING)
+		vmode->mpixelclock *= 2;
+	dev_dbg(hdmi->dev, "final pixclk = %d\n", vmode->mpixelclock);
+
+	vmode->previous_tmdsclock = vmode->mtmdsclock;
+	vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi, vmode->mpixelclock);
 	if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
 		vmode->mtmdsclock /= 2;
-
 	dev_dbg(hdmi->dev, "final tmdsclock = %d\n", vmode->mtmdsclock);
 
-	/* Set up HDMI_FC_INVIDCONF */
-	inv_val = (hdmi->hdmi_data.hdcp_enable ||
-		   (dw_hdmi_support_scdc(hdmi, display) &&
-		    (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
-		     hdmi_info->scdc.scrambling.low_rates)) ?
-		HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE :
-		HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE);
+	/* Set up HDMI_FC_INVIDCONF
+	 * Some display equipments require that the interval
+	 * between Video Data and Data island must be at least 58 pixels,
+	 * and fc_invidconf.HDCP_keepout set (1'b1) can meet the requirement.
+	 */
+	inv_val = HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE;
 
 	inv_val |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
 		HDMI_FC_INVIDCONF_VSYNC_IN_POLARITY_ACTIVE_HIGH :
@@ -1964,7 +2366,8 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
 	/* Scrambling Control */
 	if (dw_hdmi_support_scdc(hdmi, display)) {
 		if (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
-		    hdmi_info->scdc.scrambling.low_rates) {
+		    (hdmi_info->scdc.scrambling.low_rates &&
+		     hdmi->scramble_low_rates)) {
 			/*
 			 * HDMI2.0 Specifies the following procedure:
 			 * After the Source Device has determined that
@@ -1998,6 +2401,8 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
 				    HDMI_MC_SWRSTZ);
 			drm_scdc_set_scrambling(hdmi->ddc, 0);
 		}
+	} else {
+		hdmi_writeb(hdmi, 0, HDMI_FC_SCRAMBLER_CTRL);
 	}
 
 	/* Set up horizontal active pixel width */
@@ -2055,6 +2460,12 @@ static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi)
 	hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
 	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 
+	/* Enable pixel repetition path */
+	if (hdmi->hdmi_data.video_mode.mpixelrepetitioninput) {
+		hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_PREPCLK_DISABLE;
+		hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+	}
+
 	/* Enable csc path */
 	if (is_csc_needed(hdmi)) {
 		hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
@@ -2130,6 +2541,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi,
 			 const struct drm_display_mode *mode)
 {
 	int ret;
+	void *data = hdmi->plat_data->phy_data;
 
 	hdmi_disable_overflow_interrupts(hdmi);
 
@@ -2141,48 +2553,91 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi,
 		dev_dbg(hdmi->dev, "CEA mode used vic=%d\n", hdmi->vic);
 	}
 
-	if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
-	    (hdmi->vic == 21) || (hdmi->vic == 22) ||
-	    (hdmi->vic == 2) || (hdmi->vic == 3) ||
-	    (hdmi->vic == 17) || (hdmi->vic == 18))
+	if (hdmi->plat_data->get_enc_out_encoding)
+		hdmi->hdmi_data.enc_out_encoding =
+			hdmi->plat_data->get_enc_out_encoding(data);
+	else if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
+		 (hdmi->vic == 21) || (hdmi->vic == 22) ||
+		 (hdmi->vic == 2) || (hdmi->vic == 3) ||
+		 (hdmi->vic == 17) || (hdmi->vic == 18))
 		hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_601;
 	else
 		hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_709;
 
-	hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
-	hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0;
+	if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
+		hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1;
+		hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 1;
+	} else {
+		hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
+		hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0;
+	}
+	/* TOFIX: Get input format from plat data or fallback to RGB888 */
+	if (hdmi->plat_data->get_input_bus_format)
+		hdmi->hdmi_data.enc_in_bus_format =
+			hdmi->plat_data->get_input_bus_format(data);
+	else if (hdmi->plat_data->input_bus_format)
+		hdmi->hdmi_data.enc_in_bus_format =
+			hdmi->plat_data->input_bus_format;
+	else
+		hdmi->hdmi_data.enc_in_bus_format =
+			MEDIA_BUS_FMT_RGB888_1X24;
 
-	if (hdmi->hdmi_data.enc_in_bus_format == MEDIA_BUS_FMT_FIXED)
-		hdmi->hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+	/* TOFIX: Default to RGB888 output format */
+	if (hdmi->plat_data->get_output_bus_format)
+		hdmi->hdmi_data.enc_out_bus_format =
+			hdmi->plat_data->get_output_bus_format(data);
+	else
+		hdmi->hdmi_data.enc_out_bus_format =
+			MEDIA_BUS_FMT_RGB888_1X24;
 
 	/* TOFIX: Get input encoding from plat data or fallback to none */
-	if (hdmi->plat_data->input_bus_encoding)
+	if (hdmi->plat_data->get_enc_in_encoding)
+		hdmi->hdmi_data.enc_in_encoding =
+			hdmi->plat_data->get_enc_in_encoding(data);
+	else if (hdmi->plat_data->input_bus_encoding)
 		hdmi->hdmi_data.enc_in_encoding =
 			hdmi->plat_data->input_bus_encoding;
 	else
 		hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT;
 
-	if (hdmi->hdmi_data.enc_out_bus_format == MEDIA_BUS_FMT_FIXED)
-		hdmi->hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+
+	if (hdmi->plat_data->get_quant_range)
+		hdmi->hdmi_data.quant_range =
+			hdmi->plat_data->get_quant_range(data);
 
 	hdmi->hdmi_data.rgb_limited_range = hdmi->sink_is_hdmi &&
 		drm_default_rgb_quant_range(mode) ==
 		HDMI_QUANTIZATION_RANGE_LIMITED;
 
-	hdmi->hdmi_data.pix_repet_factor = 0;
-	hdmi->hdmi_data.hdcp_enable = 0;
+	if (!hdmi->sink_is_hdmi)
+		hdmi->hdmi_data.quant_range = HDMI_QUANTIZATION_RANGE_FULL;
+
+	/*
+	 * According to the dw-hdmi specification 6.4.2
+	 * vp_pr_cd[3:0]:
+	 * 0000b: No pixel repetition (pixel sent only once)
+	 * 0001b: Pixel sent two times (pixel repeated once)
+	 */
+	hdmi->hdmi_data.pix_repet_factor =
+		(mode->flags & DRM_MODE_FLAG_DBLCLK) ? 1 : 0;
 	hdmi->hdmi_data.video_mode.mdataenablepolarity = true;
 
 	/* HDMI Initialization Step B.1 */
 	hdmi_av_composer(hdmi, &connector->display_info, mode);
 
 	/* HDMI Initializateion Step B.2 */
-	ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data,
-				  &connector->display_info,
-				  &hdmi->previous_mode);
-	if (ret)
-		return ret;
-	hdmi->phy.enabled = true;
+	if (!hdmi->phy.enabled ||
+	    hdmi->hdmi_data.video_mode.previous_pixelclock !=
+	    hdmi->hdmi_data.video_mode.mpixelclock ||
+	    hdmi->hdmi_data.video_mode.previous_tmdsclock !=
+	    hdmi->hdmi_data.video_mode.mtmdsclock) {
+		ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data,
+					  &connector->display_info,
+					  &hdmi->previous_mode);
+		if (ret)
+			return ret;
+		hdmi->phy.enabled = true;
+	}
 
 	/* HDMI Initialization Step B.3 */
 	dw_hdmi_enable_video_path(hdmi);
@@ -2210,7 +2665,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi,
 	hdmi_video_packetize(hdmi);
 	hdmi_video_csc(hdmi);
 	hdmi_video_sample(hdmi);
-	hdmi_tx_hdcp_config(hdmi);
+	hdmi_tx_hdcp_config(hdmi, mode);
 
 	dw_hdmi_clear_overflow(hdmi);
 
@@ -2286,6 +2741,8 @@ static void dw_hdmi_poweroff(struct dw_hdmi *hdmi)
 		hdmi->phy.enabled = false;
 	}
 
+	if (hdmi->hdcp && hdmi->hdcp->hdcp_stop)
+		hdmi->hdcp->hdcp_stop(hdmi->hdcp);
 	hdmi->bridge_is_on = false;
 }
 
@@ -2303,6 +2760,10 @@ static void dw_hdmi_update_power(struct dw_hdmi *hdmi)
 	}
 
 	if (force == DRM_FORCE_OFF) {
+		if (hdmi->initialized) {
+			hdmi->initialized = false;
+			hdmi->disabled = true;
+		}
 		if (hdmi->bridge_is_on)
 			dw_hdmi_poweroff(hdmi);
 	} else {
@@ -2335,8 +2796,15 @@ static enum drm_connector_status dw_hdmi_detect(struct dw_hdmi *hdmi)
 {
 	enum drm_connector_status result;
 
-	result = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
+	if (!hdmi->force_logo) {
+		mutex_lock(&hdmi->mutex);
+		hdmi->force = DRM_FORCE_UNSPECIFIED;
+		dw_hdmi_update_power(hdmi);
+		dw_hdmi_update_phy_mask(hdmi);
+		mutex_unlock(&hdmi->mutex);
+	}
 
+	result = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
 	mutex_lock(&hdmi->mutex);
 	if (result != hdmi->last_connector_result) {
 		dev_dbg(hdmi->dev, "read_hpd result: %d", result);
@@ -2346,6 +2814,11 @@ static enum drm_connector_status dw_hdmi_detect(struct dw_hdmi *hdmi)
 	}
 	mutex_unlock(&hdmi->mutex);
 
+	if (result == connector_status_connected)
+		extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, true);
+	else
+		extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, false);
+
 	return result;
 }
 
@@ -2366,7 +2839,7 @@ static struct edid *dw_hdmi_get_edid(struct dw_hdmi *hdmi,
 	dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
 		edid->width_cm, edid->height_cm);
 
-	hdmi->sink_is_hdmi = drm_detect_hdmi_monitor(edid);
+	hdmi->support_hdmi = drm_detect_hdmi_monitor(edid);
 	hdmi->sink_has_audio = drm_detect_monitor_audio(edid);
 
 	return edid;
@@ -2384,21 +2857,105 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
 	return dw_hdmi_detect(hdmi);
 }
 
+static int
+dw_hdmi_update_hdr_property(struct drm_connector *connector)
+{
+	struct drm_device *dev = connector->dev;
+	struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+					    connector);
+	void *data = hdmi->plat_data->phy_data;
+	const struct hdr_static_metadata *metadata =
+		&connector->hdr_sink_metadata.hdmi_type1;
+	size_t size = sizeof(*metadata);
+	struct drm_property *property;
+	struct drm_property_blob *blob;
+	int ret;
+
+	if (hdmi->plat_data->get_hdr_property)
+		property = hdmi->plat_data->get_hdr_property(data);
+	else
+		return -EINVAL;
+
+	if (hdmi->plat_data->get_hdr_blob)
+		blob = hdmi->plat_data->get_hdr_blob(data);
+	else
+		return -EINVAL;
+
+	ret = drm_property_replace_global_blob(dev, &blob, size, metadata,
+					       &connector->base, property);
+	return ret;
+}
+
 static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
 {
 	struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
 					     connector);
+	struct hdr_static_metadata *metedata =
+			&connector->hdr_sink_metadata.hdmi_type1;
 	struct edid *edid;
-	int ret;
+	struct drm_display_mode *mode;
+	struct drm_display_info *info = &connector->display_info;
+	int i,  ret = 0;
 
+	memset(metedata, 0, sizeof(*metedata));
 	edid = dw_hdmi_get_edid(hdmi, connector);
-	if (!edid)
-		return 0;
+	if (edid) {
+		dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
+			edid->width_cm, edid->height_cm);
+		drm_connector_update_edid_property(connector, edid);
+		cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid);
+		ret = drm_add_edid_modes(connector, edid);
+		if (hdmi->plat_data->get_color_changed)
+			hdmi->plat_data->get_yuv422_format(connector, edid);
+		dw_hdmi_update_hdr_property(connector);
+		kfree(edid);
+	} else {
+		hdmi->support_hdmi = true;
+		hdmi->sink_has_audio = true;
+		for (i = 0; i < ARRAY_SIZE(dw_hdmi_default_modes); i++) {
+			const struct drm_display_mode *ptr =
+				&dw_hdmi_default_modes[i];
+
+			mode = drm_mode_duplicate(connector->dev, ptr);
+			if (mode) {
+				if (!i) {
+					mode->type = DRM_MODE_TYPE_PREFERRED;
+					mode->picture_aspect_ratio =
+						HDMI_PICTURE_ASPECT_NONE;
+				}
+				drm_mode_probed_add(connector, mode);
+				ret++;
+			}
+		}
+		info->edid_hdmi_dc_modes = 0;
+		info->hdmi.y420_dc_modes = 0;
+		info->color_formats = 0;
+
+		dev_info(hdmi->dev, "failed to get edid\n");
+	}
+	dw_hdmi_check_output_type_changed(hdmi);
+
+	return ret;
+}
+
+static struct drm_encoder *
+dw_hdmi_connector_best_encoder(struct drm_connector *connector)
+{
+	struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+					    connector);
+
+	return hdmi->bridge.encoder;
+}
+
+static bool dw_hdmi_color_changed(struct drm_connector *connector)
+{
+	struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+					    connector);
+	void *data = hdmi->plat_data->phy_data;
+	bool ret = false;
 
-	drm_connector_update_edid_property(connector, edid);
-	cec_notifier_set_phys_addr_from_edid(hdmi->cec_notifier, edid);
-	ret = drm_add_edid_modes(connector, edid);
-	kfree(edid);
+	if (hdmi->plat_data->get_color_changed)
+		ret = hdmi->plat_data->get_color_changed(data);
 
 	return ret;
 }
@@ -2427,11 +2984,54 @@ static int dw_hdmi_connector_atomic_check(struct drm_connector *connector,
 		drm_atomic_get_new_connector_state(state, connector);
 	struct drm_crtc *crtc = new_state->crtc;
 	struct drm_crtc_state *crtc_state;
+	struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+					    connector);
+	struct drm_display_mode *mode = NULL;
+	void *data = hdmi->plat_data->phy_data;
+	struct hdmi_vmode *vmode = &hdmi->hdmi_data.video_mode;
+	unsigned int in_bus_format = hdmi->hdmi_data.enc_in_bus_format;
+	unsigned int out_bus_format = hdmi->hdmi_data.enc_out_bus_format;
+	bool color_changed = false;
 
 	if (!crtc)
 		return 0;
 
-	if (!hdr_metadata_equal(old_state, new_state)) {
+	/*
+	 * If HDMI is enabled in uboot, it's need to record
+	 * drm_display_mode and set phy status to enabled.
+	 */
+	if (!vmode->mpixelclock) {
+		crtc_state = drm_atomic_get_crtc_state(state, crtc);
+		if (hdmi->plat_data->get_enc_in_encoding)
+			hdmi->hdmi_data.enc_in_encoding =
+				hdmi->plat_data->get_enc_in_encoding(data);
+		if (hdmi->plat_data->get_enc_out_encoding)
+			hdmi->hdmi_data.enc_out_encoding =
+				hdmi->plat_data->get_enc_out_encoding(data);
+		if (hdmi->plat_data->get_input_bus_format)
+			hdmi->hdmi_data.enc_in_bus_format =
+				hdmi->plat_data->get_input_bus_format(data);
+		if (hdmi->plat_data->get_output_bus_format)
+			hdmi->hdmi_data.enc_out_bus_format =
+				hdmi->plat_data->get_output_bus_format(data);
+
+		mode = &crtc_state->mode;
+		memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
+		vmode->mpixelclock = mode->crtc_clock * 1000;
+		vmode->previous_pixelclock = mode->clock;
+		vmode->previous_tmdsclock = mode->clock;
+		vmode->mtmdsclock = hdmi_get_tmdsclock(hdmi,
+						       vmode->mpixelclock);
+		if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
+			vmode->mtmdsclock /= 2;
+
+		if (in_bus_format != hdmi->hdmi_data.enc_in_bus_format ||
+		    out_bus_format != hdmi->hdmi_data.enc_out_bus_format)
+			color_changed = true;
+	}
+
+	if (!hdr_metadata_equal(old_state, new_state) ||
+	    dw_hdmi_color_changed(connector) || color_changed) {
 		crtc_state = drm_atomic_get_crtc_state(state, crtc);
 		if (IS_ERR(crtc_state))
 			return PTR_ERR(crtc_state);
@@ -2442,15 +3042,108 @@ static int dw_hdmi_connector_atomic_check(struct drm_connector *connector,
 	return 0;
 }
 
-static void dw_hdmi_connector_force(struct drm_connector *connector)
+static int
+dw_hdmi_atomic_connector_set_property(struct drm_connector *connector,
+				      struct drm_connector_state *state,
+				      struct drm_property *property,
+				      uint64_t val)
 {
 	struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
 					     connector);
+	const struct dw_hdmi_property_ops *ops =
+				hdmi->plat_data->property_ops;
 
-	mutex_lock(&hdmi->mutex);
-	hdmi->force = connector->force;
-	dw_hdmi_update_power(hdmi);
-	dw_hdmi_update_phy_mask(hdmi);
+	if (ops && ops->set_property)
+		return ops->set_property(connector, state, property,
+					 val, hdmi->plat_data->phy_data);
+	else
+		return -EINVAL;
+}
+
+static int
+dw_hdmi_atomic_connector_get_property(struct drm_connector *connector,
+				      const struct drm_connector_state *state,
+				      struct drm_property *property,
+				      uint64_t *val)
+{
+	struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+					     connector);
+	const struct dw_hdmi_property_ops *ops =
+				hdmi->plat_data->property_ops;
+
+	if (ops && ops->get_property)
+		return ops->get_property(connector, state, property,
+					 val, hdmi->plat_data->phy_data);
+	else
+		return -EINVAL;
+}
+
+static int
+dw_hdmi_connector_set_property(struct drm_connector *connector,
+			       struct drm_property *property, uint64_t val)
+{
+	return dw_hdmi_atomic_connector_set_property(connector, NULL,
+						     property, val);
+}
+
+void dw_hdmi_set_quant_range(struct dw_hdmi *hdmi)
+{
+	if (!hdmi->bridge_is_on)
+		return;
+
+	hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP);
+	dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode);
+	hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_set_quant_range);
+
+void dw_hdmi_set_output_type(struct dw_hdmi *hdmi, u64 val)
+{
+	hdmi->force_output = val;
+
+	if (!dw_hdmi_check_output_type_changed(hdmi))
+		return;
+
+	if (!hdmi->bridge_is_on)
+		return;
+
+	hdmi_writeb(hdmi, HDMI_FC_GCP_SET_AVMUTE, HDMI_FC_GCP);
+	dw_hdmi_setup(hdmi, hdmi->curr_conn, &hdmi->previous_mode);
+	hdmi_writeb(hdmi, HDMI_FC_GCP_CLEAR_AVMUTE, HDMI_FC_GCP);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_set_output_type);
+
+bool dw_hdmi_get_output_whether_hdmi(struct dw_hdmi *hdmi)
+{
+	return hdmi->sink_is_hdmi;
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_get_output_whether_hdmi);
+
+int dw_hdmi_get_output_type_cap(struct dw_hdmi *hdmi)
+{
+	return hdmi->support_hdmi;
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_get_output_type_cap);
+
+static void dw_hdmi_connector_force(struct drm_connector *connector)
+{
+	struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
+					     connector);
+
+	mutex_lock(&hdmi->mutex);
+
+	if (hdmi->force != connector->force) {
+		if (!hdmi->disabled && connector->force == DRM_FORCE_OFF)
+			extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI,
+					      false);
+		else if (hdmi->disabled && connector->force == DRM_FORCE_ON)
+			extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI,
+					      true);
+	}
+
+	hdmi->force = connector->force;
+	dw_hdmi_update_power(hdmi);
+	dw_hdmi_update_phy_mask(hdmi);
 	mutex_unlock(&hdmi->mutex);
 }
 
@@ -2460,15 +3153,98 @@ static const struct drm_connector_funcs dw_hdmi_connector_funcs = {
 	.destroy = drm_connector_cleanup,
 	.force = dw_hdmi_connector_force,
 	.reset = drm_atomic_helper_connector_reset,
+	.set_property = dw_hdmi_connector_set_property,
 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+	.atomic_set_property = dw_hdmi_atomic_connector_set_property,
+	.atomic_get_property = dw_hdmi_atomic_connector_get_property,
 };
 
 static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = {
 	.get_modes = dw_hdmi_connector_get_modes,
+	.best_encoder = dw_hdmi_connector_best_encoder,
 	.atomic_check = dw_hdmi_connector_atomic_check,
 };
 
+static void dw_hdmi_attach_properties(struct dw_hdmi *hdmi)
+{
+	unsigned int color = MEDIA_BUS_FMT_RGB888_1X24;
+	int video_mapping, colorspace;
+	enum drm_connector_status connect_status =
+		hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
+	const struct dw_hdmi_property_ops *ops =
+				hdmi->plat_data->property_ops;
+
+	if (connect_status == connector_status_connected) {
+		video_mapping = (hdmi_readb(hdmi, HDMI_TX_INVID0) &
+				  HDMI_TX_INVID0_VIDEO_MAPPING_MASK);
+		colorspace = (hdmi_readb(hdmi, HDMI_FC_AVICONF0) &
+			      HDMI_FC_AVICONF0_PIX_FMT_MASK);
+		switch (video_mapping) {
+		case 0x01:
+			color = MEDIA_BUS_FMT_RGB888_1X24;
+			break;
+		case 0x03:
+			color = MEDIA_BUS_FMT_RGB101010_1X30;
+			break;
+		case 0x09:
+			if (colorspace == HDMI_COLORSPACE_YUV420)
+				color = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
+			else if (colorspace == HDMI_COLORSPACE_YUV422)
+				color = MEDIA_BUS_FMT_UYVY8_1X16;
+			else
+				color = MEDIA_BUS_FMT_YUV8_1X24;
+			break;
+		case 0x0b:
+			if (colorspace == HDMI_COLORSPACE_YUV420)
+				color = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
+			else if (colorspace == HDMI_COLORSPACE_YUV422)
+				color = MEDIA_BUS_FMT_UYVY10_1X20;
+			else
+				color = MEDIA_BUS_FMT_YUV10_1X30;
+			break;
+		case 0x14:
+			color = MEDIA_BUS_FMT_UYVY10_1X20;
+			break;
+		case 0x16:
+			color = MEDIA_BUS_FMT_UYVY8_1X16;
+			break;
+		default:
+			color = MEDIA_BUS_FMT_RGB888_1X24;
+			dev_err(hdmi->dev, "unexpected mapping: 0x%x\n",
+				video_mapping);
+		}
+
+		hdmi->hdmi_data.enc_in_bus_format = color;
+		hdmi->hdmi_data.enc_out_bus_format = color;
+		/*
+		 * input format will be set as yuv444 when output
+		 * format is yuv420
+		 */
+		if (color == MEDIA_BUS_FMT_UYVY10_1X20)
+			hdmi->hdmi_data.enc_in_bus_format =
+				MEDIA_BUS_FMT_YUV10_1X30;
+		else if (color == MEDIA_BUS_FMT_UYVY8_1X16)
+			hdmi->hdmi_data.enc_in_bus_format =
+				MEDIA_BUS_FMT_YUV8_1X24;
+	}
+
+	if (ops && ops->attach_properties)
+		return ops->attach_properties(&hdmi->connector,
+					      color, hdmi->version,
+					      hdmi->plat_data->phy_data);
+}
+
+static void dw_hdmi_destroy_properties(struct dw_hdmi *hdmi)
+{
+	const struct dw_hdmi_property_ops *ops =
+				hdmi->plat_data->property_ops;
+
+	if (ops && ops->destroy_properties)
+		return ops->destroy_properties(&hdmi->connector,
+					       hdmi->plat_data->phy_data);
+}
+
 static int dw_hdmi_connector_create(struct dw_hdmi *hdmi)
 {
 	struct drm_connector *connector = &hdmi->connector;
@@ -2505,6 +3281,8 @@ static int dw_hdmi_connector_create(struct dw_hdmi *hdmi)
 
 	drm_connector_attach_encoder(connector, hdmi->bridge.encoder);
 
+	dw_hdmi_attach_properties(hdmi);
+
 	cec_fill_conn_info_from_drm(&conn_info, connector);
 
 	notifier = cec_notifier_conn_register(hdmi->dev, NULL, &conn_info);
@@ -2780,16 +3558,36 @@ static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
 				       struct drm_connector_state *conn_state)
 {
 	struct dw_hdmi *hdmi = bridge->driver_private;
+	void *data = hdmi->plat_data->phy_data;
 
-	hdmi->hdmi_data.enc_out_bus_format =
-			bridge_state->output_bus_cfg.format;
+	if (bridge_state->output_bus_cfg.format == MEDIA_BUS_FMT_FIXED) {
+		if (hdmi->plat_data->get_output_bus_format)
+			hdmi->hdmi_data.enc_out_bus_format =
+				hdmi->plat_data->get_output_bus_format(data);
+		else
+			hdmi->hdmi_data.enc_out_bus_format =
+				MEDIA_BUS_FMT_RGB888_1X24;
+
+		if (hdmi->plat_data->get_input_bus_format)
+			hdmi->hdmi_data.enc_in_bus_format =
+				hdmi->plat_data->get_input_bus_format(data);
+		else if (hdmi->plat_data->input_bus_format)
+			hdmi->hdmi_data.enc_in_bus_format =
+				hdmi->plat_data->input_bus_format;
+		else
+			hdmi->hdmi_data.enc_in_bus_format =
+				MEDIA_BUS_FMT_RGB888_1X24;
+	} else {
+		hdmi->hdmi_data.enc_out_bus_format =
+				bridge_state->output_bus_cfg.format;
 
-	hdmi->hdmi_data.enc_in_bus_format =
-			bridge_state->input_bus_cfg.format;
+		hdmi->hdmi_data.enc_in_bus_format =
+				bridge_state->input_bus_cfg.format;
 
-	dev_dbg(hdmi->dev, "input format 0x%04x, output format 0x%04x\n",
-		bridge_state->input_bus_cfg.format,
-		bridge_state->output_bus_cfg.format);
+		dev_dbg(hdmi->dev, "input format 0x%04x, output format 0x%04x\n",
+			bridge_state->input_bus_cfg.format,
+			bridge_state->output_bus_cfg.format);
+	}
 
 	return 0;
 }
@@ -2798,10 +3596,22 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge,
 				 enum drm_bridge_attach_flags flags)
 {
 	struct dw_hdmi *hdmi = bridge->driver_private;
+	int ret;
 
 	if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
 		return 0;
 
+	if (hdmi->next_bridge) {
+		hdmi->next_bridge->encoder = bridge->encoder;
+		ret = drm_bridge_attach(bridge->encoder, hdmi->next_bridge, bridge, flags);
+		if (ret) {
+			DRM_ERROR("Failed to attach bridge with dw-hdmi\n");
+			return ret;
+		}
+
+		return 0;
+	}
+
 	return dw_hdmi_connector_create(hdmi);
 }
 
@@ -2821,17 +3631,16 @@ dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
 			  const struct drm_display_mode *mode)
 {
 	struct dw_hdmi *hdmi = bridge->driver_private;
+	struct drm_connector *connector = &hdmi->connector;
 	const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
 	enum drm_mode_status mode_status = MODE_OK;
 
-	/* We don't support double-clocked modes */
-	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
-		return MODE_BAD;
+	if (hdmi->next_bridge)
+		return MODE_OK;
 
 	if (pdata->mode_valid)
-		mode_status = pdata->mode_valid(hdmi, pdata->priv_data, info,
-						mode);
-
+		mode_status = pdata->mode_valid(connector, pdata->priv_data,
+						info, mode);
 	return mode_status;
 }
 
@@ -2912,6 +3721,12 @@ static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
 	.get_edid = dw_hdmi_bridge_get_edid,
 };
 
+void dw_hdmi_set_cec_adap(struct dw_hdmi *hdmi, struct cec_adapter *adap)
+{
+	hdmi->cec_adap = adap;
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_set_cec_adap);
+
 /* -----------------------------------------------------------------------------
  * IRQ Handling
  */
@@ -2937,7 +3752,7 @@ static irqreturn_t dw_hdmi_i2c_irq(struct dw_hdmi *hdmi)
 static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id)
 {
 	struct dw_hdmi *hdmi = dev_id;
-	u8 intr_stat;
+	u8 intr_stat, hdcp_stat;
 	irqreturn_t ret = IRQ_NONE;
 
 	if (hdmi->i2c)
@@ -2949,6 +3764,13 @@ static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id)
 		return IRQ_WAKE_THREAD;
 	}
 
+	hdcp_stat = hdmi_readb(hdmi, HDMI_A_APIINTSTAT);
+	if (hdcp_stat) {
+		dev_dbg(hdmi->dev, "HDCP irq %#x\n", hdcp_stat);
+		hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK);
+		return IRQ_WAKE_THREAD;
+	}
+
 	return ret;
 }
 
@@ -2956,7 +3778,7 @@ void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense)
 {
 	mutex_lock(&hdmi->mutex);
 
-	if (!hdmi->force) {
+	if (!hdmi->force && !hdmi->force_logo) {
 		/*
 		 * If the RX sense status indicates we're disconnected,
 		 * clear the software rxsense status.
@@ -2983,8 +3805,7 @@ EXPORT_SYMBOL_GPL(dw_hdmi_setup_rx_sense);
 static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
 {
 	struct dw_hdmi *hdmi = dev_id;
-	u8 intr_stat, phy_int_pol, phy_pol_mask, phy_stat;
-	enum drm_connector_status status = connector_status_unknown;
+	u8 intr_stat, phy_int_pol, phy_pol_mask, phy_stat, hdcp_stat;
 
 	intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
 	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
@@ -3023,29 +3844,23 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
 			cec_notifier_phys_addr_invalidate(hdmi->cec_notifier);
 			mutex_unlock(&hdmi->cec_notifier_mutex);
 		}
-
-		if (phy_stat & HDMI_PHY_HPD)
-			status = connector_status_connected;
-
-		if (!(phy_stat & (HDMI_PHY_HPD | HDMI_PHY_RX_SENSE)))
-			status = connector_status_disconnected;
 	}
 
-	if (status != connector_status_unknown) {
-		dev_dbg(hdmi->dev, "EVENT=%s\n",
-			status == connector_status_connected ?
-			"plugin" : "plugout");
-
-		if (hdmi->bridge.dev) {
-			drm_helper_hpd_irq_event(hdmi->bridge.dev);
-			drm_bridge_hpd_notify(&hdmi->bridge, status);
-		}
-	}
+	check_hdmi_irq(hdmi, intr_stat, phy_int_pol);
 
 	hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
-	hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE),
-		    HDMI_IH_MUTE_PHY_STAT0);
-
+	if (!hdmi->next_bridge)
+		hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD |
+			    HDMI_IH_PHY_STAT0_RX_SENSE),
+			    HDMI_IH_MUTE_PHY_STAT0);
+
+	hdcp_stat = hdmi_readb(hdmi, HDMI_A_APIINTSTAT);
+	if (hdcp_stat) {
+		if (hdmi->hdcp)
+			hdmi->hdcp->hdcp_isr(hdmi->hdcp, hdcp_stat);
+		hdmi_writeb(hdmi, hdcp_stat, HDMI_A_APIINTCLR);
+		hdmi_writeb(hdmi, 0x00, HDMI_A_APIINTMSK);
+	}
 	return IRQ_HANDLED;
 }
 
@@ -3179,12 +3994,363 @@ static void dw_hdmi_init_hw(struct dw_hdmi *hdmi)
 	 * Even if we are using a separate i2c adapter doing this doesn't
 	 * hurt.
 	 */
-	dw_hdmi_i2c_init(hdmi);
+	if (hdmi->i2c)
+		dw_hdmi_i2c_init(hdmi);
 
 	if (hdmi->phy.ops->setup_hpd)
 		hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data);
 }
 
+static int dw_hdmi_status_show(struct seq_file *s, void *v)
+{
+	struct dw_hdmi *hdmi = s->private;
+	u32 val;
+
+	seq_puts(s, "PHY: ");
+	if (!hdmi->phy.enabled) {
+		seq_puts(s, "disabled\n");
+		return 0;
+	}
+	seq_puts(s, "enabled\t\t\tMode: ");
+	if (hdmi->sink_is_hdmi)
+		seq_puts(s, "HDMI\n");
+	else
+		seq_puts(s, "DVI\n");
+	if (hdmi->hdmi_data.video_mode.mtmdsclock > 340000000)
+		val = hdmi->hdmi_data.video_mode.mtmdsclock / 4;
+	else
+		val = hdmi->hdmi_data.video_mode.mtmdsclock;
+	seq_printf(s, "Pixel Clk: %uHz\t\tTMDS Clk: %uHz\n",
+		   hdmi->hdmi_data.video_mode.mpixelclock, val);
+	seq_puts(s, "Color Format: ");
+	if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format))
+		seq_puts(s, "RGB");
+	else if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format))
+		seq_puts(s, "YUV444");
+	else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format))
+		seq_puts(s, "YUV422");
+	else if (hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format))
+		seq_puts(s, "YUV420");
+	else
+		seq_puts(s, "UNKNOWN");
+	val =  hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format);
+	seq_printf(s, "\t\tColor Depth: %d bit\n", val);
+	seq_puts(s, "Colorimetry: ");
+	switch (hdmi->hdmi_data.enc_out_encoding) {
+	case V4L2_YCBCR_ENC_601:
+		seq_puts(s, "ITU.BT601");
+		break;
+	case V4L2_YCBCR_ENC_709:
+		seq_puts(s, "ITU.BT709");
+		break;
+	case V4L2_YCBCR_ENC_BT2020:
+		seq_puts(s, "ITU.BT2020");
+		break;
+	default: /* Carries no data */
+		seq_puts(s, "ITU.BT601");
+		break;
+	}
+
+	seq_puts(s, "\t\tEOTF: ");
+
+	if (hdmi->version < 0x211a) {
+		seq_puts(s, "Unsupported\n");
+		return 0;
+	}
+
+	val = hdmi_readb(hdmi, HDMI_FC_PACKET_TX_EN);
+	if (!(val & HDMI_FC_PACKET_TX_EN_DRM_MASK)) {
+		seq_puts(s, "Off\n");
+		return 0;
+	}
+
+	switch (hdmi_readb(hdmi, HDMI_FC_DRM_PB0)) {
+	case HDMI_EOTF_TRADITIONAL_GAMMA_SDR:
+		seq_puts(s, "SDR");
+		break;
+	case HDMI_EOTF_TRADITIONAL_GAMMA_HDR:
+		seq_puts(s, "HDR");
+		break;
+	case HDMI_EOTF_SMPTE_ST2084:
+		seq_puts(s, "ST2084");
+		break;
+	case HDMI_EOTF_BT_2100_HLG:
+		seq_puts(s, "HLG");
+		break;
+	default:
+		seq_puts(s, "Not Defined\n");
+		return 0;
+	}
+
+	val = hdmi_readb(hdmi, HDMI_FC_DRM_PB3) << 8;
+	val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB2);
+	seq_printf(s, "\nx0: %d", val);
+	val = hdmi_readb(hdmi, HDMI_FC_DRM_PB5) << 8;
+	val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB4);
+	seq_printf(s, "\t\t\t\ty0: %d\n", val);
+	val = hdmi_readb(hdmi, HDMI_FC_DRM_PB7) << 8;
+	val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB6);
+	seq_printf(s, "x1: %d", val);
+	val = hdmi_readb(hdmi, HDMI_FC_DRM_PB9) << 8;
+	val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB8);
+	seq_printf(s, "\t\t\t\ty1: %d\n", val);
+	val = hdmi_readb(hdmi, HDMI_FC_DRM_PB11) << 8;
+	val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB10);
+	seq_printf(s, "x2: %d", val);
+	val = hdmi_readb(hdmi, HDMI_FC_DRM_PB13) << 8;
+	val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB12);
+	seq_printf(s, "\t\t\t\ty2: %d\n", val);
+	val = hdmi_readb(hdmi, HDMI_FC_DRM_PB15) << 8;
+	val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB14);
+	seq_printf(s, "white x: %d", val);
+	val = hdmi_readb(hdmi, HDMI_FC_DRM_PB17) << 8;
+	val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB16);
+	seq_printf(s, "\t\t\twhite y: %d\n", val);
+	val = hdmi_readb(hdmi, HDMI_FC_DRM_PB19) << 8;
+	val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB18);
+	seq_printf(s, "max lum: %d", val);
+	val = hdmi_readb(hdmi, HDMI_FC_DRM_PB21) << 8;
+	val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB20);
+	seq_printf(s, "\t\t\tmin lum: %d\n", val);
+	val = hdmi_readb(hdmi, HDMI_FC_DRM_PB23) << 8;
+	val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB22);
+	seq_printf(s, "max cll: %d", val);
+	val = hdmi_readb(hdmi, HDMI_FC_DRM_PB25) << 8;
+	val |= hdmi_readb(hdmi, HDMI_FC_DRM_PB24);
+	seq_printf(s, "\t\t\tmax fall: %d\n", val);
+	return 0;
+}
+
+static int dw_hdmi_status_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dw_hdmi_status_show, inode->i_private);
+}
+
+static const struct file_operations dw_hdmi_status_fops = {
+	.owner = THIS_MODULE,
+	.open = dw_hdmi_status_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+struct dw_hdmi_reg_table {
+	int reg_base;
+	int reg_end;
+};
+
+static const struct dw_hdmi_reg_table hdmi_reg_table[] = {
+	{HDMI_DESIGN_ID, HDMI_CONFIG3_ID},
+	{HDMI_IH_FC_STAT0, HDMI_IH_MUTE},
+	{HDMI_TX_INVID0, HDMI_TX_BCBDATA1},
+	{HDMI_VP_STATUS, HDMI_VP_POL},
+	{HDMI_FC_INVIDCONF, HDMI_FC_DBGTMDS2},
+	{HDMI_PHY_CONF0, HDMI_PHY_POL0},
+	{HDMI_PHY_I2CM_SLAVE_ADDR, HDMI_PHY_I2CM_FS_SCL_LCNT_0_ADDR},
+	{HDMI_AUD_CONF0, 0x3624},
+	{HDMI_MC_SFRDIV, HDMI_MC_HEACPHY_RST},
+	{HDMI_CSC_CFG, HDMI_CSC_COEF_C4_LSB},
+	{HDMI_A_HDCPCFG0, 0x52bb},
+	{0x7800, 0x7818},
+	{0x7900, 0x790e},
+	{HDMI_CEC_CTRL, HDMI_CEC_WKUPCTRL},
+	{HDMI_I2CM_SLAVE, 0x7e31},
+};
+
+static int dw_hdmi_ctrl_show(struct seq_file *s, void *v)
+{
+	struct dw_hdmi *hdmi = s->private;
+	u32 i = 0, j = 0, val = 0;
+
+	seq_puts(s, "\n>>>hdmi_ctl reg ");
+	for (i = 0; i < 16; i++)
+		seq_printf(s, " %2x", i);
+	seq_puts(s, "\n---------------------------------------------------");
+
+	for (i = 0; i < ARRAY_SIZE(hdmi_reg_table); i++) {
+		for (j = hdmi_reg_table[i].reg_base;
+		     j <= hdmi_reg_table[i].reg_end; j++) {
+			val = hdmi_readb(hdmi, j);
+			if ((j - hdmi_reg_table[i].reg_base) % 16 == 0)
+				seq_printf(s, "\n>>>hdmi_ctl %04x:", j);
+			seq_printf(s, " %02x", val);
+		}
+	}
+	seq_puts(s, "\n---------------------------------------------------\n");
+
+	return 0;
+}
+
+static int dw_hdmi_ctrl_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dw_hdmi_ctrl_show, inode->i_private);
+}
+
+static ssize_t
+dw_hdmi_ctrl_write(struct file *file, const char __user *buf,
+		   size_t count, loff_t *ppos)
+{
+	struct dw_hdmi *hdmi =
+		((struct seq_file *)file->private_data)->private;
+	u32 reg, val;
+	char kbuf[25];
+
+	if (copy_from_user(kbuf, buf, count))
+		return -EFAULT;
+	if (sscanf(kbuf, "%x%x", &reg, &val) == -1)
+		return -EFAULT;
+	if (reg > HDMI_I2CM_FS_SCL_LCNT_0_ADDR) {
+		dev_err(hdmi->dev, "it is no a hdmi register\n");
+		return count;
+	}
+	dev_info(hdmi->dev, "/**********hdmi register config******/");
+	dev_info(hdmi->dev, "\n reg=%x val=%x\n", reg, val);
+	hdmi_writeb(hdmi, val, reg);
+	return count;
+}
+
+static const struct file_operations dw_hdmi_ctrl_fops = {
+	.owner = THIS_MODULE,
+	.open = dw_hdmi_ctrl_open,
+	.read = seq_read,
+	.write = dw_hdmi_ctrl_write,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static int dw_hdmi_phy_show(struct seq_file *s, void *v)
+{
+	struct dw_hdmi *hdmi = s->private;
+	u32 i;
+
+	seq_puts(s, "\n>>>hdmi_phy reg ");
+	for (i = 0; i < 0x28; i++)
+		seq_printf(s, "regs %02x val %04x\n",
+			   i, hdmi_phy_i2c_read(hdmi, i));
+	return 0;
+}
+
+static int dw_hdmi_phy_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, dw_hdmi_phy_show, inode->i_private);
+}
+
+static ssize_t
+dw_hdmi_phy_write(struct file *file, const char __user *buf,
+		  size_t count, loff_t *ppos)
+{
+	struct dw_hdmi *hdmi =
+		((struct seq_file *)file->private_data)->private;
+	u32 reg, val;
+	char kbuf[25];
+
+	if (copy_from_user(kbuf, buf, count))
+		return -EFAULT;
+	if (sscanf(kbuf, "%x%x", &reg, &val) == -1)
+		return -EFAULT;
+	if (reg > 0x28) {
+		dev_err(hdmi->dev, "it is not a hdmi phy register\n");
+		return count;
+	}
+	dev_info(hdmi->dev, "/*******hdmi phy register config******/");
+	dev_info(hdmi->dev, "\n reg=%x val=%x\n", reg, val);
+	dw_hdmi_phy_i2c_write(hdmi, val, reg);
+	return count;
+}
+
+static const struct file_operations dw_hdmi_phy_fops = {
+	.owner = THIS_MODULE,
+	.open = dw_hdmi_phy_open,
+	.read = seq_read,
+	.write = dw_hdmi_phy_write,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static void dw_hdmi_register_debugfs(struct device *dev, struct dw_hdmi *hdmi)
+{
+	hdmi->debugfs_dir = debugfs_create_dir("dw-hdmi", NULL);
+	if (IS_ERR(hdmi->debugfs_dir)) {
+		dev_err(dev, "failed to create debugfs dir!\n");
+		return;
+	}
+	debugfs_create_file("status", 0400, hdmi->debugfs_dir,
+			    hdmi, &dw_hdmi_status_fops);
+	debugfs_create_file("ctrl", 0400, hdmi->debugfs_dir,
+			    hdmi, &dw_hdmi_ctrl_fops);
+	debugfs_create_file("phy", 0400, hdmi->debugfs_dir,
+			    hdmi, &dw_hdmi_phy_fops);
+}
+
+static void dw_hdmi_register_hdcp(struct device *dev, struct dw_hdmi *hdmi,
+				  u32 val, bool hdcp1x_enable)
+{
+	struct dw_hdcp hdmi_hdcp = {
+		.hdmi = hdmi,
+		.write = hdmi_writeb,
+		.read = hdmi_readb,
+		.regs = hdmi->regs,
+		.reg_io_width = val,
+		.enable = hdcp1x_enable,
+	};
+	struct platform_device_info hdcp_device_info = {
+		.parent = dev,
+		.id = PLATFORM_DEVID_AUTO,
+		.res = NULL,
+		.num_res = 0,
+		.name = DW_HDCP_DRIVER_NAME,
+		.data = &hdmi_hdcp,
+		.size_data = sizeof(hdmi_hdcp),
+		.dma_mask = DMA_BIT_MASK(32),
+	};
+
+	hdmi->hdcp_dev = platform_device_register_full(&hdcp_device_info);
+	if (IS_ERR(hdmi->hdcp_dev))
+		dev_err(dev, "failed to register hdcp!\n");
+	else
+		hdmi->hdcp = hdmi->hdcp_dev->dev.platform_data;
+}
+
+static int get_force_logo_property(struct dw_hdmi *hdmi)
+{
+	struct device_node *dss;
+	struct device_node *route;
+	struct device_node *route_hdmi;
+
+	dss = of_find_node_by_name(NULL, "display-subsystem");
+	if (!dss) {
+		dev_err(hdmi->dev, "can't find display-subsystem\n");
+		return -ENODEV;
+	}
+
+	route = of_find_node_by_name(dss, "route");
+	if (!route) {
+		dev_err(hdmi->dev, "can't find route\n");
+		of_node_put(dss);
+		return -ENODEV;
+	}
+	of_node_put(dss);
+
+	route_hdmi = of_find_node_by_name(route, "route-hdmi");
+	if (!route_hdmi) {
+		dev_err(hdmi->dev, "can't find route-hdmi\n");
+		of_node_put(route);
+		return -ENODEV;
+	}
+	of_node_put(route);
+
+	hdmi->force_logo =
+		of_property_read_bool(route_hdmi, "force-output");
+
+	of_node_put(route_hdmi);
+
+	return 0;
+}
+
 /* -----------------------------------------------------------------------------
  * Probe/remove API, used from platforms based on the DRM bridge API.
  */
@@ -3193,6 +4359,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
 {
 	struct device *dev = &pdev->dev;
 	struct device_node *np = dev->of_node;
+	struct device_node *endpoint;
 	struct platform_device_info pdevinfo;
 	struct device_node *ddc_node;
 	struct dw_hdmi_cec_data cec;
@@ -3205,11 +4372,13 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
 	u8 prod_id1;
 	u8 config0;
 	u8 config3;
+	bool hdcp1x_enable = 0;
 
 	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
 	if (!hdmi)
 		return ERR_PTR(-ENOMEM);
 
+	hdmi->connector.stereo_allowed = 1;
 	hdmi->plat_data = plat_data;
 	hdmi->dev = dev;
 	hdmi->sample_rate = 48000;
@@ -3340,7 +4509,24 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
 		 prod_id1 & HDMI_PRODUCT_ID1_HDCP ? "with" : "without",
 		 hdmi->phy.name);
 
-	dw_hdmi_init_hw(hdmi);
+	ret = get_force_logo_property(hdmi);
+	if (ret)
+		goto err_iahb;
+
+	hdmi->initialized = false;
+	ret = hdmi_readb(hdmi, HDMI_PHY_STAT0);
+	if (((ret & HDMI_PHY_TX_PHY_LOCK) && (ret & HDMI_PHY_HPD) &&
+	     hdmi_readb(hdmi, HDMI_FC_EXCTRLDUR)) || hdmi->force_logo) {
+		hdmi->mc_clkdis = hdmi_readb(hdmi, HDMI_MC_CLKDIS);
+		hdmi->disabled = false;
+		hdmi->bridge_is_on = true;
+		hdmi->phy.enabled = true;
+		hdmi->initialized = true;
+	} else if (ret & HDMI_PHY_TX_PHY_LOCK) {
+		hdmi->phy.ops->disable(hdmi, hdmi->phy.data);
+	}
+
+	init_hpd_work(hdmi);
 
 	irq = platform_get_irq(pdev, 0);
 	if (irq < 0) {
@@ -3348,6 +4534,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
 		goto err_iahb;
 	}
 
+	hdmi->irq = irq;
 	ret = devm_request_threaded_irq(dev, irq, dw_hdmi_hardirq,
 					dw_hdmi_irq, IRQF_SHARED,
 					dev_name(dev), hdmi);
@@ -3383,8 +4570,20 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
 		hdmi->ddc = dw_hdmi_i2c_adapter(hdmi);
 		if (IS_ERR(hdmi->ddc))
 			hdmi->ddc = NULL;
+		/*
+		 * Read high and low time from device tree. If not available use
+		 * the default timing scl clock rate is about 99.6KHz.
+		 */
+		if (of_property_read_u32(np, "ddc-i2c-scl-high-time-ns",
+					 &hdmi->i2c->scl_high_ns))
+			hdmi->i2c->scl_high_ns = 4708;
+		if (of_property_read_u32(np, "ddc-i2c-scl-low-time-ns",
+					 &hdmi->i2c->scl_low_ns))
+			hdmi->i2c->scl_low_ns = 4916;
 	}
 
+	dw_hdmi_init_hw(hdmi);
+
 	hdmi->bridge.driver_private = hdmi;
 	hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
 	hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
@@ -3393,6 +4592,30 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
 	hdmi->bridge.of_node = pdev->dev.of_node;
 #endif
 
+	endpoint = of_graph_get_endpoint_by_regs(hdmi->dev->of_node, 1, -1);
+	if (endpoint && of_device_is_available(endpoint)) {
+		struct device_node *remote;
+
+		remote = of_graph_get_remote_port_parent(endpoint);
+		of_node_put(endpoint);
+		if (!remote || !of_device_is_available(remote)) {
+			of_node_put(remote);
+			ret = -ENODEV;
+			goto err_iahb;
+		}
+
+		hdmi->next_bridge = of_drm_find_bridge(remote);
+		of_node_put(remote);
+		if (!hdmi->next_bridge) {
+			dev_err(hdmi->dev, "can't find next bridge\n");
+			ret = -EPROBE_DEFER;
+			goto err_iahb;
+		}
+
+		hdmi->sink_is_hdmi = true;
+		hdmi->sink_has_audio = true;
+	}
+
 	memset(&pdevinfo, 0, sizeof(pdevinfo));
 	pdevinfo.parent = dev;
 	pdevinfo.id = PLATFORM_DEVID_AUTO;
@@ -3407,7 +4630,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
 		audio.base = hdmi->regs;
 		audio.irq = irq;
 		audio.hdmi = hdmi;
-		audio.get_eld = hdmi_audio_get_eld;
+		audio.eld = hdmi->connector.eld;
 		hdmi->enable_audio = dw_hdmi_ahb_audio_enable;
 		hdmi->disable_audio = dw_hdmi_ahb_audio_disable;
 
@@ -3420,7 +4643,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
 		struct dw_hdmi_i2s_audio_data audio;
 
 		audio.hdmi	= hdmi;
-		audio.get_eld	= hdmi_audio_get_eld;
+		audio.eld	= hdmi->connector.eld;
 		audio.write	= hdmi_writeb;
 		audio.read	= hdmi_readb;
 		hdmi->enable_audio = dw_hdmi_i2s_audio_enable;
@@ -3446,8 +4669,40 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
 		hdmi->cec = platform_device_register_full(&pdevinfo);
 	}
 
+	hdmi->extcon = devm_extcon_dev_allocate(hdmi->dev, dw_hdmi_cable);
+	if (IS_ERR(hdmi->extcon)) {
+		ret = PTR_ERR(hdmi->extcon);
+		dev_err(hdmi->dev, "allocate extcon failed: %d\n", ret);
+		goto err_iahb;
+	}
+
+	ret = devm_extcon_dev_register(hdmi->dev, hdmi->extcon);
+	if (ret) {
+		dev_err(hdmi->dev, "failed to register extcon: %d\n",
+			ret);
+		goto err_iahb;
+	}
+
+	ret = extcon_set_property_capability(hdmi->extcon, EXTCON_DISP_HDMI,
+					     EXTCON_PROP_DISP_HPD);
+	if (ret) {
+		dev_err(hdmi->dev,
+			"failed to set USB property capability: %d\n",
+			ret);
+		goto err_iahb;
+	}
+
 	drm_bridge_add(&hdmi->bridge);
 
+	dw_hdmi_register_debugfs(dev, hdmi);
+
+	if (of_property_read_bool(np, "scramble-low-rates"))
+		hdmi->scramble_low_rates = true;
+
+	if (of_property_read_bool(np, "hdcp1x-enable"))
+		hdcp1x_enable = 1;
+	dw_hdmi_register_hdcp(dev, hdmi, val, hdcp1x_enable);
+
 	return hdmi;
 
 err_iahb:
@@ -3457,7 +4712,10 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
 err_isfr:
 	clk_disable_unprepare(hdmi->isfr_clk);
 err_res:
-	i2c_put_adapter(hdmi->ddc);
+	if (hdmi->i2c)
+		i2c_del_adapter(&hdmi->i2c->adap);
+	else
+		i2c_put_adapter(hdmi->ddc);
 
 	return ERR_PTR(ret);
 }
@@ -3465,16 +4723,35 @@ EXPORT_SYMBOL_GPL(dw_hdmi_probe);
 
 void dw_hdmi_remove(struct dw_hdmi *hdmi)
 {
+	if (hdmi->irq)
+		disable_irq(hdmi->irq);
+
+	cancel_delayed_work(&hdmi->work);
+	flush_workqueue(hdmi->workqueue);
+	destroy_workqueue(hdmi->workqueue);
+
+	debugfs_remove_recursive(hdmi->debugfs_dir);
+
 	drm_bridge_remove(&hdmi->bridge);
 
 	if (hdmi->audio && !IS_ERR(hdmi->audio))
 		platform_device_unregister(hdmi->audio);
+	if (hdmi->hdcp_dev && !IS_ERR(hdmi->hdcp_dev))
+		platform_device_unregister(hdmi->hdcp_dev);
 	if (!IS_ERR(hdmi->cec))
 		platform_device_unregister(hdmi->cec);
 
 	/* Disable all interrupts */
 	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
+	if (!hdmi->next_bridge) {
+		dw_hdmi_destroy_properties(hdmi);
+		hdmi->connector.funcs->destroy(&hdmi->connector);
+	}
+
+	if (hdmi->bridge.encoder)
+		hdmi->bridge.encoder->funcs->destroy(hdmi->bridge.encoder);
+
 	clk_disable_unprepare(hdmi->iahb_clk);
 	clk_disable_unprepare(hdmi->isfr_clk);
 	if (hdmi->cec_clk)
@@ -3492,7 +4769,7 @@ EXPORT_SYMBOL_GPL(dw_hdmi_remove);
  */
 struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev,
 			     struct drm_encoder *encoder,
-			     const struct dw_hdmi_plat_data *plat_data)
+			     struct dw_hdmi_plat_data *plat_data)
 {
 	struct dw_hdmi *hdmi;
 	int ret;
@@ -3508,6 +4785,9 @@ struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev,
 		return ERR_PTR(ret);
 	}
 
+	if (!hdmi->next_bridge)
+		plat_data->connector = &hdmi->connector;
+
 	return hdmi;
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_bind);
@@ -3518,9 +4798,87 @@ void dw_hdmi_unbind(struct dw_hdmi *hdmi)
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_unbind);
 
+static void dw_hdmi_reg_initial(struct dw_hdmi *hdmi)
+{
+	if (hdmi_readb(hdmi, HDMI_IH_MUTE)) {
+		initialize_hdmi_ih_mutes(hdmi);
+		/* unmute cec irq */
+		hdmi_writeb(hdmi, 0x68, HDMI_IH_MUTE_CEC_STAT0);
+
+		hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
+			    HDMI_PHY_I2CM_INT_ADDR);
+
+		hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
+			    HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL,
+			    HDMI_PHY_I2CM_CTLINT_ADDR);
+
+		if (!hdmi->next_bridge) {
+			hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE,
+				    HDMI_PHY_POL0);
+			hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0);
+			hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD |
+				    HDMI_IH_PHY_STAT0_RX_SENSE),
+				    HDMI_IH_MUTE_PHY_STAT0);
+		}
+	}
+}
+
+void dw_hdmi_suspend(struct dw_hdmi *hdmi)
+{
+	if (!hdmi)
+		return;
+
+	mutex_lock(&hdmi->mutex);
+
+	/*
+	 * When system shutdown, hdmi should be disabled.
+	 * When system suspend, dw_hdmi_bridge_disable will disable hdmi first.
+	 * To prevent duplicate operation, we should determine whether hdmi
+	 * has been disabled.
+	 */
+	if (!hdmi->disabled) {
+		hdmi->disabled = true;
+		dw_hdmi_update_power(hdmi);
+		dw_hdmi_update_phy_mask(hdmi);
+	}
+	mutex_unlock(&hdmi->mutex);
+
+	if (hdmi->irq)
+		disable_irq(hdmi->irq);
+	cancel_delayed_work(&hdmi->work);
+	flush_workqueue(hdmi->workqueue);
+	pinctrl_pm_select_sleep_state(hdmi->dev);
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_suspend);
+
 void dw_hdmi_resume(struct dw_hdmi *hdmi)
 {
-	dw_hdmi_init_hw(hdmi);
+	if (!hdmi)
+		return;
+
+	pinctrl_pm_select_default_state(hdmi->dev);
+	mutex_lock(&hdmi->mutex);
+	dw_hdmi_reg_initial(hdmi);
+	if (hdmi->i2c)
+		dw_hdmi_i2c_init(hdmi);
+	if (hdmi->irq)
+		enable_irq(hdmi->irq);
+	/*
+	 * HDMI status maybe incorrect in the following condition:
+	 * HDMI plug in -> system sleep ->  HDMI plug out -> system wake up.
+	 * At this time, cat /sys/class/drm/card 0-HDMI-A-1/status is connected.
+	 * There is no hpd interrupt, because HDMI is powerdown during suspend.
+	 * So we need check the current HDMI status in this case.
+	 */
+	if (hdmi->connector.status == connector_status_connected) {
+		if (hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data) ==
+		    connector_status_disconnected) {
+			hdmi->hpd_state = false;
+			mod_delayed_work(hdmi->workqueue, &hdmi->work,
+					 msecs_to_jiffies(20));
+		}
+	}
+	mutex_unlock(&hdmi->mutex);
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_resume);
 
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
index 1999db05b..509732800 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
@@ -509,6 +509,51 @@
 #define HDMI_A_PRESETUP                         0x501A
 #define HDMI_A_SRM_BASE                         0x5020
 
+/* CEC Engine Registers */
+#define HDMI_CEC_CTRL                           0x7D00
+#define HDMI_CEC_STAT                           0x7D01
+#define HDMI_CEC_MASK                           0x7D02
+#define HDMI_CEC_POLARITY                       0x7D03
+#define HDMI_CEC_INT                            0x7D04
+#define HDMI_CEC_ADDR_L                         0x7D05
+#define HDMI_CEC_ADDR_H                         0x7D06
+#define HDMI_CEC_TX_CNT                         0x7D07
+#define HDMI_CEC_RX_CNT                         0x7D08
+#define HDMI_CEC_TX_DATA0                       0x7D10
+#define HDMI_CEC_TX_DATA1                       0x7D11
+#define HDMI_CEC_TX_DATA2                       0x7D12
+#define HDMI_CEC_TX_DATA3                       0x7D13
+#define HDMI_CEC_TX_DATA4                       0x7D14
+#define HDMI_CEC_TX_DATA5                       0x7D15
+#define HDMI_CEC_TX_DATA6                       0x7D16
+#define HDMI_CEC_TX_DATA7                       0x7D17
+#define HDMI_CEC_TX_DATA8                       0x7D18
+#define HDMI_CEC_TX_DATA9                       0x7D19
+#define HDMI_CEC_TX_DATA10                      0x7D1a
+#define HDMI_CEC_TX_DATA11                      0x7D1b
+#define HDMI_CEC_TX_DATA12                      0x7D1c
+#define HDMI_CEC_TX_DATA13                      0x7D1d
+#define HDMI_CEC_TX_DATA14                      0x7D1e
+#define HDMI_CEC_TX_DATA15                      0x7D1f
+#define HDMI_CEC_RX_DATA0                       0x7D20
+#define HDMI_CEC_RX_DATA1                       0x7D21
+#define HDMI_CEC_RX_DATA2                       0x7D22
+#define HDMI_CEC_RX_DATA3                       0x7D23
+#define HDMI_CEC_RX_DATA4                       0x7D24
+#define HDMI_CEC_RX_DATA5                       0x7D25
+#define HDMI_CEC_RX_DATA6                       0x7D26
+#define HDMI_CEC_RX_DATA7                       0x7D27
+#define HDMI_CEC_RX_DATA8                       0x7D28
+#define HDMI_CEC_RX_DATA9                       0x7D29
+#define HDMI_CEC_RX_DATA10                      0x7D2a
+#define HDMI_CEC_RX_DATA11                      0x7D2b
+#define HDMI_CEC_RX_DATA12                      0x7D2c
+#define HDMI_CEC_RX_DATA13                      0x7D2d
+#define HDMI_CEC_RX_DATA14                      0x7D2e
+#define HDMI_CEC_RX_DATA15                      0x7D2f
+#define HDMI_CEC_LOCK                           0x7D30
+#define HDMI_CEC_WKUPCTRL                       0x7D31
+
 /* I2C Master Registers (E-DDC) */
 #define HDMI_I2CM_SLAVE                         0x7E00
 #define HDMI_I2CM_ADDRESS                       0x7E01
@@ -529,6 +574,7 @@
 #define HDMI_I2CM_FS_SCL_HCNT_0_ADDR            0x7E10
 #define HDMI_I2CM_FS_SCL_LCNT_1_ADDR            0x7E11
 #define HDMI_I2CM_FS_SCL_LCNT_0_ADDR            0x7E12
+#define HDMI_I2CM_SDA_HOLD                      0x7E13
 
 enum {
 /* PRODUCT_ID0 field values */
@@ -842,6 +888,10 @@ enum {
 	HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED = 0x00,
 	HDMI_FC_AVICONF3_QUANT_RANGE_FULL = 0x04,
 
+/* HDMI_FC_GCP */
+	HDMI_FC_GCP_SET_AVMUTE = 0x2,
+	HDMI_FC_GCP_CLEAR_AVMUTE = 0x1,
+
 /* FC_DBGFORCE field values */
 	HDMI_FC_DBGFORCE_FORCEAUDIO = 0x10,
 	HDMI_FC_DBGFORCE_FORCEVIDEO = 0x1,
@@ -1085,6 +1135,11 @@ enum {
 	HDMI_I2CM_CTLINT_NAC_MASK = 0x40,
 	HDMI_I2CM_CTLINT_ARB_POL = 0x8,
 	HDMI_I2CM_CTLINT_ARB_MASK = 0x4,
+
+/* I2CM_DIV field values */
+	HDMI_I2CM_DIV_FAST_STD_MODE = 0x8,
+	HDMI_I2CM_DIV_FAST_MODE = 0x8,
+	HDMI_I2CM_DIV_STD_MODE = 0,
 };
 
 /*
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
index 376fa6eb4..163dcc03b 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
@@ -244,7 +244,7 @@ struct dw_mipi_dsi {
 	struct device *dev;
 	void __iomem *base;
 
-	struct clk *pclk;
+	struct reset_control *apb_rst;
 
 	unsigned int lane_mbps; /* per lane */
 	u32 channel;
@@ -316,15 +316,10 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
 	const struct dw_mipi_dsi_plat_data *pdata = dsi->plat_data;
 	struct drm_bridge *bridge;
 	struct drm_panel *panel;
+	int max_data_lanes = dsi->plat_data->max_data_lanes;
 	int ret;
 
-	if (device->lanes > dsi->plat_data->max_data_lanes) {
-		dev_err(dsi->dev, "the number of data lanes(%u) is too many\n",
-			device->lanes);
-		return -EINVAL;
-	}
-
-	dsi->lanes = device->lanes;
+	dsi->lanes = (device->lanes > max_data_lanes) ? device->lanes / 2 : device->lanes;
 	dsi->channel = device->channel;
 	dsi->format = device->format;
 	dsi->mode_flags = device->mode_flags;
@@ -599,8 +594,14 @@ static void dw_mipi_dsi_set_mode(struct dw_mipi_dsi *dsi,
 
 static void dw_mipi_dsi_disable(struct dw_mipi_dsi *dsi)
 {
+	const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops;
+
+	if (phy_ops->power_off)
+		phy_ops->power_off(dsi->plat_data->priv_data);
+
 	dsi_write(dsi, DSI_PWR_UP, RESET);
 	dsi_write(dsi, DSI_PHY_RSTZ, PHY_RSTZ);
+	pm_runtime_put(dsi->dev);
 }
 
 static void dw_mipi_dsi_init(struct dw_mipi_dsi *dsi)
@@ -715,16 +716,16 @@ static u32 dw_mipi_dsi_get_hcomponent_lbcc(struct dw_mipi_dsi *dsi,
 					   const struct drm_display_mode *mode,
 					   u32 hcomponent)
 {
-	u32 frac, lbcc;
+	u32 lbcc;
 
 	lbcc = hcomponent * dsi->lane_mbps * MSEC_PER_SEC / 8;
 
-	frac = lbcc % mode->clock;
-	lbcc = lbcc / mode->clock;
-	if (frac)
-		lbcc++;
+	if (mode->clock == 0) {
+		DRM_ERROR("dsi mode clock is 0!\n");
+		return 0;
+	}
 
-	return lbcc;
+	return DIV_ROUND_CLOSEST_ULL(lbcc, mode->clock);
 }
 
 static void dw_mipi_dsi_line_timer_config(struct dw_mipi_dsi *dsi,
@@ -837,13 +838,13 @@ static void dw_mipi_dsi_dphy_enable(struct dw_mipi_dsi *dsi)
 	ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS, val,
 				 val & PHY_LOCK, 1000, PHY_STATUS_TIMEOUT_US);
 	if (ret)
-		DRM_DEBUG_DRIVER("failed to wait phy lock state\n");
+		DRM_ERROR("failed to wait phy lock state\n");
 
 	ret = readl_poll_timeout(dsi->base + DSI_PHY_STATUS,
 				 val, val & PHY_STOP_STATE_CLK_LANE, 1000,
 				 PHY_STATUS_TIMEOUT_US);
 	if (ret)
-		DRM_DEBUG_DRIVER("failed to wait phy clk lane stop state\n");
+		DRM_ERROR("failed to wait phy clk lane stop state\n");
 }
 
 static void dw_mipi_dsi_clear_err(struct dw_mipi_dsi *dsi)
@@ -857,7 +858,6 @@ static void dw_mipi_dsi_clear_err(struct dw_mipi_dsi *dsi)
 static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge)
 {
 	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
-	const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops;
 
 	/*
 	 * Switch to command mode before panel-bridge post_disable &
@@ -866,6 +866,8 @@ static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge)
 	 * before by the drm framework.
 	 */
 	dw_mipi_dsi_set_mode(dsi, 0);
+	if (dsi->slave)
+		dw_mipi_dsi_set_mode(dsi->slave, 0);
 
 	/*
 	 * TODO Only way found to call panel-bridge post_disable &
@@ -876,18 +878,10 @@ static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge)
 	if (dsi->panel_bridge->funcs->post_disable)
 		dsi->panel_bridge->funcs->post_disable(dsi->panel_bridge);
 
-	if (phy_ops->power_off)
-		phy_ops->power_off(dsi->plat_data->priv_data);
-
-	if (dsi->slave) {
+	if (dsi->slave)
 		dw_mipi_dsi_disable(dsi->slave);
-		clk_disable_unprepare(dsi->slave->pclk);
-		pm_runtime_put(dsi->slave->dev);
-	}
-	dw_mipi_dsi_disable(dsi);
 
-	clk_disable_unprepare(dsi->pclk);
-	pm_runtime_put(dsi->dev);
+	dw_mipi_dsi_disable(dsi);
 }
 
 static unsigned int dw_mipi_dsi_get_lanes(struct dw_mipi_dsi *dsi)
@@ -912,7 +906,11 @@ static void dw_mipi_dsi_mode_set(struct dw_mipi_dsi *dsi,
 	int ret;
 	u32 lanes = dw_mipi_dsi_get_lanes(dsi);
 
-	clk_prepare_enable(dsi->pclk);
+	if (dsi->apb_rst) {
+		reset_control_assert(dsi->apb_rst);
+		usleep_range(10, 20);
+		reset_control_deassert(dsi->apb_rst);
+	}
 
 	ret = phy_ops->get_lane_mbps(priv_data, adjusted_mode, dsi->mode_flags,
 				     lanes, dsi->format, &dsi->lane_mbps);
@@ -939,15 +937,15 @@ static void dw_mipi_dsi_mode_set(struct dw_mipi_dsi *dsi,
 	if (ret)
 		DRM_DEBUG_DRIVER("Phy init() failed\n");
 
+	if (phy_ops->power_on)
+		phy_ops->power_on(dsi->plat_data->priv_data);
+
 	dw_mipi_dsi_dphy_enable(dsi);
 
 	dw_mipi_dsi_wait_for_two_frames(adjusted_mode);
 
 	/* Switch to cmd mode for panel-bridge pre_enable & panel prepare */
 	dw_mipi_dsi_set_mode(dsi, 0);
-
-	if (phy_ops->power_on)
-		phy_ops->power_on(dsi->plat_data->priv_data);
 }
 
 static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
@@ -959,16 +957,25 @@ static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge,
 	dw_mipi_dsi_mode_set(dsi, adjusted_mode);
 	if (dsi->slave)
 		dw_mipi_dsi_mode_set(dsi->slave, adjusted_mode);
+
+	DRM_DEV_INFO(dsi->dev, "final DSI-Link bandwidth: %u x %d Mbps\n",
+		     dsi->lane_mbps, dsi->slave ? dsi->lanes * 2 : dsi->lanes);
 }
 
 static void dw_mipi_dsi_bridge_enable(struct drm_bridge *bridge)
 {
 	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
 
-	/* Switch to video mode for panel-bridge enable & panel enable */
-	dw_mipi_dsi_set_mode(dsi, MIPI_DSI_MODE_VIDEO);
-	if (dsi->slave)
-		dw_mipi_dsi_set_mode(dsi->slave, MIPI_DSI_MODE_VIDEO);
+	/* Switch to video/cmd mode for panel-bridge enable & panel enable */
+	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
+		dw_mipi_dsi_set_mode(dsi, MIPI_DSI_MODE_VIDEO);
+		if (dsi->slave)
+			dw_mipi_dsi_set_mode(dsi->slave, MIPI_DSI_MODE_VIDEO);
+	} else {
+		dw_mipi_dsi_set_mode(dsi, 0);
+		if (dsi->slave)
+			dw_mipi_dsi_set_mode(dsi->slave, 0);
+	}
 }
 
 static enum drm_mode_status
@@ -1103,7 +1110,6 @@ __dw_mipi_dsi_probe(struct platform_device *pdev,
 		    const struct dw_mipi_dsi_plat_data *plat_data)
 {
 	struct device *dev = &pdev->dev;
-	struct reset_control *apb_rst;
 	struct dw_mipi_dsi *dsi;
 	int ret;
 
@@ -1129,20 +1135,13 @@ __dw_mipi_dsi_probe(struct platform_device *pdev,
 		dsi->base = plat_data->base;
 	}
 
-	dsi->pclk = devm_clk_get(dev, "pclk");
-	if (IS_ERR(dsi->pclk)) {
-		ret = PTR_ERR(dsi->pclk);
-		dev_err(dev, "Unable to get pclk: %d\n", ret);
-		return ERR_PTR(ret);
-	}
-
 	/*
 	 * Note that the reset was not defined in the initial device tree, so
 	 * we have to be prepared for it not being found.
 	 */
-	apb_rst = devm_reset_control_get_optional_exclusive(dev, "apb");
-	if (IS_ERR(apb_rst)) {
-		ret = PTR_ERR(apb_rst);
+	dsi->apb_rst = devm_reset_control_get_optional_exclusive(dev, "apb");
+	if (IS_ERR(dsi->apb_rst)) {
+		ret = PTR_ERR(dsi->apb_rst);
 
 		if (ret != -EPROBE_DEFER)
 			dev_err(dev, "Unable to get reset control: %d\n", ret);
@@ -1150,20 +1149,6 @@ __dw_mipi_dsi_probe(struct platform_device *pdev,
 		return ERR_PTR(ret);
 	}
 
-	if (apb_rst) {
-		ret = clk_prepare_enable(dsi->pclk);
-		if (ret) {
-			dev_err(dev, "%s: Failed to enable pclk\n", __func__);
-			return ERR_PTR(ret);
-		}
-
-		reset_control_assert(apb_rst);
-		usleep_range(10, 20);
-		reset_control_deassert(apb_rst);
-
-		clk_disable_unprepare(dsi->pclk);
-	}
-
 	dw_mipi_dsi_debugfs_init(dsi);
 	pm_runtime_enable(dev);
 
@@ -1247,6 +1232,12 @@ void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi)
 }
 EXPORT_SYMBOL_GPL(dw_mipi_dsi_unbind);
 
+struct drm_connector *dw_mipi_dsi_get_connector(struct dw_mipi_dsi *dsi)
+{
+	return drm_panel_bridge_connector(dsi->panel_bridge);
+}
+EXPORT_SYMBOL_GPL(dw_mipi_dsi_get_connector);
+
 MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
 MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
 MODULE_DESCRIPTION("DW MIPI DSI host controller driver");
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 7fc8e7000..4108c7265 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -296,12 +296,14 @@ update_connector_routing(struct drm_atomic_state *state,
 	if (old_connector_state->crtc != new_connector_state->crtc) {
 		if (old_connector_state->crtc) {
 			crtc_state = drm_atomic_get_new_crtc_state(state, old_connector_state->crtc);
-			crtc_state->connectors_changed = true;
+			if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
+				crtc_state->connectors_changed = true;
 		}
 
 		if (new_connector_state->crtc) {
 			crtc_state = drm_atomic_get_new_crtc_state(state, new_connector_state->crtc);
-			crtc_state->connectors_changed = true;
+			if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
+				crtc_state->connectors_changed = true;
 		}
 	}
 
@@ -386,7 +388,8 @@ update_connector_routing(struct drm_atomic_state *state,
 
 	set_best_encoder(state, new_connector_state, new_encoder);
 
-	crtc_state->connectors_changed = true;
+	if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
+		crtc_state->connectors_changed = true;
 
 	DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] using [ENCODER:%d:%s] on [CRTC:%d:%s]\n",
 			 connector->base.id,
@@ -3554,6 +3557,9 @@ int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc,
 	replaced  = drm_property_replace_blob(&crtc_state->degamma_lut, NULL);
 	replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL);
 	replaced |= drm_property_replace_blob(&crtc_state->gamma_lut, blob);
+#if defined(CONFIG_ROCKCHIP_DRM_CUBIC_LUT)
+	replaced |= drm_property_replace_blob(&crtc_state->cubic_lut, NULL);
+#endif
 	crtc_state->color_mgmt_changed |= replaced;
 
 	ret = drm_atomic_commit(state);
diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
index 9ad740451..c29183d2a 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -141,6 +141,10 @@ void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc,
 		drm_property_blob_get(state->ctm);
 	if (state->gamma_lut)
 		drm_property_blob_get(state->gamma_lut);
+#if defined(CONFIG_ROCKCHIP_DRM_CUBIC_LUT)
+	if (state->cubic_lut)
+		drm_property_blob_get(state->cubic_lut);
+#endif
 	state->mode_changed = false;
 	state->active_changed = false;
 	state->planes_changed = false;
@@ -213,6 +217,9 @@ void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state)
 	drm_property_blob_put(state->degamma_lut);
 	drm_property_blob_put(state->ctm);
 	drm_property_blob_put(state->gamma_lut);
+#if defined(CONFIG_ROCKCHIP_DRM_CUBIC_LUT)
+	drm_property_blob_put(state->cubic_lut);
+#endif
 }
 EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state);
 
diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c
index 25c269bc4..975ece7e0 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -459,6 +459,16 @@ static int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
 					&replaced);
 		state->color_mgmt_changed |= replaced;
 		return ret;
+#if defined(CONFIG_ROCKCHIP_DRM_CUBIC_LUT)
+	} else if (property == config->cubic_lut_property) {
+		ret = drm_atomic_replace_property_blob_from_id(dev,
+					&state->cubic_lut,
+					val,
+					-1, sizeof(struct drm_color_lut),
+					&replaced);
+		state->color_mgmt_changed |= replaced;
+		return ret;
+#endif
 	} else if (property == config->prop_out_fence_ptr) {
 		s32 __user *fence_ptr = u64_to_user_ptr(val);
 
@@ -501,6 +511,10 @@ drm_atomic_crtc_get_property(struct drm_crtc *crtc,
 		*val = (state->ctm) ? state->ctm->base.id : 0;
 	else if (property == config->gamma_lut_property)
 		*val = (state->gamma_lut) ? state->gamma_lut->base.id : 0;
+#if defined(CONFIG_ROCKCHIP_DRM_CUBIC_LUT)
+	else if (property == config->cubic_lut_property)
+		*val = (state->cubic_lut) ? state->cubic_lut->base.id : 0;
+#endif
 	else if (property == config->prop_out_fence_ptr)
 		*val = 0;
 	else if (crtc->funcs->atomic_get_property)
diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c
index c7adbeaf1..232abbba3 100644
--- a/drivers/gpu/drm/drm_auth.c
+++ b/drivers/gpu/drm/drm_auth.c
@@ -135,18 +135,16 @@ static void drm_set_master(struct drm_device *dev, struct drm_file *fpriv,
 static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
 {
 	struct drm_master *old_master;
-	struct drm_master *new_master;
 
 	lockdep_assert_held_once(&dev->master_mutex);
 
 	WARN_ON(fpriv->is_master);
 	old_master = fpriv->master;
-	new_master = drm_master_create(dev);
-	if (!new_master)
+	fpriv->master = drm_master_create(dev);
+	if (!fpriv->master) {
+		fpriv->master = old_master;
 		return -ENOMEM;
-	spin_lock(&fpriv->master_lookup_lock);
-	fpriv->master = new_master;
-	spin_unlock(&fpriv->master_lookup_lock);
+	}
 
 	fpriv->is_master = 1;
 	fpriv->authenticated = 1;
@@ -304,13 +302,10 @@ int drm_master_open(struct drm_file *file_priv)
 	/* if there is no current master make this fd it, but do not create
 	 * any master object for render clients */
 	mutex_lock(&dev->master_mutex);
-	if (!dev->master) {
+	if (!dev->master)
 		ret = drm_new_set_master(dev, file_priv);
-	} else {
-		spin_lock(&file_priv->master_lookup_lock);
+	else
 		file_priv->master = drm_master_get(dev->master);
-		spin_unlock(&file_priv->master_lookup_lock);
-	}
 	mutex_unlock(&dev->master_mutex);
 
 	return ret;
@@ -376,31 +371,6 @@ struct drm_master *drm_master_get(struct drm_master *master)
 }
 EXPORT_SYMBOL(drm_master_get);
 
-/**
- * drm_file_get_master - reference &drm_file.master of @file_priv
- * @file_priv: DRM file private
- *
- * Increments the reference count of @file_priv's &drm_file.master and returns
- * the &drm_file.master. If @file_priv has no &drm_file.master, returns NULL.
- *
- * Master pointers returned from this function should be unreferenced using
- * drm_master_put().
- */
-struct drm_master *drm_file_get_master(struct drm_file *file_priv)
-{
-	struct drm_master *master = NULL;
-
-	spin_lock(&file_priv->master_lookup_lock);
-	if (!file_priv->master)
-		goto unlock;
-	master = drm_master_get(file_priv->master);
-
-unlock:
-	spin_unlock(&file_priv->master_lookup_lock);
-	return master;
-}
-EXPORT_SYMBOL(drm_file_get_master);
-
 static void drm_master_destroy(struct kref *kref)
 {
 	struct drm_master *master = container_of(kref, struct drm_master, refcount);
diff --git a/drivers/gpu/drm/drm_color_mgmt.c b/drivers/gpu/drm/drm_color_mgmt.c
index 138ff34b3..7b270b68a 100644
--- a/drivers/gpu/drm/drm_color_mgmt.c
+++ b/drivers/gpu/drm/drm_color_mgmt.c
@@ -33,7 +33,7 @@
 /**
  * DOC: overview
  *
- * Color management or color space adjustments is supported through a set of 5
+ * Color management or color space adjustments is supported through a set of 7
  * properties on the &drm_crtc object. They are set up by calling
  * drm_crtc_enable_color_mgmt().
  *
@@ -60,7 +60,7 @@
  * “CTM”:
  *	Blob property to set the current transformation matrix (CTM) apply to
  *	pixel data after the lookup through the degamma LUT and before the
- *	lookup through the gamma LUT. The data is interpreted as a struct
+ *	lookup through the cubic LUT. The data is interpreted as a struct
  *	&drm_color_ctm.
  *
  *	Setting this to NULL (blob property value set to 0) means a
@@ -68,13 +68,40 @@
  *	boot-up state too. Drivers can access the blob for the color conversion
  *	matrix through &drm_crtc_state.ctm.
  *
+ * ”CUBIC_LUT”:
+ *	Blob property to set the cubic (3D) lookup table performing color
+ *	mapping after the transformation matrix and before the lookup through
+ *	the gamma LUT. Unlike the degamma and gamma LUTs that map color
+ *	components independently, the 3D LUT converts an input color to an
+ *	output color by indexing into the 3D table using the color components
+ *	as a 3D coordinate. The LUT is subsampled as 8-bit (or more) precision
+ *	would require too much storage space in the hardware, so the precision
+ *	of the color components is reduced before the look up, and the low
+ *	order bits may be used to interpolate between the nearest points in 3D
+ *	space.
+ *
+ *	The data is interpreted as an array of &struct drm_color_lut elements.
+ *	Hardware might choose not to use the full precision of the LUT
+ *	elements.
+ *
+ *	Setting this to NULL (blob property value set to 0) means the output
+ *	color is identical to the input color. This is generally the driver
+ *	boot-up state too. Drivers can access this blob through
+ *	&drm_crtc_state.cubic_lut.
+ *
+ * ”CUBIC_LUT_SIZE”:
+ *	Unsigned range property to give the size of the lookup table to be set
+ *	on the CUBIC_LUT property (the size depends on the underlying hardware).
+ *	If drivers support multiple LUT sizes then they should publish the
+ *	largest size, and sub-sample smaller sized LUTs appropriately.
+ *
  * “GAMMA_LUT”:
  *	Blob property to set the gamma lookup table (LUT) mapping pixel data
- *	after the transformation matrix to data sent to the connector. The
- *	data is interpreted as an array of &struct drm_color_lut elements.
- *	Hardware might choose not to use the full precision of the LUT elements
- *	nor use all the elements of the LUT (for example the hardware might
- *	choose to interpolate between LUT[0] and LUT[4]).
+ *	after the cubic LUT to data sent to the connector. The data is
+ *	interpreted as an array of &struct drm_color_lut elements. Hardware
+ *	might choose not to use the full precision of the LUT elements nor use
+ *	all the elements of the LUT (for example the hardware might choose to
+ *	interpolate between LUT[0] and LUT[4]).
  *
  *	Setting this to NULL (blob property value set to 0) means a
  *	linear/pass-thru gamma table should be used. This is generally the
diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c
index b0a826489..3d7182001 100644
--- a/drivers/gpu/drm/drm_debugfs.c
+++ b/drivers/gpu/drm/drm_debugfs.c
@@ -91,7 +91,6 @@ static int drm_clients_info(struct seq_file *m, void *data)
 	mutex_lock(&dev->filelist_mutex);
 	list_for_each_entry_reverse(priv, &dev->filelist, lhead) {
 		struct task_struct *task;
-		bool is_current_master = drm_is_current_master(priv);
 
 		rcu_read_lock(); /* locks pid_task()->comm */
 		task = pid_task(priv->pid, PIDTYPE_PID);
@@ -100,7 +99,7 @@ static int drm_clients_info(struct seq_file *m, void *data)
 			   task ? task->comm : "<unknown>",
 			   pid_vnr(priv->pid),
 			   priv->minor->index,
-			   is_current_master ? 'y' : 'n',
+			   drm_is_current_master(priv) ? 'y' : 'n',
 			   priv->authenticated ? 'y' : 'n',
 			   from_kuid_munged(seq_user_ns(m), uid),
 			   priv->magic);
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 4334e466b..ab52f7fed 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -1835,20 +1835,11 @@ static void connector_bad_edid(struct drm_connector *connector,
 			       u8 *edid, int num_blocks)
 {
 	int i;
-	u8 last_block;
-
-	/*
-	 * 0x7e in the EDID is the number of extension blocks. The EDID
-	 * is 1 (base block) + num_ext_blocks big. That means we can think
-	 * of 0x7e in the EDID of the _index_ of the last block in the
-	 * combined chunk of memory.
-	 */
-	last_block = edid[0x7e];
+	u8 num_of_ext = edid[0x7e];
 
 	/* Calculate real checksum for the last edid extension block data */
-	if (last_block < num_blocks)
-		connector->real_edid_checksum =
-			drm_edid_block_checksum(edid + last_block * EDID_LENGTH);
+	connector->real_edid_checksum =
+		drm_edid_block_checksum(edid + num_of_ext * EDID_LENGTH);
 
 	if (connector->bad_edid_counter++ && !drm_debug_enabled(DRM_UT_KMS))
 		return;
@@ -4861,6 +4852,43 @@ static void drm_parse_vcdb(struct drm_connector *connector, const u8 *db)
 		info->rgb_quant_range_selectable = true;
 }
 
+#ifdef CONFIG_NO_GKI
+static
+void drm_get_max_frl_rate(int max_frl_rate, u8 *max_lanes, u8 *max_rate_per_lane)
+{
+	switch (max_frl_rate) {
+	case 1:
+		*max_lanes = 3;
+		*max_rate_per_lane = 3;
+		break;
+	case 2:
+		*max_lanes = 3;
+		*max_rate_per_lane = 6;
+		break;
+	case 3:
+		*max_lanes = 4;
+		*max_rate_per_lane = 6;
+		break;
+	case 4:
+		*max_lanes = 4;
+		*max_rate_per_lane = 8;
+		break;
+	case 5:
+		*max_lanes = 4;
+		*max_rate_per_lane = 10;
+		break;
+	case 6:
+		*max_lanes = 4;
+		*max_rate_per_lane = 12;
+		break;
+	case 0:
+	default:
+		*max_lanes = 0;
+		*max_rate_per_lane = 0;
+	}
+}
+#endif
+
 static void drm_parse_ycbcr420_deep_color_info(struct drm_connector *connector,
 					       const u8 *db)
 {
@@ -4914,6 +4942,76 @@ static void drm_parse_hdmi_forum_vsdb(struct drm_connector *connector,
 		}
 	}
 
+#ifdef CONFIG_NO_GKI
+	if (hf_vsdb[7]) {
+		u8 max_frl_rate;
+		u8 dsc_max_frl_rate;
+		u8 dsc_max_slices;
+		struct drm_hdmi_dsc_cap *hdmi_dsc = &hdmi->dsc_cap;
+
+		DRM_DEBUG_KMS("hdmi_21 sink detected. parsing edid\n");
+		max_frl_rate = (hf_vsdb[7] & DRM_EDID_MAX_FRL_RATE_MASK) >> 4;
+		drm_get_max_frl_rate(max_frl_rate, &hdmi->max_lanes,
+				&hdmi->max_frl_rate_per_lane);
+		hdmi_dsc->v_1p2 = hf_vsdb[11] & DRM_EDID_DSC_1P2;
+
+		if (hdmi_dsc->v_1p2) {
+			hdmi_dsc->native_420 = hf_vsdb[11] & DRM_EDID_DSC_NATIVE_420;
+			hdmi_dsc->all_bpp = hf_vsdb[11] & DRM_EDID_DSC_ALL_BPP;
+
+			if (hf_vsdb[11] & DRM_EDID_DSC_16BPC)
+				hdmi_dsc->bpc_supported = 16;
+			else if (hf_vsdb[11] & DRM_EDID_DSC_12BPC)
+				hdmi_dsc->bpc_supported = 12;
+			else if (hf_vsdb[11] & DRM_EDID_DSC_10BPC)
+				hdmi_dsc->bpc_supported = 10;
+			else
+				hdmi_dsc->bpc_supported = 0;
+
+			dsc_max_frl_rate = (hf_vsdb[12] & DRM_EDID_DSC_MAX_FRL_RATE_MASK) >> 4;
+			drm_get_max_frl_rate(dsc_max_frl_rate, &hdmi_dsc->max_lanes,
+					&hdmi_dsc->max_frl_rate_per_lane);
+			hdmi_dsc->total_chunk_kbytes = hf_vsdb[13] & DRM_EDID_DSC_TOTAL_CHUNK_KBYTES;
+
+			dsc_max_slices = hf_vsdb[12] & DRM_EDID_DSC_MAX_SLICES;
+			switch (dsc_max_slices) {
+			case 1:
+				hdmi_dsc->max_slices = 1;
+				hdmi_dsc->clk_per_slice = 340;
+				break;
+			case 2:
+				hdmi_dsc->max_slices = 2;
+				hdmi_dsc->clk_per_slice = 340;
+				break;
+			case 3:
+				hdmi_dsc->max_slices = 4;
+				hdmi_dsc->clk_per_slice = 340;
+				break;
+			case 4:
+				hdmi_dsc->max_slices = 8;
+				hdmi_dsc->clk_per_slice = 340;
+				break;
+			case 5:
+				hdmi_dsc->max_slices = 8;
+				hdmi_dsc->clk_per_slice = 400;
+				break;
+			case 6:
+				hdmi_dsc->max_slices = 12;
+				hdmi_dsc->clk_per_slice = 400;
+				break;
+			case 7:
+				hdmi_dsc->max_slices = 16;
+				hdmi_dsc->clk_per_slice = 400;
+				break;
+			case 0:
+			default:
+				hdmi_dsc->max_slices = 0;
+				hdmi_dsc->clk_per_slice = 0;
+			}
+		}
+	}
+#endif
+
 	drm_parse_ycbcr420_deep_color_info(connector, hf_vsdb);
 }
 
diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c
index 537e7de8e..01670305d 100644
--- a/drivers/gpu/drm/drm_file.c
+++ b/drivers/gpu/drm/drm_file.c
@@ -177,7 +177,6 @@ struct drm_file *drm_file_alloc(struct drm_minor *minor)
 	init_waitqueue_head(&file->event_wait);
 	file->event_space = 4096; /* set aside 4k for event buffer */
 
-	spin_lock_init(&file->master_lookup_lock);
 	mutex_init(&file->event_read_lock);
 
 	if (drm_core_check_feature(dev, DRIVER_GEM))
@@ -776,20 +775,19 @@ void drm_event_cancel_free(struct drm_device *dev,
 EXPORT_SYMBOL(drm_event_cancel_free);
 
 /**
- * drm_send_event_locked - send DRM event to file descriptor
+ * drm_send_event_helper - send DRM event to file descriptor
  * @dev: DRM device
  * @e: DRM event to deliver
+ * @timestamp: timestamp to set for the fence event in kernel's CLOCK_MONOTONIC
+ * time domain
  *
- * This function sends the event @e, initialized with drm_event_reserve_init(),
- * to its associated userspace DRM file. Callers must already hold
- * &drm_device.event_lock, see drm_send_event() for the unlocked version.
- *
- * Note that the core will take care of unlinking and disarming events when the
- * corresponding DRM file is closed. Drivers need not worry about whether the
- * DRM file for this event still exists and can call this function upon
- * completion of the asynchronous work unconditionally.
+ * This helper function sends the event @e, initialized with
+ * drm_event_reserve_init(), to its associated userspace DRM file.
+ * The timestamp variant of dma_fence_signal is used when the caller
+ * sends a valid timestamp.
  */
-void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e)
+void drm_send_event_helper(struct drm_device *dev,
+			   struct drm_pending_event *e, ktime_t timestamp)
 {
 	assert_spin_locked(&dev->event_lock);
 
@@ -800,7 +798,10 @@ void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e)
 	}
 
 	if (e->fence) {
-		dma_fence_signal(e->fence);
+		if (timestamp)
+			dma_fence_signal_timestamp(e->fence, timestamp);
+		else
+			dma_fence_signal(e->fence);
 		dma_fence_put(e->fence);
 	}
 
@@ -815,6 +816,48 @@ void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e)
 	wake_up_interruptible_poll(&e->file_priv->event_wait,
 		EPOLLIN | EPOLLRDNORM);
 }
+
+/**
+ * drm_send_event_timestamp_locked - send DRM event to file descriptor
+ * @dev: DRM device
+ * @e: DRM event to deliver
+ * @timestamp: timestamp to set for the fence event in kernel's CLOCK_MONOTONIC
+ * time domain
+ *
+ * This function sends the event @e, initialized with drm_event_reserve_init(),
+ * to its associated userspace DRM file. Callers must already hold
+ * &drm_device.event_lock.
+ *
+ * Note that the core will take care of unlinking and disarming events when the
+ * corresponding DRM file is closed. Drivers need not worry about whether the
+ * DRM file for this event still exists and can call this function upon
+ * completion of the asynchronous work unconditionally.
+ */
+void drm_send_event_timestamp_locked(struct drm_device *dev,
+				     struct drm_pending_event *e, ktime_t timestamp)
+{
+	drm_send_event_helper(dev, e, timestamp);
+}
+EXPORT_SYMBOL(drm_send_event_timestamp_locked);
+
+/**
+ * drm_send_event_locked - send DRM event to file descriptor
+ * @dev: DRM device
+ * @e: DRM event to deliver
+ *
+ * This function sends the event @e, initialized with drm_event_reserve_init(),
+ * to its associated userspace DRM file. Callers must already hold
+ * &drm_device.event_lock, see drm_send_event() for the unlocked version.
+ *
+ * Note that the core will take care of unlinking and disarming events when the
+ * corresponding DRM file is closed. Drivers need not worry about whether the
+ * DRM file for this event still exists and can call this function upon
+ * completion of the asynchronous work unconditionally.
+ */
+void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e)
+{
+	drm_send_event_helper(dev, e, 0);
+}
 EXPORT_SYMBOL(drm_send_event_locked);
 
 /**
@@ -837,7 +880,7 @@ void drm_send_event(struct drm_device *dev, struct drm_pending_event *e)
 	unsigned long irqflags;
 
 	spin_lock_irqsave(&dev->event_lock, irqflags);
-	drm_send_event_locked(dev, e);
+	drm_send_event_helper(dev, e, 0);
 	spin_unlock_irqrestore(&dev->event_lock, irqflags);
 }
 EXPORT_SYMBOL(drm_send_event);
diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c
index 92152c06b..f20307985 100644
--- a/drivers/gpu/drm/drm_fourcc.c
+++ b/drivers/gpu/drm/drm_fourcc.c
@@ -278,6 +278,16 @@ const struct drm_format_info *__drm_format_info(u32 format)
 		  .num_planes = 2, .char_per_block = { 5, 5, 0 },
 		  .block_w = { 4, 2, 0 }, .block_h = { 1, 1, 0 }, .hsub = 2,
 		  .vsub = 2, .is_yuv = true },
+#ifdef CONFIG_NO_GKI
+		{ .format = DRM_FORMAT_NV20,		.depth = 0,
+		  .num_planes = 2, .char_per_block = { 5, 5, 0 },
+		  .block_w = { 4, 2, 0 }, .block_h = { 1, 1, 0 }, .hsub = 2,
+		  .vsub = 1, .is_yuv = true },
+		{ .format = DRM_FORMAT_NV30,		.depth = 0,
+		  .num_planes = 2, .char_per_block = { 5, 5, 0 },
+		  .block_w = { 4, 2, 0 }, .block_h = { 1, 1, 0 }, .hsub = 1,
+		  .vsub = 1, .is_yuv = true },
+#endif
 		{ .format = DRM_FORMAT_Q410,		.depth = 0,
 		  .num_planes = 3, .char_per_block = { 2, 2, 2 },
 		  .block_w = { 1, 1, 1 }, .block_h = { 1, 1, 1 }, .hsub = 1,
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index c160a45a4..73818ffa0 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -543,6 +543,7 @@ int drm_version(struct drm_device *dev, void *data,
  */
 int drm_ioctl_permit(u32 flags, struct drm_file *file_priv)
 {
+#ifndef CONFIG_DRM_IGNORE_IOTCL_PERMIT
 	/* ROOT_ONLY is only for CAP_SYS_ADMIN */
 	if (unlikely((flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)))
 		return -EACCES;
@@ -561,6 +562,7 @@ int drm_ioctl_permit(u32 flags, struct drm_file *file_priv)
 	if (unlikely(!(flags & DRM_RENDER_ALLOW) &&
 		     drm_is_render_client(file_priv)))
 		return -EACCES;
+#endif
 
 	return 0;
 }
@@ -684,9 +686,9 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
 	DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb_ioctl, 0),
 	DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER),
 	DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER),
-	DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, 0),
-	DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, 0),
-	DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, 0),
+	DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_RENDER_ALLOW),
 	DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, 0),
 	DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER),
 	DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR2, drm_mode_cursor2_ioctl, DRM_MASTER),
diff --git a/drivers/gpu/drm/drm_lease.c b/drivers/gpu/drm/drm_lease.c
index aef226340..da4f085fc 100644
--- a/drivers/gpu/drm/drm_lease.c
+++ b/drivers/gpu/drm/drm_lease.c
@@ -107,19 +107,10 @@ static bool _drm_has_leased(struct drm_master *master, int id)
  */
 bool _drm_lease_held(struct drm_file *file_priv, int id)
 {
-	bool ret;
-	struct drm_master *master;
-
-	if (!file_priv)
+	if (!file_priv || !file_priv->master)
 		return true;
 
-	master = drm_file_get_master(file_priv);
-	if (!master)
-		return true;
-	ret = _drm_lease_held_master(master, id);
-	drm_master_put(&master);
-
-	return ret;
+	return _drm_lease_held_master(file_priv->master, id);
 }
 
 /**
@@ -138,22 +129,13 @@ bool drm_lease_held(struct drm_file *file_priv, int id)
 	struct drm_master *master;
 	bool ret;
 
-	if (!file_priv)
+	if (!file_priv || !file_priv->master || !file_priv->master->lessor)
 		return true;
 
-	master = drm_file_get_master(file_priv);
-	if (!master)
-		return true;
-	if (!master->lessor) {
-		ret = true;
-		goto out;
-	}
+	master = file_priv->master;
 	mutex_lock(&master->dev->mode_config.idr_mutex);
 	ret = _drm_lease_held_master(master, id);
 	mutex_unlock(&master->dev->mode_config.idr_mutex);
-
-out:
-	drm_master_put(&master);
 	return ret;
 }
 
@@ -173,16 +155,10 @@ uint32_t drm_lease_filter_crtcs(struct drm_file *file_priv, uint32_t crtcs_in)
 	int count_in, count_out;
 	uint32_t crtcs_out = 0;
 
-	if (!file_priv)
+	if (!file_priv || !file_priv->master || !file_priv->master->lessor)
 		return crtcs_in;
 
-	master = drm_file_get_master(file_priv);
-	if (!master)
-		return crtcs_in;
-	if (!master->lessor) {
-		crtcs_out = crtcs_in;
-		goto out;
-	}
+	master = file_priv->master;
 	dev = master->dev;
 
 	count_in = count_out = 0;
@@ -201,9 +177,6 @@ uint32_t drm_lease_filter_crtcs(struct drm_file *file_priv, uint32_t crtcs_in)
 		count_in++;
 	}
 	mutex_unlock(&master->dev->mode_config.idr_mutex);
-
-out:
-	drm_master_put(&master);
 	return crtcs_out;
 }
 
@@ -517,7 +490,7 @@ int drm_mode_create_lease_ioctl(struct drm_device *dev,
 	size_t object_count;
 	int ret = 0;
 	struct idr leases;
-	struct drm_master *lessor;
+	struct drm_master *lessor = lessor_priv->master;
 	struct drm_master *lessee = NULL;
 	struct file *lessee_file = NULL;
 	struct file *lessor_file = lessor_priv->filp;
@@ -529,6 +502,12 @@ int drm_mode_create_lease_ioctl(struct drm_device *dev,
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EOPNOTSUPP;
 
+	/* Do not allow sub-leases */
+	if (lessor->lessor) {
+		DRM_DEBUG_LEASE("recursive leasing not allowed\n");
+		return -EINVAL;
+	}
+
 	/* need some objects */
 	if (cl->object_count == 0) {
 		DRM_DEBUG_LEASE("no objects in lease\n");
@@ -540,22 +519,12 @@ int drm_mode_create_lease_ioctl(struct drm_device *dev,
 		return -EINVAL;
 	}
 
-	lessor = drm_file_get_master(lessor_priv);
-	/* Do not allow sub-leases */
-	if (lessor->lessor) {
-		DRM_DEBUG_LEASE("recursive leasing not allowed\n");
-		ret = -EINVAL;
-		goto out_lessor;
-	}
-
 	object_count = cl->object_count;
 
 	object_ids = memdup_user(u64_to_user_ptr(cl->object_ids),
 			array_size(object_count, sizeof(__u32)));
-	if (IS_ERR(object_ids)) {
-		ret = PTR_ERR(object_ids);
-		goto out_lessor;
-	}
+	if (IS_ERR(object_ids))
+		return PTR_ERR(object_ids);
 
 	idr_init(&leases);
 
@@ -566,15 +535,14 @@ int drm_mode_create_lease_ioctl(struct drm_device *dev,
 	if (ret) {
 		DRM_DEBUG_LEASE("lease object lookup failed: %i\n", ret);
 		idr_destroy(&leases);
-		goto out_lessor;
+		return ret;
 	}
 
 	/* Allocate a file descriptor for the lease */
 	fd = get_unused_fd_flags(cl->flags & (O_CLOEXEC | O_NONBLOCK));
 	if (fd < 0) {
 		idr_destroy(&leases);
-		ret = fd;
-		goto out_lessor;
+		return fd;
 	}
 
 	DRM_DEBUG_LEASE("Creating lease\n");
@@ -610,7 +578,6 @@ int drm_mode_create_lease_ioctl(struct drm_device *dev,
 	/* Hook up the fd */
 	fd_install(fd, lessee_file);
 
-	drm_master_put(&lessor);
 	DRM_DEBUG_LEASE("drm_mode_create_lease_ioctl succeeded\n");
 	return 0;
 
@@ -620,8 +587,6 @@ int drm_mode_create_lease_ioctl(struct drm_device *dev,
 out_leases:
 	put_unused_fd(fd);
 
-out_lessor:
-	drm_master_put(&lessor);
 	DRM_DEBUG_LEASE("drm_mode_create_lease_ioctl failed: %d\n", ret);
 	return ret;
 }
@@ -644,7 +609,7 @@ int drm_mode_list_lessees_ioctl(struct drm_device *dev,
 	struct drm_mode_list_lessees *arg = data;
 	__u32 __user *lessee_ids = (__u32 __user *) (uintptr_t) (arg->lessees_ptr);
 	__u32 count_lessees = arg->count_lessees;
-	struct drm_master *lessor, *lessee;
+	struct drm_master *lessor = lessor_priv->master, *lessee;
 	int count;
 	int ret = 0;
 
@@ -655,7 +620,6 @@ int drm_mode_list_lessees_ioctl(struct drm_device *dev,
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EOPNOTSUPP;
 
-	lessor = drm_file_get_master(lessor_priv);
 	DRM_DEBUG_LEASE("List lessees for %d\n", lessor->lessee_id);
 
 	mutex_lock(&dev->mode_config.idr_mutex);
@@ -679,7 +643,6 @@ int drm_mode_list_lessees_ioctl(struct drm_device *dev,
 		arg->count_lessees = count;
 
 	mutex_unlock(&dev->mode_config.idr_mutex);
-	drm_master_put(&lessor);
 
 	return ret;
 }
@@ -699,7 +662,7 @@ int drm_mode_get_lease_ioctl(struct drm_device *dev,
 	struct drm_mode_get_lease *arg = data;
 	__u32 __user *object_ids = (__u32 __user *) (uintptr_t) (arg->objects_ptr);
 	__u32 count_objects = arg->count_objects;
-	struct drm_master *lessee;
+	struct drm_master *lessee = lessee_priv->master;
 	struct idr *object_idr;
 	int count;
 	void *entry;
@@ -713,7 +676,6 @@ int drm_mode_get_lease_ioctl(struct drm_device *dev,
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EOPNOTSUPP;
 
-	lessee = drm_file_get_master(lessee_priv);
 	DRM_DEBUG_LEASE("get lease for %d\n", lessee->lessee_id);
 
 	mutex_lock(&dev->mode_config.idr_mutex);
@@ -741,7 +703,6 @@ int drm_mode_get_lease_ioctl(struct drm_device *dev,
 		arg->count_objects = count;
 
 	mutex_unlock(&dev->mode_config.idr_mutex);
-	drm_master_put(&lessee);
 
 	return ret;
 }
@@ -760,7 +721,7 @@ int drm_mode_revoke_lease_ioctl(struct drm_device *dev,
 				void *data, struct drm_file *lessor_priv)
 {
 	struct drm_mode_revoke_lease *arg = data;
-	struct drm_master *lessor;
+	struct drm_master *lessor = lessor_priv->master;
 	struct drm_master *lessee;
 	int ret = 0;
 
@@ -770,7 +731,6 @@ int drm_mode_revoke_lease_ioctl(struct drm_device *dev,
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EOPNOTSUPP;
 
-	lessor = drm_file_get_master(lessor_priv);
 	mutex_lock(&dev->mode_config.idr_mutex);
 
 	lessee = _drm_find_lessee(lessor, arg->lessee_id);
@@ -791,7 +751,6 @@ int drm_mode_revoke_lease_ioctl(struct drm_device *dev,
 
 fail:
 	mutex_unlock(&dev->mode_config.idr_mutex);
-	drm_master_put(&lessor);
 
 	return ret;
 }
diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index 2c43d5476..d95bf573a 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -356,6 +356,7 @@ static ssize_t mipi_dsi_device_transfer(struct mipi_dsi_device *dsi,
 
 	if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
 		msg->flags |= MIPI_DSI_MSG_USE_LPM;
+	msg->flags |= MIPI_DSI_MSG_LASTCOMMAND;
 
 	return ops->transfer(dsi->host, msg);
 }
diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c
index f1affc1bb..f7f21dfc4 100644
--- a/drivers/gpu/drm/drm_mode_config.c
+++ b/drivers/gpu/drm/drm_mode_config.c
@@ -364,6 +364,22 @@ static int drm_mode_create_standard_properties(struct drm_device *dev)
 		return -ENOMEM;
 	dev->mode_config.gamma_lut_size_property = prop;
 
+#if defined(CONFIG_ROCKCHIP_DRM_CUBIC_LUT)
+	prop = drm_property_create(dev,
+			DRM_MODE_PROP_BLOB,
+			"CUBIC_LUT", 0);
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.cubic_lut_property = prop;
+
+	prop = drm_property_create_range(dev,
+			DRM_MODE_PROP_IMMUTABLE,
+			"CUBIC_LUT_SIZE", 0, UINT_MAX);
+	if (!prop)
+		return -ENOMEM;
+	dev->mode_config.cubic_lut_size_property = prop;
+#endif
+
 	prop = drm_property_create(dev,
 				   DRM_MODE_PROP_IMMUTABLE | DRM_MODE_PROP_BLOB,
 				   "IN_FORMATS", 0);
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 0f99e5453..d42c7310b 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -1940,6 +1940,7 @@ void drm_mode_convert_to_umode(struct drm_mode_modeinfo *out,
 	strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN);
 	out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
 }
+EXPORT_SYMBOL_GPL(drm_mode_convert_to_umode);
 
 /**
  * drm_crtc_convert_umode - convert a modeinfo into a drm_display_mode
@@ -2016,6 +2017,7 @@ int drm_mode_convert_umode(struct drm_device *dev,
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(drm_mode_convert_umode);
 
 /**
  * drm_mode_is_420_only - if a given videomode can be only supported in YCBCR420
diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c
index 825499ea3..272e5cdd6 100644
--- a/drivers/gpu/drm/drm_prime.c
+++ b/drivers/gpu/drm/drm_prime.c
@@ -784,6 +784,28 @@ int drm_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma)
 }
 EXPORT_SYMBOL(drm_gem_dmabuf_mmap);
 
+/**
+ * drm_gem_dmabuf_get_uuid - dma_buf get_uuid implementation for GEM
+ * @dma_buf: buffer to query
+ * @uuid: uuid outparam
+ *
+ * Queries the buffer's virtio UUID. This can be used as the
+ * &dma_buf_ops.get_uuid callback. Calls into &drm_driver.gem_prime_get_uuid.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_gem_dmabuf_get_uuid(struct dma_buf *dma_buf, uuid_t *uuid)
+{
+	struct drm_gem_object *obj = dma_buf->priv;
+	struct drm_device *dev = obj->dev;
+
+	if (!dev->driver->gem_prime_get_uuid)
+		return -ENODEV;
+
+	return dev->driver->gem_prime_get_uuid(obj, uuid);
+}
+EXPORT_SYMBOL(drm_gem_dmabuf_get_uuid);
+
 static const struct dma_buf_ops drm_gem_prime_dmabuf_ops =  {
 	.cache_sgt_mapping = true,
 	.attach = drm_gem_map_attach,
@@ -794,6 +816,7 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops =  {
 	.mmap = drm_gem_dmabuf_mmap,
 	.vmap = drm_gem_dmabuf_vmap,
 	.vunmap = drm_gem_dmabuf_vunmap,
+	.get_uuid = drm_gem_dmabuf_get_uuid,
 };
 
 /**
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
index f135b7959..286edbe1b 100644
--- a/drivers/gpu/drm/drm_vblank.c
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -1000,7 +1000,14 @@ static void send_vblank_event(struct drm_device *dev,
 		break;
 	}
 	trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe, seq);
-	drm_send_event_locked(dev, &e->base);
+	/*
+	 * Use the same timestamp for any associated fence signal to avoid
+	 * mismatch in timestamps for vsync & fence events triggered by the
+	 * same HW event. Frameworks like SurfaceFlinger in Android expects the
+	 * retire-fence timestamp to match exactly with HW vsync as it uses it
+	 * for its software vsync modeling.
+	 */
+	drm_send_event_timestamp_locked(dev, &e->base, now);
 }
 
 /**
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index 1a87cc445..7c7d10225 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -30,6 +30,7 @@
 #include <linux/regulator/consumer.h>
 
 #include <video/display_timing.h>
+#include <video/mipi_display.h>
 #include <video/of_display_timing.h>
 #include <video/videomode.h>
 
@@ -37,6 +38,25 @@
 #include <drm/drm_device.h>
 #include <drm/drm_mipi_dsi.h>
 #include <drm/drm_panel.h>
+#include <drm/drm_dsc.h>
+
+#include <linux/panel-simple.h>
+
+struct panel_cmd_header {
+	u8 data_type;
+	u8 delay;
+	u8 payload_length;
+} __packed;
+
+struct panel_cmd_desc {
+	struct panel_cmd_header header;
+	u8 *payload;
+};
+
+struct panel_cmd_seq {
+	struct panel_cmd_desc *cmds;
+	unsigned int cmd_cnt;
+};
 
 /**
  * @modes: Pointer to array of fixed modes appropriate for this panel.  If
@@ -83,6 +103,10 @@ struct panel_desc {
 	 *           turn the display off (no content is visible)
 	 * @unprepare: the time (in milliseconds) that it takes for the panel
 	 *             to power itself down completely
+	 * @reset: the time (in milliseconds) that it takes for the panel
+	 *         to reset itself completely
+	 * @init: the time (in milliseconds) that it takes for the panel to
+	 *	  send init command sequence after reset deassert
 	 */
 	struct {
 		unsigned int prepare;
@@ -90,17 +114,24 @@ struct panel_desc {
 		unsigned int enable;
 		unsigned int disable;
 		unsigned int unprepare;
+		unsigned int reset;
+		unsigned int init;
 	} delay;
 
 	u32 bus_format;
 	u32 bus_flags;
 	int connector_type;
+
+	struct panel_cmd_seq *init_seq;
+	struct panel_cmd_seq *exit_seq;
 };
 
 struct panel_simple {
 	struct drm_panel base;
+	struct mipi_dsi_device *dsi;
 	bool prepared;
 	bool enabled;
+	bool power_invert;
 	bool no_hpd;
 
 	const struct panel_desc *desc;
@@ -109,10 +140,12 @@ struct panel_simple {
 	struct i2c_adapter *ddc;
 
 	struct gpio_desc *enable_gpio;
+	struct gpio_desc *reset_gpio;
 	struct gpio_desc *hpd_gpio;
 
 	struct drm_display_mode override_mode;
 
+	struct drm_dsc_picture_parameter_set *pps;
 	enum drm_panel_orientation orientation;
 };
 
@@ -121,6 +154,124 @@ static inline struct panel_simple *to_panel_simple(struct drm_panel *panel)
 	return container_of(panel, struct panel_simple, base);
 }
 
+static int panel_simple_parse_cmd_seq(struct device *dev,
+				      const u8 *data, int length,
+				      struct panel_cmd_seq *seq)
+{
+	struct panel_cmd_header *header;
+	struct panel_cmd_desc *desc;
+	char *buf, *d;
+	unsigned int i, cnt, len;
+
+	if (!seq)
+		return -EINVAL;
+
+	buf = devm_kmemdup(dev, data, length, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	d = buf;
+	len = length;
+	cnt = 0;
+	while (len > sizeof(*header)) {
+		header = (struct panel_cmd_header *)d;
+
+		d += sizeof(*header);
+		len -= sizeof(*header);
+
+		if (header->payload_length > len)
+			return -EINVAL;
+
+		d += header->payload_length;
+		len -= header->payload_length;
+		cnt++;
+	}
+
+	if (len)
+		return -EINVAL;
+
+	seq->cmd_cnt = cnt;
+	seq->cmds = devm_kcalloc(dev, cnt, sizeof(*desc), GFP_KERNEL);
+	if (!seq->cmds)
+		return -ENOMEM;
+
+	d = buf;
+	len = length;
+	for (i = 0; i < cnt; i++) {
+		header = (struct panel_cmd_header *)d;
+		len -= sizeof(*header);
+		d += sizeof(*header);
+
+		desc = &seq->cmds[i];
+		desc->header = *header;
+		desc->payload = d;
+
+		d += header->payload_length;
+		len -= header->payload_length;
+	}
+
+	return 0;
+}
+
+static int panel_simple_xfer_dsi_cmd_seq(struct panel_simple *panel,
+					 struct panel_cmd_seq *seq)
+{
+	struct device *dev = panel->base.dev;
+	struct mipi_dsi_device *dsi = panel->dsi;
+	unsigned int i;
+	int err;
+
+	if (!IS_ENABLED(CONFIG_DRM_MIPI_DSI))
+		return -EINVAL;
+	if (!seq)
+		return -EINVAL;
+
+	for (i = 0; i < seq->cmd_cnt; i++) {
+		struct panel_cmd_desc *cmd = &seq->cmds[i];
+
+		switch (cmd->header.data_type) {
+		case MIPI_DSI_COMPRESSION_MODE:
+			err = mipi_dsi_compression_mode(dsi, cmd->payload[0]);
+			break;
+		case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
+		case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
+		case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
+		case MIPI_DSI_GENERIC_LONG_WRITE:
+			err = mipi_dsi_generic_write(dsi, cmd->payload,
+						     cmd->header.payload_length);
+			break;
+		case MIPI_DSI_DCS_SHORT_WRITE:
+		case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
+		case MIPI_DSI_DCS_LONG_WRITE:
+			err = mipi_dsi_dcs_write_buffer(dsi, cmd->payload,
+							cmd->header.payload_length);
+			break;
+		case MIPI_DSI_PICTURE_PARAMETER_SET:
+			if (!panel->pps) {
+				panel->pps = devm_kzalloc(dev, sizeof(*panel->pps),
+							  GFP_KERNEL);
+				if (!panel->pps)
+					return -ENOMEM;
+
+				memcpy(panel->pps, cmd->payload, cmd->header.payload_length);
+			}
+
+			err = mipi_dsi_picture_parameter_set(dsi, panel->pps);
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		if (err < 0)
+			dev_err(dev, "failed to write dcs cmd: %d\n", err);
+
+		if (cmd->header.delay)
+			msleep(cmd->header.delay);
+	}
+
+	return 0;
+}
+
 static unsigned int panel_simple_get_timings_modes(struct panel_simple *panel,
 						   struct drm_connector *connector)
 {
@@ -219,17 +370,72 @@ static int panel_simple_get_non_edid_modes(struct panel_simple *panel,
 	if (num == 0)
 		num = panel_simple_get_display_modes(panel, connector);
 
-	connector->display_info.bpc = panel->desc->bpc;
-	connector->display_info.width_mm = panel->desc->size.width;
-	connector->display_info.height_mm = panel->desc->size.height;
+	if (panel->desc->bpc)
+		connector->display_info.bpc = panel->desc->bpc;
+	if (panel->desc->size.width)
+		connector->display_info.width_mm = panel->desc->size.width;
+	if (panel->desc->size.height)
+		connector->display_info.height_mm = panel->desc->size.height;
 	if (panel->desc->bus_format)
 		drm_display_info_set_bus_formats(&connector->display_info,
 						 &panel->desc->bus_format, 1);
-	connector->display_info.bus_flags = panel->desc->bus_flags;
+	if (panel->desc->bus_flags)
+		connector->display_info.bus_flags = panel->desc->bus_flags;
 
 	return num;
 }
 
+static int panel_simple_regulator_enable(struct panel_simple *p)
+{
+	int err;
+
+	if (p->power_invert) {
+		if (regulator_is_enabled(p->supply) > 0)
+			regulator_disable(p->supply);
+	} else {
+		err = regulator_enable(p->supply);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static int panel_simple_regulator_disable(struct panel_simple *p)
+{
+	int err;
+
+	if (p->power_invert) {
+		if (!regulator_is_enabled(p->supply)) {
+			err = regulator_enable(p->supply);
+			if (err < 0)
+				return err;
+		}
+	} else {
+		regulator_disable(p->supply);
+	}
+
+	return 0;
+}
+
+int panel_simple_loader_protect(struct drm_panel *panel)
+{
+	struct panel_simple *p = to_panel_simple(panel);
+	int err;
+
+	err = panel_simple_regulator_enable(p);
+	if (err < 0) {
+		dev_err(panel->dev, "failed to enable supply: %d\n", err);
+		return err;
+	}
+
+	p->prepared = true;
+	p->enabled = true;
+
+	return 0;
+}
+EXPORT_SYMBOL(panel_simple_loader_protect);
+
 static int panel_simple_disable(struct drm_panel *panel)
 {
 	struct panel_simple *p = to_panel_simple(panel);
@@ -252,9 +458,14 @@ static int panel_simple_unprepare(struct drm_panel *panel)
 	if (!p->prepared)
 		return 0;
 
-	gpiod_set_value_cansleep(p->enable_gpio, 0);
+	if (p->desc->exit_seq)
+		if (p->dsi)
+			panel_simple_xfer_dsi_cmd_seq(p, p->desc->exit_seq);
+
+	gpiod_direction_output(p->reset_gpio, 1);
+	gpiod_direction_output(p->enable_gpio, 0);
 
-	regulator_disable(p->supply);
+	panel_simple_regulator_disable(p);
 
 	if (p->desc->delay.unprepare)
 		msleep(p->desc->delay.unprepare);
@@ -299,13 +510,23 @@ static int panel_simple_prepare(struct drm_panel *panel)
 	if (p->prepared)
 		return 0;
 
-	err = regulator_enable(p->supply);
+	err = panel_simple_regulator_enable(p);
 	if (err < 0) {
 		dev_err(panel->dev, "failed to enable supply: %d\n", err);
 		return err;
 	}
 
-	gpiod_set_value_cansleep(p->enable_gpio, 1);
+	gpiod_direction_output(p->enable_gpio, 1);
+
+	if (p->desc->delay.reset)
+		msleep(p->desc->delay.prepare);
+
+	gpiod_direction_output(p->reset_gpio, 1);
+
+	if (p->desc->delay.reset)
+		msleep(p->desc->delay.reset);
+
+	gpiod_direction_output(p->reset_gpio, 0);
 
 	delay = p->desc->delay.prepare;
 	if (p->no_hpd)
@@ -333,6 +554,13 @@ static int panel_simple_prepare(struct drm_panel *panel)
 		}
 	}
 
+	if (p->desc->init_seq)
+		if (p->dsi)
+			panel_simple_xfer_dsi_cmd_seq(p, p->desc->init_seq);
+
+	if (p->desc->delay.init)
+		msleep(p->desc->delay.init);
+
 	p->prepared = true;
 
 	return 0;
@@ -500,6 +728,52 @@ static void panel_simple_parse_panel_timing_node(struct device *dev,
 		dev_err(dev, "Reject override mode: No display_timing found\n");
 }
 
+static int dcs_bl_update_status(struct backlight_device *bl)
+{
+	struct panel_simple *p = bl_get_data(bl);
+	struct mipi_dsi_device *dsi = p->dsi;
+	int ret;
+
+	if (!p->prepared)
+		return 0;
+
+	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_set_display_brightness(dsi, bl->props.brightness);
+	if (ret < 0)
+		return ret;
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	return 0;
+}
+
+static int dcs_bl_get_brightness(struct backlight_device *bl)
+{
+	struct panel_simple *p = bl_get_data(bl);
+	struct mipi_dsi_device *dsi = p->dsi;
+	u16 brightness = bl->props.brightness;
+	int ret;
+
+	if (!p->prepared)
+		return 0;
+
+	dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+	ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
+	if (ret < 0)
+		return ret;
+
+	dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	return brightness & 0xff;
+}
+
+static const struct backlight_ops dcs_bl_ops = {
+	.update_status = dcs_bl_update_status,
+	.get_brightness = dcs_bl_get_brightness,
+};
+
 static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
 {
 	struct panel_simple *panel;
@@ -528,12 +802,19 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
 	if (IS_ERR(panel->supply))
 		return PTR_ERR(panel->supply);
 
-	panel->enable_gpio = devm_gpiod_get_optional(dev, "enable",
-						     GPIOD_OUT_LOW);
+	panel->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_ASIS);
 	if (IS_ERR(panel->enable_gpio)) {
 		err = PTR_ERR(panel->enable_gpio);
 		if (err != -EPROBE_DEFER)
-			dev_err(dev, "failed to request GPIO: %d\n", err);
+			dev_err(dev, "failed to get enable GPIO: %d\n", err);
+		return err;
+	}
+
+	panel->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
+	if (IS_ERR(panel->reset_gpio)) {
+		err = PTR_ERR(panel->reset_gpio);
+		if (err != -EPROBE_DEFER)
+			dev_err(dev, "failed to get reset GPIO: %d\n", err);
 		return err;
 	}
 
@@ -543,6 +824,8 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
 		return err;
 	}
 
+	panel->power_invert = of_property_read_bool(dev->of_node, "power-invert");
+
 	ddc = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0);
 	if (ddc) {
 		panel->ddc = of_find_i2c_adapter_by_node(ddc);
@@ -557,7 +840,6 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
 		err = panel_dpi_probe(dev, panel);
 		if (err)
 			goto free_ddc;
-		desc = panel->desc;
 	} else {
 		if (!of_get_display_timing(dev->of_node, "panel-timing", &dt))
 			panel_simple_parse_panel_timing_node(dev, panel, &dt);
@@ -567,7 +849,7 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
 	/* Catch common mistakes for panels. */
 	switch (connector_type) {
 	case 0:
-		dev_warn(dev, "Specify missing connector_type\n");
+		dev_dbg(dev, "Specify missing connector_type\n");
 		connector_type = DRM_MODE_CONNECTOR_DPI;
 		break;
 	case DRM_MODE_CONNECTOR_LVDS:
@@ -3906,6 +4188,9 @@ static const struct panel_desc arm_rtsm = {
 
 static const struct of_device_id platform_of_match[] = {
 	{
+		.compatible = "simple-panel",
+		.data = NULL,
+	}, {
 		.compatible = "ampire,am-1280800n3tzqw-t00h",
 		.data = &ampire_am_1280800n3tzqw_t00h,
 	}, {
@@ -4314,15 +4599,132 @@ static const struct of_device_id platform_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, platform_of_match);
 
+static bool of_child_node_is_present(const struct device_node *node,
+				     const char *name)
+{
+	struct device_node *child;
+
+	child = of_get_child_by_name(node, name);
+	of_node_put(child);
+
+	return !!child;
+}
+
+static int panel_simple_of_get_desc_data(struct device *dev,
+					 struct panel_desc *desc)
+{
+	struct device_node *np = dev->of_node;
+	u32 bus_flags;
+	const void *data;
+	int len;
+	int err;
+
+	if (of_child_node_is_present(np, "display-timings")) {
+		struct drm_display_mode *mode;
+
+		mode = devm_kzalloc(dev, sizeof(*mode), GFP_KERNEL);
+		if (!mode)
+			return -ENOMEM;
+
+		if (!of_get_drm_display_mode(np, mode, &bus_flags,
+					     OF_USE_NATIVE_MODE)) {
+			desc->modes = mode;
+			desc->num_modes = 1;
+			desc->bus_flags = bus_flags;
+		}
+	} else if (of_child_node_is_present(np, "panel-timing")) {
+		struct display_timing *timing;
+		struct videomode vm;
+
+		timing = devm_kzalloc(dev, sizeof(*timing), GFP_KERNEL);
+		if (!timing)
+			return -ENOMEM;
+
+		if (!of_get_display_timing(np, "panel-timing", timing)) {
+			desc->timings = timing;
+			desc->num_timings = 1;
+
+			bus_flags = 0;
+			vm.flags = timing->flags;
+			drm_bus_flags_from_videomode(&vm, &bus_flags);
+			desc->bus_flags = bus_flags;
+		}
+	}
+
+	if (desc->num_modes || desc->num_timings) {
+		of_property_read_u32(np, "bpc", &desc->bpc);
+		of_property_read_u32(np, "bus-format", &desc->bus_format);
+		of_property_read_u32(np, "width-mm", &desc->size.width);
+		of_property_read_u32(np, "height-mm", &desc->size.height);
+	}
+
+	of_property_read_u32(np, "prepare-delay-ms", &desc->delay.prepare);
+	of_property_read_u32(np, "enable-delay-ms", &desc->delay.enable);
+	of_property_read_u32(np, "disable-delay-ms", &desc->delay.disable);
+	of_property_read_u32(np, "unprepare-delay-ms", &desc->delay.unprepare);
+	of_property_read_u32(np, "reset-delay-ms", &desc->delay.reset);
+	of_property_read_u32(np, "init-delay-ms", &desc->delay.init);
+
+	data = of_get_property(np, "panel-init-sequence", &len);
+	if (data) {
+		desc->init_seq = devm_kzalloc(dev, sizeof(*desc->init_seq),
+					      GFP_KERNEL);
+		if (!desc->init_seq)
+			return -ENOMEM;
+
+		err = panel_simple_parse_cmd_seq(dev, data, len,
+						 desc->init_seq);
+		if (err) {
+			dev_err(dev, "failed to parse init sequence\n");
+			return err;
+		}
+	}
+
+	data = of_get_property(np, "panel-exit-sequence", &len);
+	if (data) {
+		desc->exit_seq = devm_kzalloc(dev, sizeof(*desc->exit_seq),
+					      GFP_KERNEL);
+		if (!desc->exit_seq)
+			return -ENOMEM;
+
+		err = panel_simple_parse_cmd_seq(dev, data, len,
+						 desc->exit_seq);
+		if (err) {
+			dev_err(dev, "failed to parse exit sequence\n");
+			return err;
+		}
+	}
+
+	return 0;
+}
+
 static int panel_simple_platform_probe(struct platform_device *pdev)
 {
+	struct device *dev = &pdev->dev;
 	const struct of_device_id *id;
+	const struct panel_desc *desc;
+	struct panel_desc *d;
+	int err;
 
 	id = of_match_node(platform_of_match, pdev->dev.of_node);
 	if (!id)
 		return -ENODEV;
 
-	return panel_simple_probe(&pdev->dev, id->data);
+	if (!id->data) {
+		d = devm_kzalloc(dev, sizeof(*d), GFP_KERNEL);
+		if (!d)
+			return -ENOMEM;
+
+		err = panel_simple_of_get_desc_data(dev, d);
+		if (err) {
+			dev_err(dev, "failed to get desc data: %d\n", err);
+			return err;
+		}
+	}
+
+	desc = id->data ? id->data : d;
+
+	return panel_simple_probe(&pdev->dev, desc);
 }
 
 static int panel_simple_platform_remove(struct platform_device *pdev)
@@ -4557,6 +4959,9 @@ static const struct panel_desc_dsi osd101t2045_53ts = {
 
 static const struct of_device_id dsi_of_match[] = {
 	{
+		.compatible = "simple-panel-dsi",
+		.data = NULL,
+	}, {
 		.compatible = "auo,b080uan01",
 		.data = &auo_b080uan01
 	}, {
@@ -4583,9 +4988,33 @@ static const struct of_device_id dsi_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, dsi_of_match);
 
+static int panel_simple_dsi_of_get_desc_data(struct device *dev,
+					     struct panel_desc_dsi *desc)
+{
+	struct device_node *np = dev->of_node;
+	u32 val;
+	int err;
+
+	err = panel_simple_of_get_desc_data(dev, &desc->desc);
+	if (err)
+		return err;
+
+	if (!of_property_read_u32(np, "dsi,flags", &val))
+		desc->flags = val;
+	if (!of_property_read_u32(np, "dsi,format", &val))
+		desc->format = val;
+	if (!of_property_read_u32(np, "dsi,lanes", &val))
+		desc->lanes = val;
+
+	return 0;
+}
+
 static int panel_simple_dsi_probe(struct mipi_dsi_device *dsi)
 {
+	struct panel_simple *panel;
+	struct device *dev = &dsi->dev;
 	const struct panel_desc_dsi *desc;
+	struct panel_desc_dsi *d;
 	const struct of_device_id *id;
 	int err;
 
@@ -4593,12 +5022,47 @@ static int panel_simple_dsi_probe(struct mipi_dsi_device *dsi)
 	if (!id)
 		return -ENODEV;
 
-	desc = id->data;
+	if (!id->data) {
+		d = devm_kzalloc(dev, sizeof(*d), GFP_KERNEL);
+		if (!d)
+			return -ENOMEM;
+
+		err = panel_simple_dsi_of_get_desc_data(dev, d);
+		if (err) {
+			dev_err(dev, "failed to get desc data: %d\n", err);
+			return err;
+		}
+	}
+
+	desc = id->data ? id->data : d;
 
 	err = panel_simple_probe(&dsi->dev, &desc->desc);
 	if (err < 0)
 		return err;
 
+	panel = dev_get_drvdata(dev);
+	panel->dsi = dsi;
+
+	if (!panel->base.backlight) {
+		struct backlight_properties props;
+
+		memset(&props, 0, sizeof(props));
+		props.type = BACKLIGHT_RAW;
+		props.brightness = 255;
+		props.max_brightness = 255;
+
+		panel->base.backlight =
+			devm_backlight_device_register(dev, "dcs-backlight",
+						       dev, panel, &dcs_bl_ops,
+						       &props);
+		if (IS_ERR(panel->base.backlight)) {
+			err = PTR_ERR(panel->base.backlight);
+			dev_err(dev, "failed to register dcs backlight: %d\n",
+				err);
+			return err;
+		}
+	}
+
 	dsi->mode_flags = desc->flags;
 	dsi->format = desc->format;
 	dsi->lanes = desc->lanes;
diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index 512581698..4726b16a3 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -16,6 +16,7 @@
 #include <linux/reset.h>
 #include <linux/clk.h>
 
+#include <uapi/linux/videodev2.h>
 #include <video/of_videomode.h>
 #include <video/videomode.h>
 
@@ -31,54 +32,163 @@
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_vop.h"
 
-#define RK3288_GRF_SOC_CON6		0x25c
-#define RK3288_EDP_LCDC_SEL		BIT(5)
-#define RK3399_GRF_SOC_CON20		0x6250
-#define RK3399_EDP_LCDC_SEL		BIT(5)
-
-#define HIWORD_UPDATE(val, mask)	(val | (mask) << 16)
-
 #define PSR_WAIT_LINE_FLAG_TIMEOUT_MS	100
 
 #define to_dp(nm)	container_of(nm, struct rockchip_dp_device, nm)
 
+#define GRF_REG_FIELD(_reg, _lsb, _msb) {	\
+				.reg = _reg,	\
+				.lsb = _lsb,	\
+				.msb = _msb,	\
+				.valid = true,	\
+				}
+
+struct rockchip_grf_reg_field {
+	unsigned int reg;
+	unsigned int lsb;
+	unsigned int msb;
+	bool valid;
+};
+
 /**
  * struct rockchip_dp_chip_data - splite the grf setting of kind of chips
- * @lcdsel_grf_reg: grf register offset of lcdc select
- * @lcdsel_big: reg value of selecting vop big for eDP
- * @lcdsel_lit: reg value of selecting vop little for eDP
+ * @lcdc_sel: grf register field of lcdc_sel
+ * @spdif_sel: grf register field of spdif_sel
+ * @i2s_sel: grf register field of i2s_sel
+ * @edp_mode: grf register field of edp_mode
  * @chip_type: specific chip type
+ * @ssc: check if SSC is supported by source
+ * @audio: check if audio is supported by source
+ * @split_mode: check if split mode is supported
  */
 struct rockchip_dp_chip_data {
-	u32	lcdsel_grf_reg;
-	u32	lcdsel_big;
-	u32	lcdsel_lit;
+	const struct rockchip_grf_reg_field lcdc_sel;
+	const struct rockchip_grf_reg_field spdif_sel;
+	const struct rockchip_grf_reg_field i2s_sel;
+	const struct rockchip_grf_reg_field edp_mode;
 	u32	chip_type;
+	bool	ssc;
+	bool	audio;
+	bool	split_mode;
 };
 
 struct rockchip_dp_device {
 	struct drm_device        *drm_dev;
 	struct device            *dev;
 	struct drm_encoder       encoder;
+	struct drm_bridge	 *bridge;
 	struct drm_display_mode  mode;
 
-	struct clk               *pclk;
-	struct clk               *grfclk;
 	struct regmap            *grf;
 	struct reset_control     *rst;
+	struct reset_control     *apb_reset;
 
+	struct platform_device *audio_pdev;
 	const struct rockchip_dp_chip_data *data;
+	int id;
 
 	struct analogix_dp_device *adp;
 	struct analogix_dp_plat_data plat_data;
+	struct rockchip_drm_sub_dev sub_dev;
 };
 
+static int rockchip_grf_write(struct regmap *grf, unsigned int reg,
+			      unsigned int mask, unsigned int val)
+{
+	return regmap_write(grf, reg, (mask << 16) | (val & mask));
+}
+
+static int rockchip_grf_field_write(struct regmap *grf,
+				    const struct rockchip_grf_reg_field *field,
+				    unsigned int val)
+{
+	unsigned int mask;
+
+	if (!field->valid)
+		return 0;
+
+	mask = GENMASK(field->msb, field->lsb);
+	val <<= field->lsb;
+
+	return rockchip_grf_write(grf, field->reg, mask, val);
+}
+
+static int rockchip_dp_audio_hw_params(struct device *dev, void *data,
+				       struct hdmi_codec_daifmt *daifmt,
+				       struct hdmi_codec_params *params)
+{
+	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
+
+	rockchip_grf_field_write(dp->grf, &dp->data->spdif_sel,
+				 daifmt->fmt == HDMI_SPDIF);
+	rockchip_grf_field_write(dp->grf, &dp->data->i2s_sel,
+				 daifmt->fmt == HDMI_I2S);
+
+	return analogix_dp_audio_hw_params(dp->adp, daifmt, params);
+}
+
+static void rockchip_dp_audio_shutdown(struct device *dev, void *data)
+{
+	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
+
+	analogix_dp_audio_shutdown(dp->adp);
+
+	rockchip_grf_field_write(dp->grf, &dp->data->spdif_sel, 0);
+	rockchip_grf_field_write(dp->grf, &dp->data->i2s_sel, 0);
+}
+
+static int rockchip_dp_audio_startup(struct device *dev, void *data)
+{
+	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
+
+	return analogix_dp_audio_startup(dp->adp);
+}
+
+static int rockchip_dp_audio_get_eld(struct device *dev, void *data,
+				     u8 *buf, size_t len)
+{
+	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
+
+	return analogix_dp_audio_get_eld(dp->adp, buf, len);
+}
+
+static const struct hdmi_codec_ops rockchip_dp_audio_codec_ops = {
+	.hw_params = rockchip_dp_audio_hw_params,
+	.audio_startup = rockchip_dp_audio_startup,
+	.audio_shutdown = rockchip_dp_audio_shutdown,
+	.get_eld = rockchip_dp_audio_get_eld,
+};
+
+static int rockchip_dp_match_by_id(struct device *dev, const void *data)
+{
+	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
+	const unsigned int *id = data;
+
+	return dp->id == *id;
+}
+
+static struct rockchip_dp_device *
+rockchip_dp_find_by_id(struct device_driver *drv, unsigned int id)
+{
+	struct device *dev;
+
+	dev = driver_find_device(drv, NULL, &id, rockchip_dp_match_by_id);
+	if (!dev)
+		return NULL;
+
+	return dev_get_drvdata(dev);
+}
+
 static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
 {
 	reset_control_assert(dp->rst);
 	usleep_range(10, 20);
 	reset_control_deassert(dp->rst);
 
+	reset_control_assert(dp->apb_reset);
+	usleep_range(10, 20);
+	reset_control_deassert(dp->apb_reset);
+
 	return 0;
 }
 
@@ -87,29 +197,20 @@ static int rockchip_dp_poweron_start(struct analogix_dp_plat_data *plat_data)
 	struct rockchip_dp_device *dp = to_dp(plat_data);
 	int ret;
 
-	ret = clk_prepare_enable(dp->pclk);
-	if (ret < 0) {
-		DRM_DEV_ERROR(dp->dev, "failed to enable pclk %d\n", ret);
-		return ret;
-	}
-
 	ret = rockchip_dp_pre_init(dp);
 	if (ret < 0) {
 		DRM_DEV_ERROR(dp->dev, "failed to dp pre init %d\n", ret);
-		clk_disable_unprepare(dp->pclk);
 		return ret;
 	}
 
-	return ret;
+	return rockchip_grf_field_write(dp->grf, &dp->data->edp_mode, 1);
 }
 
 static int rockchip_dp_powerdown(struct analogix_dp_plat_data *plat_data)
 {
 	struct rockchip_dp_device *dp = to_dp(plat_data);
 
-	clk_disable_unprepare(dp->pclk);
-
-	return 0;
+	return rockchip_grf_field_write(dp->grf, &dp->data->edp_mode, 0);
 }
 
 static int rockchip_dp_get_modes(struct analogix_dp_plat_data *plat_data,
@@ -129,6 +230,56 @@ static int rockchip_dp_get_modes(struct analogix_dp_plat_data *plat_data,
 	return 0;
 }
 
+static void rockchip_dp_loader_protect(struct drm_encoder *encoder, bool on)
+{
+	struct rockchip_dp_device *dp = to_dp(encoder);
+	struct analogix_dp_plat_data *plat_data = &dp->plat_data;
+
+	if (!on)
+		return;
+
+	if (plat_data->panel)
+		panel_simple_loader_protect(plat_data->panel);
+
+	analogix_dp_loader_protect(dp->adp);
+}
+
+static int rockchip_dp_bridge_attach(struct analogix_dp_plat_data *plat_data,
+				     struct drm_bridge *bridge,
+				     struct drm_connector *connector)
+{
+	struct rockchip_dp_device *dp = to_dp(plat_data);
+	struct rockchip_drm_sub_dev *sdev = &dp->sub_dev;
+	int ret;
+
+	if (dp->bridge) {
+		ret = drm_bridge_attach(&dp->encoder, dp->bridge, bridge, 0);
+		if (ret) {
+			DRM_ERROR("Failed to attach bridge to drm: %d\n", ret);
+			return ret;
+		}
+	}
+
+	if (connector) {
+		sdev->connector = connector;
+		sdev->of_node = dp->dev->of_node;
+		sdev->loader_protect = rockchip_dp_loader_protect;
+		rockchip_drm_register_sub_dev(sdev);
+	}
+
+	return 0;
+}
+
+static void rockchip_dp_bridge_detach(struct analogix_dp_plat_data *plat_data,
+				      struct drm_bridge *bridge)
+{
+	struct rockchip_dp_device *dp = to_dp(plat_data);
+	struct rockchip_drm_sub_dev *sdev = &dp->sub_dev;
+
+	if (sdev->connector)
+		rockchip_drm_unregister_sub_dev(sdev);
+}
+
 static bool
 rockchip_dp_drm_encoder_mode_fixup(struct drm_encoder *encoder,
 				   const struct drm_display_mode *mode,
@@ -170,7 +321,6 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder,
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *old_crtc_state;
 	int ret;
-	u32 val;
 
 	crtc = rockchip_dp_drm_get_new_crtc(encoder, state);
 	if (!crtc)
@@ -185,24 +335,11 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder,
 	if (ret < 0)
 		return;
 
-	if (ret)
-		val = dp->data->lcdsel_lit;
-	else
-		val = dp->data->lcdsel_big;
-
 	DRM_DEV_DEBUG(dp->dev, "vop %s output to dp\n", (ret) ? "LIT" : "BIG");
 
-	ret = clk_prepare_enable(dp->grfclk);
-	if (ret < 0) {
-		DRM_DEV_ERROR(dp->dev, "failed to enable grfclk %d\n", ret);
-		return;
-	}
-
-	ret = regmap_write(dp->grf, dp->data->lcdsel_grf_reg, val);
+	ret = rockchip_grf_field_write(dp->grf, &dp->data->lcdc_sel, ret);
 	if (ret != 0)
 		DRM_DEV_ERROR(dp->dev, "Could not write to GRF: %d\n", ret);
-
-	clk_disable_unprepare(dp->grfclk);
 }
 
 static void rockchip_dp_drm_encoder_disable(struct drm_encoder *encoder,
@@ -233,9 +370,15 @@ rockchip_dp_drm_encoder_atomic_check(struct drm_encoder *encoder,
 				      struct drm_crtc_state *crtc_state,
 				      struct drm_connector_state *conn_state)
 {
+	struct rockchip_dp_device *dp = to_dp(encoder);
 	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
 	struct drm_display_info *di = &conn_state->connector->display_info;
 
+	if (di->num_bus_formats)
+		s->bus_format = di->bus_formats[0];
+	else
+		s->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+
 	/*
 	 * The hardware IC designed that VOP must output the RGB10 video
 	 * format to eDP controller, and if eDP panel only support RGB8,
@@ -246,7 +389,18 @@ rockchip_dp_drm_encoder_atomic_check(struct drm_encoder *encoder,
 
 	s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
 	s->output_type = DRM_MODE_CONNECTOR_eDP;
+	if (dp->plat_data.split_mode) {
+		s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE;
+		s->output_flags |= dp->id ? ROCKCHIP_OUTPUT_DATA_SWAP : 0;
+		s->output_if |= VOP_OUTPUT_IF_eDP0 | VOP_OUTPUT_IF_eDP1;
+	} else {
+		s->output_if |= dp->id ? VOP_OUTPUT_IF_eDP1 : VOP_OUTPUT_IF_eDP0;
+	}
 	s->output_bpc = di->bpc;
+	s->bus_flags = di->bus_flags;
+	s->tv_state = &conn_state->tv;
+	s->eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR;
+	s->color_space = V4L2_COLORSPACE_DEFAULT;
 
 	return 0;
 }
@@ -264,26 +418,12 @@ static int rockchip_dp_of_probe(struct rockchip_dp_device *dp)
 	struct device *dev = dp->dev;
 	struct device_node *np = dev->of_node;
 
-	dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
-	if (IS_ERR(dp->grf)) {
-		DRM_DEV_ERROR(dev, "failed to get rockchip,grf property\n");
-		return PTR_ERR(dp->grf);
-	}
-
-	dp->grfclk = devm_clk_get(dev, "grf");
-	if (PTR_ERR(dp->grfclk) == -ENOENT) {
-		dp->grfclk = NULL;
-	} else if (PTR_ERR(dp->grfclk) == -EPROBE_DEFER) {
-		return -EPROBE_DEFER;
-	} else if (IS_ERR(dp->grfclk)) {
-		DRM_DEV_ERROR(dev, "failed to get grf clock\n");
-		return PTR_ERR(dp->grfclk);
-	}
-
-	dp->pclk = devm_clk_get(dev, "pclk");
-	if (IS_ERR(dp->pclk)) {
-		DRM_DEV_ERROR(dev, "failed to get pclk property\n");
-		return PTR_ERR(dp->pclk);
+	if (of_property_read_bool(np, "rockchip,grf")) {
+		dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+		if (IS_ERR(dp->grf)) {
+			DRM_DEV_ERROR(dev, "failed to get rockchip,grf\n");
+			return PTR_ERR(dp->grf);
+		}
 	}
 
 	dp->rst = devm_reset_control_get(dev, "dp");
@@ -292,6 +432,12 @@ static int rockchip_dp_of_probe(struct rockchip_dp_device *dp)
 		return PTR_ERR(dp->rst);
 	}
 
+	dp->apb_reset = devm_reset_control_get_optional(dev, "apb");
+	if (IS_ERR(dp->apb_reset)) {
+		DRM_DEV_ERROR(dev, "failed to get apb reset control\n");
+		return PTR_ERR(dp->apb_reset);
+	}
+
 	return 0;
 }
 
@@ -302,8 +448,8 @@ static int rockchip_dp_drm_create_encoder(struct rockchip_dp_device *dp)
 	struct device *dev = dp->dev;
 	int ret;
 
-	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
-							     dev->of_node);
+	encoder->possible_crtcs = rockchip_drm_of_find_possible_crtcs(drm_dev,
+								      dev->of_node);
 	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
 
 	ret = drm_simple_encoder_init(drm_dev, encoder,
@@ -327,19 +473,44 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
 
 	dp->drm_dev = drm_dev;
 
-	ret = rockchip_dp_drm_create_encoder(dp);
-	if (ret) {
-		DRM_ERROR("failed to create drm encoder\n");
-		return ret;
+	if (!dp->plat_data.left) {
+		ret = rockchip_dp_drm_create_encoder(dp);
+		if (ret) {
+			DRM_ERROR("failed to create drm encoder\n");
+			return ret;
+		}
+
+		dp->plat_data.encoder = &dp->encoder;
 	}
 
-	dp->plat_data.encoder = &dp->encoder;
+	if (dp->data->audio) {
+		struct hdmi_codec_pdata codec_data = {
+			.ops = &rockchip_dp_audio_codec_ops,
+			.spdif = 1,
+			.i2s = 1,
+			.max_i2s_channels = 2,
+		};
+
+		dp->audio_pdev =
+			platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
+						      PLATFORM_DEVID_AUTO,
+						      &codec_data,
+						      sizeof(codec_data));
+		if (IS_ERR(dp->audio_pdev)) {
+			ret = PTR_ERR(dp->audio_pdev);
+			goto err_cleanup_encoder;
+		}
+	}
 
 	ret = analogix_dp_bind(dp->adp, drm_dev);
 	if (ret)
-		goto err_cleanup_encoder;
+		goto err_unregister_audio_pdev;
 
 	return 0;
+
+err_unregister_audio_pdev:
+	if (dp->audio_pdev)
+		platform_device_unregister(dp->audio_pdev);
 err_cleanup_encoder:
 	dp->encoder.funcs->destroy(&dp->encoder);
 	return ret;
@@ -350,6 +521,8 @@ static void rockchip_dp_unbind(struct device *dev, struct device *master,
 {
 	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
 
+	if (dp->audio_pdev)
+		platform_device_unregister(dp->audio_pdev);
 	analogix_dp_unbind(dp->adp);
 	dp->encoder.funcs->destroy(&dp->encoder);
 }
@@ -364,29 +537,51 @@ static int rockchip_dp_probe(struct platform_device *pdev)
 	struct device *dev = &pdev->dev;
 	const struct rockchip_dp_chip_data *dp_data;
 	struct drm_panel *panel = NULL;
+	struct drm_bridge *bridge = NULL;
 	struct rockchip_dp_device *dp;
-	int ret;
+	int id, i, ret;
 
 	dp_data = of_device_get_match_data(dev);
 	if (!dp_data)
 		return -ENODEV;
 
-	ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, NULL);
-	if (ret < 0)
+	ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, &bridge);
+	if (ret < 0 && ret != -ENODEV)
 		return ret;
 
 	dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
 	if (!dp)
 		return -ENOMEM;
 
+	id = of_alias_get_id(dev->of_node, "edp");
+	if (id < 0)
+		id = 0;
+
+	i = 0;
+	while (is_rockchip(dp_data[i].chip_type))
+		i++;
+
+	if (id >= i) {
+		dev_err(dev, "invalid id: %d\n", id);
+		return -ENODEV;
+	}
+
 	dp->dev = dev;
+	dp->id = id;
 	dp->adp = ERR_PTR(-ENODEV);
-	dp->data = dp_data;
+	dp->data = &dp_data[id];
+	dp->plat_data.ssc = dp->data->ssc;
 	dp->plat_data.panel = panel;
 	dp->plat_data.dev_type = dp->data->chip_type;
 	dp->plat_data.power_on_start = rockchip_dp_poweron_start;
 	dp->plat_data.power_off = rockchip_dp_powerdown;
 	dp->plat_data.get_modes = rockchip_dp_get_modes;
+	dp->plat_data.attach = rockchip_dp_bridge_attach;
+	dp->plat_data.detach = rockchip_dp_bridge_detach;
+	dp->plat_data.convert_to_split_mode = drm_mode_convert_to_split_mode;
+	dp->plat_data.convert_to_origin_mode = drm_mode_convert_to_origin_mode;
+	dp->plat_data.skip_connector = !!bridge;
+	dp->bridge = bridge;
 
 	ret = rockchip_dp_of_probe(dp);
 	if (ret < 0)
@@ -398,15 +593,19 @@ static int rockchip_dp_probe(struct platform_device *pdev)
 	if (IS_ERR(dp->adp))
 		return PTR_ERR(dp->adp);
 
-	ret = component_add(dev, &rockchip_dp_component_ops);
-	if (ret)
-		goto err_dp_remove;
+	if (dp->data->split_mode && device_property_read_bool(dev, "split-mode")) {
+		struct rockchip_dp_device *secondary =
+				rockchip_dp_find_by_id(dev->driver, !dp->id);
+		if (!secondary)
+			return -EPROBE_DEFER;
 
-	return 0;
+		dp->plat_data.right = secondary->adp;
+		dp->plat_data.split_mode = true;
+		secondary->plat_data.left = dp->adp;
+		secondary->plat_data.split_mode = true;
+	}
 
-err_dp_remove:
-	analogix_dp_remove(dp->adp);
-	return ret;
+	return component_add(dev, &rockchip_dp_component_ops);
 }
 
 static int rockchip_dp_remove(struct platform_device *pdev)
@@ -419,52 +618,85 @@ static int rockchip_dp_remove(struct platform_device *pdev)
 	return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int rockchip_dp_suspend(struct device *dev)
+static __maybe_unused int rockchip_dp_runtime_suspend(struct device *dev)
 {
 	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
 
 	if (IS_ERR(dp->adp))
 		return 0;
 
-	return analogix_dp_suspend(dp->adp);
+	return analogix_dp_runtime_suspend(dp->adp);
 }
 
-static int rockchip_dp_resume(struct device *dev)
+static __maybe_unused int rockchip_dp_runtime_resume(struct device *dev)
 {
 	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
 
 	if (IS_ERR(dp->adp))
 		return 0;
 
-	return analogix_dp_resume(dp->adp);
+	return analogix_dp_runtime_resume(dp->adp);
 }
-#endif
 
 static const struct dev_pm_ops rockchip_dp_pm_ops = {
-#ifdef CONFIG_PM_SLEEP
-	.suspend_late = rockchip_dp_suspend,
-	.resume_early = rockchip_dp_resume,
-#endif
+	SET_RUNTIME_PM_OPS(rockchip_dp_runtime_suspend,
+			   rockchip_dp_runtime_resume, NULL)
 };
 
-static const struct rockchip_dp_chip_data rk3399_edp = {
-	.lcdsel_grf_reg = RK3399_GRF_SOC_CON20,
-	.lcdsel_big = HIWORD_UPDATE(0, RK3399_EDP_LCDC_SEL),
-	.lcdsel_lit = HIWORD_UPDATE(RK3399_EDP_LCDC_SEL, RK3399_EDP_LCDC_SEL),
-	.chip_type = RK3399_EDP,
+static const struct rockchip_dp_chip_data rk3399_edp[] = {
+	{
+		.chip_type = RK3399_EDP,
+		.lcdc_sel = GRF_REG_FIELD(0x6250, 5, 5),
+		.ssc = true,
+	},
+	{ /* sentinel */ }
 };
 
-static const struct rockchip_dp_chip_data rk3288_dp = {
-	.lcdsel_grf_reg = RK3288_GRF_SOC_CON6,
-	.lcdsel_big = HIWORD_UPDATE(0, RK3288_EDP_LCDC_SEL),
-	.lcdsel_lit = HIWORD_UPDATE(RK3288_EDP_LCDC_SEL, RK3288_EDP_LCDC_SEL),
-	.chip_type = RK3288_DP,
+static const struct rockchip_dp_chip_data rk3288_dp[] = {
+	{
+		.chip_type = RK3288_DP,
+		.lcdc_sel = GRF_REG_FIELD(0x025c, 5, 5),
+		.ssc = true,
+	},
+	{ /* sentinel */ }
+};
+
+static const struct rockchip_dp_chip_data rk3568_edp[] = {
+	{
+		.chip_type = RK3568_EDP,
+		.ssc = true,
+		.audio = true,
+	},
+	{ /* sentinel */ }
+};
+
+static const struct rockchip_dp_chip_data rk3588_edp[] = {
+	{
+		.chip_type = RK3588_EDP,
+		.spdif_sel = GRF_REG_FIELD(0x0000, 4, 4),
+		.i2s_sel = GRF_REG_FIELD(0x0000, 3, 3),
+		.edp_mode = GRF_REG_FIELD(0x0000, 0, 0),
+		.ssc = true,
+		.audio = true,
+		.split_mode = true,
+	},
+	{
+		.chip_type = RK3588_EDP,
+		.spdif_sel = GRF_REG_FIELD(0x0004, 4, 4),
+		.i2s_sel = GRF_REG_FIELD(0x0004, 3, 3),
+		.edp_mode = GRF_REG_FIELD(0x0004, 0, 0),
+		.ssc = true,
+		.audio = true,
+		.split_mode = true,
+	},
+	{ /* sentinel */ }
 };
 
 static const struct of_device_id rockchip_dp_dt_ids[] = {
 	{.compatible = "rockchip,rk3288-dp", .data = &rk3288_dp },
 	{.compatible = "rockchip,rk3399-edp", .data = &rk3399_edp },
+	{.compatible = "rockchip,rk3568-edp", .data = &rk3568_edp },
+	{.compatible = "rockchip,rk3588-edp", .data = &rk3588_edp },
 	{}
 };
 MODULE_DEVICE_TABLE(of, rockchip_dp_dt_ids);
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
index adeaa0140..d7cc434fa 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -6,7 +6,6 @@
 
 #include <linux/clk.h>
 #include <linux/component.h>
-#include <linux/extcon.h>
 #include <linux/firmware.h>
 #include <linux/mfd/syscon.h>
 #include <linux/phy/phy.h>
@@ -143,24 +142,7 @@ static void cdn_dp_clk_disable(struct cdn_dp_device *dp)
 
 static int cdn_dp_get_port_lanes(struct cdn_dp_port *port)
 {
-	struct extcon_dev *edev = port->extcon;
-	union extcon_property_value property;
-	int dptx;
-	u8 lanes;
-
-	dptx = extcon_get_state(edev, EXTCON_DISP_DP);
-	if (dptx > 0) {
-		extcon_get_property(edev, EXTCON_DISP_DP,
-				    EXTCON_PROP_USB_SS, &property);
-		if (property.intval)
-			lanes = 2;
-		else
-			lanes = 4;
-	} else {
-		lanes = 0;
-	}
-
-	return lanes;
+	return phy_get_bus_width(port->phy);
 }
 
 static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count)
@@ -194,7 +176,6 @@ static struct cdn_dp_port *cdn_dp_connected_port(struct cdn_dp_device *dp)
 static bool cdn_dp_check_sink_connection(struct cdn_dp_device *dp)
 {
 	unsigned long timeout = jiffies + msecs_to_jiffies(CDN_DPCD_TIMEOUT_MS);
-	struct cdn_dp_port *port;
 	u8 sink_count = 0;
 
 	if (dp->active_port < 0 || dp->active_port >= dp->ports) {
@@ -202,8 +183,6 @@ static bool cdn_dp_check_sink_connection(struct cdn_dp_device *dp)
 		return false;
 	}
 
-	port = dp->port[dp->active_port];
-
 	/*
 	 * Attempt to read sink count, retry in case the sink may not be ready.
 	 *
@@ -211,9 +190,6 @@ static bool cdn_dp_check_sink_connection(struct cdn_dp_device *dp)
 	 * some docks need more time to power up.
 	 */
 	while (time_before(jiffies, timeout)) {
-		if (!extcon_get_state(port->extcon, EXTCON_DISP_DP))
-			return false;
-
 		if (!cdn_dp_get_sink_count(dp, &sink_count))
 			return sink_count ? true : false;
 
@@ -244,6 +220,13 @@ static void cdn_dp_connector_destroy(struct drm_connector *connector)
 	drm_connector_cleanup(connector);
 }
 
+static void cdn_dp_oob_hotplug_event(struct drm_connector *connector)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+
+	schedule_delayed_work(&dp->event_work, msecs_to_jiffies(100));
+}
+
 static const struct drm_connector_funcs cdn_dp_atomic_connector_funcs = {
 	.detect = cdn_dp_connector_detect,
 	.destroy = cdn_dp_connector_destroy,
@@ -383,7 +366,6 @@ static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp)
 
 static int cdn_dp_enable_phy(struct cdn_dp_device *dp, struct cdn_dp_port *port)
 {
-	union extcon_property_value property;
 	int ret;
 
 	if (!port->phy_enabled) {
@@ -410,15 +392,8 @@ static int cdn_dp_enable_phy(struct cdn_dp_device *dp, struct cdn_dp_port *port)
 		goto err_power_on;
 	}
 
-	ret = extcon_get_property(port->extcon, EXTCON_DISP_DP,
-				  EXTCON_PROP_USB_TYPEC_POLARITY, &property);
-	if (ret) {
-		DRM_DEV_ERROR(dp->dev, "get property failed\n");
-		goto err_power_on;
-	}
-
 	port->lanes = cdn_dp_get_port_lanes(port);
-	ret = cdn_dp_set_host_cap(dp, port->lanes, property.intval);
+	ret = cdn_dp_set_host_cap(dp, port->lanes, 0);
 	if (ret) {
 		DRM_DEV_ERROR(dp->dev, "set host capabilities failed: %d\n",
 			      ret);
@@ -670,7 +645,7 @@ static void cdn_dp_encoder_disable(struct drm_encoder *encoder)
 	 *    run the event_work to re-connect it.
 	 */
 	if (!dp->connected && cdn_dp_connected_port(dp))
-		schedule_work(&dp->event_work);
+		schedule_delayed_work(&dp->event_work, 0);
 }
 
 static int cdn_dp_encoder_atomic_check(struct drm_encoder *encoder,
@@ -681,6 +656,7 @@ static int cdn_dp_encoder_atomic_check(struct drm_encoder *encoder,
 
 	s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
 	s->output_type = DRM_MODE_CONNECTOR_DisplayPort;
+	s->tv_state = &conn_state->tv;
 
 	return 0;
 }
@@ -913,7 +889,7 @@ static int cdn_dp_request_firmware(struct cdn_dp_device *dp)
 
 static void cdn_dp_pd_event_work(struct work_struct *work)
 {
-	struct cdn_dp_device *dp = container_of(work, struct cdn_dp_device,
+	struct cdn_dp_device *dp = container_of(to_delayed_work(work), struct cdn_dp_device,
 						event_work);
 	struct drm_connector *connector = &dp->connector;
 	enum drm_connector_status old_status;
@@ -986,31 +962,13 @@ static void cdn_dp_pd_event_work(struct work_struct *work)
 		drm_kms_helper_hotplug_event(dp->drm_dev);
 }
 
-static int cdn_dp_pd_event(struct notifier_block *nb,
-			   unsigned long event, void *priv)
-{
-	struct cdn_dp_port *port = container_of(nb, struct cdn_dp_port,
-						event_nb);
-	struct cdn_dp_device *dp = port->dp;
-
-	/*
-	 * It would be nice to be able to just do the work inline right here.
-	 * However, we need to make a bunch of calls that might sleep in order
-	 * to turn on the block/phy, so use a worker instead.
-	 */
-	schedule_work(&dp->event_work);
-
-	return NOTIFY_DONE;
-}
-
 static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
 {
 	struct cdn_dp_device *dp = dev_get_drvdata(dev);
 	struct drm_encoder *encoder;
 	struct drm_connector *connector;
-	struct cdn_dp_port *port;
 	struct drm_device *drm_dev = data;
-	int ret, i;
+	int ret;
 
 	ret = cdn_dp_parse_dt(dp);
 	if (ret < 0)
@@ -1022,12 +980,12 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
 	dp->active_port = -1;
 	dp->fw_loaded = false;
 
-	INIT_WORK(&dp->event_work, cdn_dp_pd_event_work);
+	INIT_DELAYED_WORK(&dp->event_work, cdn_dp_pd_event_work);
 
 	encoder = &dp->encoder;
 
-	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
-							     dev->of_node);
+	encoder->possible_crtcs = rockchip_drm_of_find_possible_crtcs(drm_dev,
+								      dev->of_node);
 	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
 
 	ret = drm_simple_encoder_init(drm_dev, encoder,
@@ -1059,23 +1017,14 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
 		goto err_free_connector;
 	}
 
-	for (i = 0; i < dp->ports; i++) {
-		port = dp->port[i];
-
-		port->event_nb.notifier_call = cdn_dp_pd_event;
-		ret = devm_extcon_register_notifier(dp->dev, port->extcon,
-						    EXTCON_DISP_DP,
-						    &port->event_nb);
-		if (ret) {
-			DRM_DEV_ERROR(dev,
-				      "register EXTCON_DISP_DP notifier err\n");
-			goto err_free_connector;
-		}
-	}
+	dp->sub_dev.connector = &dp->connector;
+	dp->sub_dev.of_node = dev->of_node;
+	dp->sub_dev.oob_hotplug_event = cdn_dp_oob_hotplug_event;
+	rockchip_drm_register_sub_dev(&dp->sub_dev);
 
 	pm_runtime_enable(dev);
 
-	schedule_work(&dp->event_work);
+	schedule_delayed_work(&dp->event_work, 0);
 
 	return 0;
 
@@ -1092,7 +1041,7 @@ static void cdn_dp_unbind(struct device *dev, struct device *master, void *data)
 	struct drm_encoder *encoder = &dp->encoder;
 	struct drm_connector *connector = &dp->connector;
 
-	cancel_work_sync(&dp->event_work);
+	cancel_delayed_work_sync(&dp->event_work);
 	cdn_dp_encoder_disable(encoder);
 	encoder->funcs->destroy(encoder);
 	connector->funcs->destroy(connector);
@@ -1123,14 +1072,14 @@ static int cdn_dp_suspend(struct device *dev)
 	return ret;
 }
 
-static __maybe_unused int cdn_dp_resume(struct device *dev)
+static int cdn_dp_resume(struct device *dev)
 {
 	struct cdn_dp_device *dp = dev_get_drvdata(dev);
 
 	mutex_lock(&dp->lock);
 	dp->suspended = false;
 	if (dp->fw_loaded)
-		schedule_work(&dp->event_work);
+		schedule_delayed_work(&dp->event_work, 0);
 	mutex_unlock(&dp->lock);
 
 	return 0;
@@ -1143,7 +1092,6 @@ static int cdn_dp_probe(struct platform_device *pdev)
 	struct cdn_dp_data *dp_data;
 	struct cdn_dp_port *port;
 	struct cdn_dp_device *dp;
-	struct extcon_dev *extcon;
 	struct phy *phy;
 	int i;
 
@@ -1156,21 +1104,18 @@ static int cdn_dp_probe(struct platform_device *pdev)
 	dp_data = (struct cdn_dp_data *)match->data;
 
 	for (i = 0; i < dp_data->max_phy; i++) {
-		extcon = extcon_get_edev_by_phandle(dev, i);
 		phy = devm_of_phy_get_by_index(dev, dev->of_node, i);
 
-		if (PTR_ERR(extcon) == -EPROBE_DEFER ||
-		    PTR_ERR(phy) == -EPROBE_DEFER)
+		if (PTR_ERR(phy) == -EPROBE_DEFER)
 			return -EPROBE_DEFER;
 
-		if (IS_ERR(extcon) || IS_ERR(phy))
+		if (IS_ERR(phy))
 			continue;
 
 		port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
 		if (!port)
 			return -ENOMEM;
 
-		port->extcon = extcon;
 		port->phy = phy;
 		port->dp = dp;
 		port->id = i;
@@ -1178,7 +1123,7 @@ static int cdn_dp_probe(struct platform_device *pdev)
 	}
 
 	if (!dp->ports) {
-		DRM_DEV_ERROR(dev, "missing extcon or phy\n");
+		DRM_DEV_ERROR(dev, "missing phy\n");
 		return -EINVAL;
 	}
 
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
index 81ac9b658..519900c67 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -53,8 +53,6 @@ struct cdn_firmware_header {
 
 struct cdn_dp_port {
 	struct cdn_dp_device *dp;
-	struct notifier_block event_nb;
-	struct extcon_dev *extcon;
 	struct phy *phy;
 	u8 lanes;
 	bool phy_enabled;
@@ -68,8 +66,9 @@ struct cdn_dp_device {
 	struct drm_encoder encoder;
 	struct drm_display_mode mode;
 	struct platform_device *audio_pdev;
-	struct work_struct event_work;
+	struct delayed_work event_work;
 	struct edid *edid;
+	struct rockchip_drm_sub_dev sub_dev;
 
 	struct mutex lock;
 	bool connected;
diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
index 82cbfccba..10dea26b0 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
@@ -16,8 +16,9 @@
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 
+#include <drm/drm_dsc.h>
 #include <video/mipi_display.h>
-
+#include <uapi/linux/videodev2.h>
 #include <drm/bridge/dw_mipi_dsi.h>
 #include <drm/drm_mipi_dsi.h>
 #include <drm/drm_of.h>
@@ -140,6 +141,7 @@
 
 #define DW_MIPI_NEEDS_PHY_CFG_CLK	BIT(0)
 #define DW_MIPI_NEEDS_GRF_CLK		BIT(1)
+#define DW_MIPI_NEEDS_HCLK		BIT(2)
 
 #define PX30_GRF_PD_VO_CON1		0x0438
 #define PX30_DSI_FORCETXSTOPMODE	(0xf << 7)
@@ -172,6 +174,12 @@
 #define RK3399_TXRX_ENABLECLK		BIT(6)
 #define RK3399_TXRX_BASEDIR		BIT(5)
 
+#define RK3568_GRF_VO_CON2		0x0368
+#define RK3568_GRF_VO_CON3		0x036c
+#define RK3568_DSI_FORCETXSTOPMODE	(0xf << 4)
+#define RK3568_DSI_TURNDISABLE		(0x1 << 2)
+#define RK3568_DSI_FORCERXMODE		(0x1 << 0)
+
 #define HIWORD_UPDATE(val, mask)	(val | (mask) << 16)
 
 #define to_dsi(nm)	container_of(nm, struct dw_mipi_dsi_rockchip, nm)
@@ -198,6 +206,19 @@ enum {
 	BIASEXTR_127_7,
 };
 
+enum soc_type {
+	PX30,
+	RK3288,
+	RK3399,
+	RK3568,
+};
+
+struct cmd_header {
+	u8 cmd_type;
+	u8 delay;
+	u8 payload_length;
+};
+
 struct rockchip_dw_dsi_chip_data {
 	u32 reg;
 
@@ -213,25 +234,42 @@ struct rockchip_dw_dsi_chip_data {
 	u32 lanecfg2_grf_reg;
 	u32 lanecfg2;
 
+	enum soc_type soc_type;
 	unsigned int flags;
 	unsigned int max_data_lanes;
+	unsigned long max_bit_rate_per_lane;
 };
 
 struct dw_mipi_dsi_rockchip {
 	struct device *dev;
 	struct drm_encoder encoder;
 	void __iomem *base;
-
+	int id;
+
+	bool c_option;
+	bool scrambling_en;
+	unsigned int slice_width;
+	unsigned int slice_height;
+	unsigned int slice_per_pkt;
+	bool block_pred_enable;
+	bool dsc_enable;
+	u8 version_major;
+	u8 version_minor;
+
+	struct drm_dsc_picture_parameter_set *pps;
 	struct regmap *grf_regmap;
 	struct clk *pllref_clk;
+	struct clk *pclk;
 	struct clk *grf_clk;
 	struct clk *phy_cfg_clk;
+	struct clk *hclk;
 
 	/* dual-channel */
 	bool is_slave;
 	struct dw_mipi_dsi_rockchip *slave;
 
 	/* optional external dphy */
+	bool phy_enabled;
 	struct phy *phy;
 	union phy_configure_opts phy_opts;
 
@@ -243,6 +281,9 @@ struct dw_mipi_dsi_rockchip {
 	struct dw_mipi_dsi *dmd;
 	const struct rockchip_dw_dsi_chip_data *cdata;
 	struct dw_mipi_dsi_plat_data pdata;
+	int devcnt;
+	struct rockchip_drm_sub_dev sub_dev;
+	struct drm_panel *panel;
 };
 
 struct dphy_pll_parameter_map {
@@ -365,10 +406,27 @@ static inline unsigned int ns2ui(struct dw_mipi_dsi_rockchip *dsi, int ns)
 	return DIV_ROUND_UP(ns * dsi->lane_mbps, 1000);
 }
 
+static void dw_mipi_dsi_phy_tx_config(struct dw_mipi_dsi_rockchip *dsi)
+{
+	if (dsi->cdata->lanecfg1_grf_reg)
+		regmap_write(dsi->grf_regmap, dsi->cdata->lanecfg1_grf_reg,
+					      dsi->cdata->lanecfg1);
+
+	if (dsi->cdata->lanecfg2_grf_reg)
+		regmap_write(dsi->grf_regmap, dsi->cdata->lanecfg2_grf_reg,
+					      dsi->cdata->lanecfg2);
+
+	if (dsi->cdata->enable_grf_reg)
+		regmap_write(dsi->grf_regmap, dsi->cdata->enable_grf_reg,
+					      dsi->cdata->enable);
+}
+
 static int dw_mipi_dsi_phy_init(void *priv_data)
 {
 	struct dw_mipi_dsi_rockchip *dsi = priv_data;
-	int ret, i, vco;
+	int i, vco;
+
+	dw_mipi_dsi_phy_tx_config(dsi);
 
 	if (dsi->phy)
 		return 0;
@@ -395,12 +453,6 @@ static int dw_mipi_dsi_phy_init(void *priv_data)
 		return i;
 	}
 
-	ret = clk_prepare_enable(dsi->phy_cfg_clk);
-	if (ret) {
-		DRM_DEV_ERROR(dsi->dev, "Failed to enable phy_cfg_clk\n");
-		return ret;
-	}
-
 	dw_mipi_dsi_phy_write(dsi, PLL_BIAS_CUR_SEL_CAP_VCO_CONTROL,
 			      BYPASS_VCO_RANGE |
 			      VCO_RANGE_CON_SEL(vco) |
@@ -453,7 +505,7 @@ static int dw_mipi_dsi_phy_init(void *priv_data)
 			      TER_RESISTORS_ON);
 
 	dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_REQUEST_STATE_TIME_CONTROL,
-			      TLP_PROGRAM_EN | ns2bc(dsi, 500));
+			      TLP_PROGRAM_EN | ns2bc(dsi, 60));
 	dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_PREPARE_STATE_TIME_CONTROL,
 			      THS_PRE_PROGRAM_EN | ns2ui(dsi, 40));
 	dw_mipi_dsi_phy_write(dsi, HS_TX_CLOCK_LANE_HS_ZERO_STATE_TIME_CONTROL,
@@ -466,7 +518,7 @@ static int dw_mipi_dsi_phy_init(void *priv_data)
 			      BIT(5) | (ns2bc(dsi, 60) + 7));
 
 	dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_REQUEST_STATE_TIME_CONTROL,
-			      TLP_PROGRAM_EN | ns2bc(dsi, 500));
+			      TLP_PROGRAM_EN | ns2bc(dsi, 60));
 	dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_PREPARE_STATE_TIME_CONTROL,
 			      THS_PRE_PROGRAM_EN | (ns2ui(dsi, 50) + 20));
 	dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_HS_ZERO_STATE_TIME_CONTROL,
@@ -476,31 +528,29 @@ static int dw_mipi_dsi_phy_init(void *priv_data)
 	dw_mipi_dsi_phy_write(dsi, HS_TX_DATA_LANE_EXIT_STATE_TIME_CONTROL,
 			      BIT(5) | ns2bc(dsi, 100));
 
-	clk_disable_unprepare(dsi->phy_cfg_clk);
-
-	return ret;
+	return 0;
 }
 
 static void dw_mipi_dsi_phy_power_on(void *priv_data)
 {
 	struct dw_mipi_dsi_rockchip *dsi = priv_data;
-	int ret;
 
-	ret = phy_set_mode(dsi->phy, PHY_MODE_MIPI_DPHY);
-	if (ret) {
-		DRM_DEV_ERROR(dsi->dev, "failed to set phy mode: %d\n", ret);
+	if (dsi->phy_enabled)
 		return;
-	}
 
-	phy_configure(dsi->phy, &dsi->phy_opts);
 	phy_power_on(dsi->phy);
+	dsi->phy_enabled = true;
 }
 
 static void dw_mipi_dsi_phy_power_off(void *priv_data)
 {
 	struct dw_mipi_dsi_rockchip *dsi = priv_data;
 
+	if (!dsi->phy_enabled)
+		return;
+
 	phy_power_off(dsi->phy);
+	dsi->phy_enabled = false;
 }
 
 static int
@@ -509,17 +559,22 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode,
 			  unsigned int *lane_mbps)
 {
 	struct dw_mipi_dsi_rockchip *dsi = priv_data;
+	struct device *dev = dsi->dev;
 	int bpp;
 	unsigned long mpclk, tmp;
 	unsigned int target_mbps = 1000;
-	unsigned int max_mbps = dppa_map[ARRAY_SIZE(dppa_map) - 1].max_mbps;
+	unsigned int max_mbps;
 	unsigned long best_freq = 0;
 	unsigned long fvco_min, fvco_max, fin, fout;
 	unsigned int min_prediv, max_prediv;
 	unsigned int _prediv, best_prediv;
 	unsigned long _fbdiv, best_fbdiv;
 	unsigned long min_delta = ULONG_MAX;
+	unsigned long target_pclk, hs_clk_rate;
+	unsigned int value;
+	int ret;
 
+	max_mbps = dsi->cdata->max_bit_rate_per_lane / USEC_PER_SEC;
 	dsi->format = format;
 	bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
 	if (bpp < 0) {
@@ -529,23 +584,40 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode,
 		return bpp;
 	}
 
-	mpclk = DIV_ROUND_UP(mode->clock, MSEC_PER_SEC);
-	if (mpclk) {
-		/* take 1 / 0.8, since mbps must big than bandwidth of RGB */
-		tmp = mpclk * (bpp / lanes) * 10 / 8;
-		if (tmp < max_mbps)
-			target_mbps = tmp;
-		else
-			DRM_DEV_ERROR(dsi->dev,
-				      "DPHY clock frequency is out of range\n");
+	/* optional override of the desired bandwidth */
+	if (!of_property_read_u32(dev->of_node, "rockchip,lane-rate", &value)) {
+		target_mbps = value;
+	} else {
+		mpclk = DIV_ROUND_UP(mode->clock, MSEC_PER_SEC);
+		if (mpclk) {
+			/* take 1 / 0.9, since mbps must big than bandwidth of RGB */
+			tmp = mpclk * (bpp / lanes) * 10 / 9;
+			if (tmp < max_mbps)
+				target_mbps = tmp;
+			else {
+				DRM_DEV_ERROR(dsi->dev,
+					      "DPHY clock frequency is out of range\n");
+				target_mbps = max_mbps;
+			}
+		}
 	}
 
 	/* for external phy only a the mipi_dphy_config is necessary */
 	if (dsi->phy) {
-		phy_mipi_dphy_get_default_config(mode->clock * 1000 * 10 / 8,
+		target_pclk = DIV_ROUND_CLOSEST_ULL(target_mbps * lanes, bpp);
+		phy_mipi_dphy_get_default_config(target_pclk * USEC_PER_SEC,
 						 bpp, lanes,
 						 &dsi->phy_opts.mipi_dphy);
-		dsi->lane_mbps = target_mbps;
+		ret = phy_set_mode(dsi->phy, PHY_MODE_MIPI_DPHY);
+		if (ret) {
+			DRM_DEV_ERROR(dsi->dev,
+				      "failed to set phy mode: %d\n", ret);
+			return ret;
+		}
+
+		phy_configure(dsi->phy, &dsi->phy_opts);
+		hs_clk_rate = dsi->phy_opts.mipi_dphy.hs_clk_rate;
+		dsi->lane_mbps = DIV_ROUND_UP(hs_clk_rate, USEC_PER_SEC);
 		*lane_mbps = dsi->lane_mbps;
 
 		return 0;
@@ -611,74 +683,18 @@ struct hstt {
 	struct dw_mipi_dsi_dphy_timing timing;
 };
 
-#define HSTT(_maxfreq, _c_lp2hs, _c_hs2lp, _d_lp2hs, _d_hs2lp)	\
-{					\
-	.maxfreq = _maxfreq,		\
-	.timing = {			\
-		.clk_lp2hs = _c_lp2hs,	\
-		.clk_hs2lp = _c_hs2lp,	\
-		.data_lp2hs = _d_lp2hs,	\
-		.data_hs2lp = _d_hs2lp,	\
-	}				\
-}
-
-/* Table A-3 High-Speed Transition Times */
-struct hstt hstt_table[] = {
-	HSTT(  90,  32, 20,  26, 13),
-	HSTT( 100,  35, 23,  28, 14),
-	HSTT( 110,  32, 22,  26, 13),
-	HSTT( 130,  31, 20,  27, 13),
-	HSTT( 140,  33, 22,  26, 14),
-	HSTT( 150,  33, 21,  26, 14),
-	HSTT( 170,  32, 20,  27, 13),
-	HSTT( 180,  36, 23,  30, 15),
-	HSTT( 200,  40, 22,  33, 15),
-	HSTT( 220,  40, 22,  33, 15),
-	HSTT( 240,  44, 24,  36, 16),
-	HSTT( 250,  48, 24,  38, 17),
-	HSTT( 270,  48, 24,  38, 17),
-	HSTT( 300,  50, 27,  41, 18),
-	HSTT( 330,  56, 28,  45, 18),
-	HSTT( 360,  59, 28,  48, 19),
-	HSTT( 400,  61, 30,  50, 20),
-	HSTT( 450,  67, 31,  55, 21),
-	HSTT( 500,  73, 31,  59, 22),
-	HSTT( 550,  79, 36,  63, 24),
-	HSTT( 600,  83, 37,  68, 25),
-	HSTT( 650,  90, 38,  73, 27),
-	HSTT( 700,  95, 40,  77, 28),
-	HSTT( 750, 102, 40,  84, 28),
-	HSTT( 800, 106, 42,  87, 30),
-	HSTT( 850, 113, 44,  93, 31),
-	HSTT( 900, 118, 47,  98, 32),
-	HSTT( 950, 124, 47, 102, 34),
-	HSTT(1000, 130, 49, 107, 35),
-	HSTT(1050, 135, 51, 111, 37),
-	HSTT(1100, 139, 51, 114, 38),
-	HSTT(1150, 146, 54, 120, 40),
-	HSTT(1200, 153, 57, 125, 41),
-	HSTT(1250, 158, 58, 130, 42),
-	HSTT(1300, 163, 58, 135, 44),
-	HSTT(1350, 168, 60, 140, 45),
-	HSTT(1400, 172, 64, 144, 47),
-	HSTT(1450, 176, 65, 148, 48),
-	HSTT(1500, 181, 66, 153, 50)
+struct dw_mipi_dsi_dphy_timing dphy_hstt = {
+	.clk_lp2hs = 0x40,
+	.clk_hs2lp = 0x40,
+	.data_lp2hs = 0x10,
+	.data_hs2lp = 0x14,
 };
 
 static int
 dw_mipi_dsi_phy_get_timing(void *priv_data, unsigned int lane_mbps,
 			   struct dw_mipi_dsi_dphy_timing *timing)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(hstt_table); i++)
-		if (lane_mbps < hstt_table[i].maxfreq)
-			break;
-
-	if (i == ARRAY_SIZE(hstt_table))
-		i--;
-
-	*timing = hstt_table[i].timing;
+	*timing = dphy_hstt;
 
 	return 0;
 }
@@ -691,26 +707,25 @@ static const struct dw_mipi_dsi_phy_ops dw_mipi_dsi_rockchip_phy_ops = {
 	.get_timing = dw_mipi_dsi_phy_get_timing,
 };
 
-static void dw_mipi_dsi_rockchip_config(struct dw_mipi_dsi_rockchip *dsi)
+static void dw_mipi_dsi_rockchip_vop_routing(struct dw_mipi_dsi_rockchip *dsi)
 {
-	if (dsi->cdata->lanecfg1_grf_reg)
-		regmap_write(dsi->grf_regmap, dsi->cdata->lanecfg1_grf_reg,
-					      dsi->cdata->lanecfg1);
+	int mux;
 
-	if (dsi->cdata->lanecfg2_grf_reg)
-		regmap_write(dsi->grf_regmap, dsi->cdata->lanecfg2_grf_reg,
-					      dsi->cdata->lanecfg2);
+	mux = drm_of_encoder_active_endpoint_id(dsi->dev->of_node,
+						&dsi->encoder);
+	if (mux < 0)
+		return;
 
-	if (dsi->cdata->enable_grf_reg)
-		regmap_write(dsi->grf_regmap, dsi->cdata->enable_grf_reg,
-					      dsi->cdata->enable);
-}
+	if (dsi->cdata->lcdsel_grf_reg) {
+		regmap_write(dsi->grf_regmap, dsi->cdata->lcdsel_grf_reg,
+			mux ? dsi->cdata->lcdsel_lit : dsi->cdata->lcdsel_big);
 
-static void dw_mipi_dsi_rockchip_set_lcdsel(struct dw_mipi_dsi_rockchip *dsi,
-					    int mux)
-{
-	regmap_write(dsi->grf_regmap, dsi->cdata->lcdsel_grf_reg,
-		mux ? dsi->cdata->lcdsel_lit : dsi->cdata->lcdsel_big);
+		if (dsi->slave && dsi->slave->cdata->lcdsel_grf_reg)
+			regmap_write(dsi->slave->grf_regmap,
+				     dsi->slave->cdata->lcdsel_grf_reg,
+				     mux ? dsi->slave->cdata->lcdsel_lit :
+				     dsi->slave->cdata->lcdsel_big);
+	}
 }
 
 static int
@@ -720,6 +735,8 @@ dw_mipi_dsi_encoder_atomic_check(struct drm_encoder *encoder,
 {
 	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
 	struct dw_mipi_dsi_rockchip *dsi = to_dsi(encoder);
+	struct drm_connector *connector = conn_state->connector;
+	struct drm_display_info *info = &connector->display_info;
 
 	switch (dsi->format) {
 	case MIPI_DSI_FMT_RGB888:
@@ -736,9 +753,42 @@ dw_mipi_dsi_encoder_atomic_check(struct drm_encoder *encoder,
 		return -EINVAL;
 	}
 
+	if (info->num_bus_formats)
+		s->bus_format = info->bus_formats[0];
+	else
+		s->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+
+	/* rk356x series drive mipi pixdata on posedge */
+	if (dsi->cdata->soc_type == RK3568) {
+		s->bus_flags &= ~DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE;
+		s->bus_flags |= DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE;
+	}
+
 	s->output_type = DRM_MODE_CONNECTOR_DSI;
-	if (dsi->slave)
-		s->output_flags = ROCKCHIP_OUTPUT_DSI_DUAL;
+	s->color_space = V4L2_COLORSPACE_DEFAULT;
+	s->output_if = dsi->id ? VOP_OUTPUT_IF_MIPI1 : VOP_OUTPUT_IF_MIPI0;
+	if (dsi->slave) {
+		s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE;
+		s->output_if |= VOP_OUTPUT_IF_MIPI1;
+	}
+
+	/* dual link dsi for rk3399 */
+	if (dsi->id && dsi->cdata->soc_type == RK3399)
+		s->output_flags |= ROCKCHIP_OUTPUT_DATA_SWAP;
+
+	if (dsi->dsc_enable) {
+		s->dsc_enable = 1;
+		s->dsc_sink_cap.version_major = dsi->version_major;
+		s->dsc_sink_cap.version_minor = dsi->version_minor;
+		s->dsc_sink_cap.slice_width = dsi->slice_width;
+		s->dsc_sink_cap.slice_height = dsi->slice_height;
+		/* only can support rgb888 panel now */
+		s->dsc_sink_cap.target_bits_per_pixel_x16 = 8 << 4;
+		s->dsc_sink_cap.block_pred = dsi->block_pred_enable;
+		s->dsc_sink_cap.native_420 = 0;
+
+		memcpy(&s->pps, dsi->pps, sizeof(struct drm_dsc_picture_parameter_set));
+	}
 
 	return 0;
 }
@@ -746,42 +796,43 @@ dw_mipi_dsi_encoder_atomic_check(struct drm_encoder *encoder,
 static void dw_mipi_dsi_encoder_enable(struct drm_encoder *encoder)
 {
 	struct dw_mipi_dsi_rockchip *dsi = to_dsi(encoder);
-	int ret, mux;
 
-	mux = drm_of_encoder_active_endpoint_id(dsi->dev->of_node,
-						&dsi->encoder);
-	if (mux < 0)
-		return;
+	dw_mipi_dsi_rockchip_vop_routing(dsi);
+}
 
-	pm_runtime_get_sync(dsi->dev);
-	if (dsi->slave)
-		pm_runtime_get_sync(dsi->slave->dev);
+static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder)
+{
+}
 
-	/*
-	 * For the RK3399, the clk of grf must be enabled before writing grf
-	 * register. And for RK3288 or other soc, this grf_clk must be NULL,
-	 * the clk_prepare_enable return true directly.
-	 */
-	ret = clk_prepare_enable(dsi->grf_clk);
-	if (ret) {
-		DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret);
-		return;
+static void dw_mipi_dsi_rockchip_loader_protect(struct dw_mipi_dsi_rockchip *dsi, bool on)
+{
+	if (on) {
+		pm_runtime_get_sync(dsi->dev);
+		phy_init(dsi->phy);
+		dsi->phy_enabled = true;
+		if (dsi->phy)
+			dsi->phy->power_count++;
+	} else {
+		pm_runtime_put(dsi->dev);
+		phy_exit(dsi->phy);
+		dsi->phy_enabled = false;
+		if (dsi->phy)
+			dsi->phy->power_count--;
 	}
 
-	dw_mipi_dsi_rockchip_set_lcdsel(dsi, mux);
 	if (dsi->slave)
-		dw_mipi_dsi_rockchip_set_lcdsel(dsi->slave, mux);
-
-	clk_disable_unprepare(dsi->grf_clk);
+		dw_mipi_dsi_rockchip_loader_protect(dsi->slave, on);
 }
 
-static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder)
+static void dw_mipi_dsi_rockchip_encoder_loader_protect(struct drm_encoder *encoder,
+					      bool on)
 {
 	struct dw_mipi_dsi_rockchip *dsi = to_dsi(encoder);
 
-	if (dsi->slave)
-		pm_runtime_put(dsi->slave->dev);
-	pm_runtime_put(dsi->dev);
+	if (dsi->panel)
+		panel_simple_loader_protect(dsi->panel);
+
+	dw_mipi_dsi_rockchip_loader_protect(dsi, on);
 }
 
 static const struct drm_encoder_helper_funcs
@@ -797,8 +848,8 @@ static int rockchip_dsi_drm_create_encoder(struct dw_mipi_dsi_rockchip *dsi,
 	struct drm_encoder *encoder = &dsi->encoder;
 	int ret;
 
-	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
-							     dsi->dev->of_node);
+	encoder->possible_crtcs = rockchip_drm_of_find_possible_crtcs(drm_dev,
+								      dsi->dev->of_node);
 
 	ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_DSI);
 	if (ret) {
@@ -814,61 +865,90 @@ static int rockchip_dsi_drm_create_encoder(struct dw_mipi_dsi_rockchip *dsi,
 static struct device
 *dw_mipi_dsi_rockchip_find_second(struct dw_mipi_dsi_rockchip *dsi)
 {
-	const struct of_device_id *match;
-	struct device_node *node = NULL, *local;
-
-	match = of_match_device(dsi->dev->driver->of_match_table, dsi->dev);
-
-	local = of_graph_get_remote_node(dsi->dev->of_node, 1, 0);
-	if (!local)
-		return NULL;
-
-	while ((node = of_find_compatible_node(node, NULL,
-					       match->compatible))) {
-		struct device_node *remote;
+	struct device_node *node = NULL;
+	struct platform_device *pdev;
+	struct dw_mipi_dsi_rockchip *dsi2;
+
+	node = of_parse_phandle(dsi->dev->of_node, "rockchip,dual-channel", 0);
+	if (node) {
+		pdev = of_find_device_by_node(node);
+		if (!pdev)
+			return ERR_PTR(-EPROBE_DEFER);
+
+		dsi2 = platform_get_drvdata(pdev);
+		if (!dsi2) {
+			platform_device_put(pdev);
+			return ERR_PTR(-EPROBE_DEFER);
+		}
 
-		/* found ourself */
-		if (node == dsi->dev->of_node)
-			continue;
+		return &pdev->dev;
+	}
 
-		remote = of_graph_get_remote_node(node, 1, 0);
-		if (!remote)
-			continue;
+	return NULL;
+}
 
-		/* same display device in port1-ep0 for both */
-		if (remote == local) {
-			struct dw_mipi_dsi_rockchip *dsi2;
-			struct platform_device *pdev;
+static int dw_mipi_dsi_get_dsc_info_from_sink(struct dw_mipi_dsi_rockchip *dsi,
+					      struct drm_panel *panel,
+					      struct drm_bridge *bridge)
+{
+	struct drm_dsc_picture_parameter_set *pps = NULL;
+	struct device_node *np = NULL;
+	struct cmd_header *header;
+	const void *data;
+	char *d;
+	uint8_t *dsc_packed_pps;
+	int len;
+
+	if (!panel && !bridge)
+		return -ENODEV;
+
+	if (panel)
+		np = panel->dev->of_node;
+	else
+		np = bridge->of_node;
+
+	dsi->c_option = of_property_read_bool(np, "phy-c-option");
+	dsi->scrambling_en = of_property_read_bool(np, "scrambling-enable");
+	dsi->dsc_enable = of_property_read_bool(np, "compressed-data");
+	dsi->block_pred_enable = of_property_read_bool(np, "blk-pred-enable");
+	of_property_read_u32(np, "slice-width", &dsi->slice_width);
+	of_property_read_u32(np, "slice-height", &dsi->slice_height);
+	of_property_read_u32(np, "slice-per-pkt", &dsi->slice_per_pkt);
+	of_property_read_u8(np, "version-major", &dsi->version_major);
+	of_property_read_u8(np, "version-minor", &dsi->version_minor);
+
+	data = of_get_property(np, "panel-init-sequence", &len);
+	if (!data)
+		return -EINVAL;
 
-			pdev = of_find_device_by_node(node);
+	d = devm_kmemdup(dsi->dev, data, len, GFP_KERNEL);
+	if (!d)
+		return -ENOMEM;
 
-			/*
-			 * we have found the second, so will either return it
-			 * or return with an error. In any case won't need the
-			 * nodes anymore nor continue the loop.
-			 */
-			of_node_put(remote);
-			of_node_put(node);
-			of_node_put(local);
+	while (len > sizeof(*header)) {
+		header = (struct cmd_header *)d;
+		d += sizeof(*header);
+		len -= sizeof(*header);
 
-			if (!pdev)
-				return ERR_PTR(-EPROBE_DEFER);
+		if (header->payload_length > len)
+			return -EINVAL;
 
-			dsi2 = platform_get_drvdata(pdev);
-			if (!dsi2) {
-				platform_device_put(pdev);
-				return ERR_PTR(-EPROBE_DEFER);
-			}
+		if (header->cmd_type == MIPI_DSI_PICTURE_PARAMETER_SET) {
+			dsc_packed_pps = devm_kmemdup(dsi->dev, d,
+						      header->payload_length, GFP_KERNEL);
+			if (!dsc_packed_pps)
+				return -ENOMEM;
 
-			return &pdev->dev;
+			pps = (struct drm_dsc_picture_parameter_set *)dsc_packed_pps;
+			break;
 		}
 
-		of_node_put(remote);
+		d += header->payload_length;
+		len -= header->payload_length;
 	}
+	dsi->pps = pps;
 
-	of_node_put(local);
-
-	return NULL;
+	return 0;
 }
 
 static int dw_mipi_dsi_rockchip_bind(struct device *dev,
@@ -878,7 +958,6 @@ static int dw_mipi_dsi_rockchip_bind(struct device *dev,
 	struct dw_mipi_dsi_rockchip *dsi = dev_get_drvdata(dev);
 	struct drm_device *drm_dev = data;
 	struct device *second;
-	bool master1, master2;
 	int ret;
 
 	second = dw_mipi_dsi_rockchip_find_second(dsi);
@@ -886,27 +965,7 @@ static int dw_mipi_dsi_rockchip_bind(struct device *dev,
 		return PTR_ERR(second);
 
 	if (second) {
-		master1 = of_property_read_bool(dsi->dev->of_node,
-						"clock-master");
-		master2 = of_property_read_bool(second->of_node,
-						"clock-master");
-
-		if (master1 && master2) {
-			DRM_DEV_ERROR(dsi->dev, "only one clock-master allowed\n");
-			return -EINVAL;
-		}
-
-		if (!master1 && !master2) {
-			DRM_DEV_ERROR(dsi->dev, "no clock-master defined\n");
-			return -EINVAL;
-		}
-
 		/* we are the slave in dual-DSI */
-		if (!master1) {
-			dsi->is_slave = true;
-			return 0;
-		}
-
 		dsi->slave = dev_get_drvdata(second);
 		if (!dsi->slave) {
 			DRM_DEV_ERROR(dev, "could not get slaves data\n");
@@ -918,30 +977,15 @@ static int dw_mipi_dsi_rockchip_bind(struct device *dev,
 		put_device(second);
 	}
 
+	if (dsi->is_slave)
+		return 0;
+
 	ret = clk_prepare_enable(dsi->pllref_clk);
 	if (ret) {
 		DRM_DEV_ERROR(dev, "Failed to enable pllref_clk: %d\n", ret);
 		return ret;
 	}
 
-	/*
-	 * With the GRF clock running, write lane and dual-mode configurations
-	 * that won't change immediately. If we waited until enable() to do
-	 * this, things like panel preparation would not be able to send
-	 * commands over DSI.
-	 */
-	ret = clk_prepare_enable(dsi->grf_clk);
-	if (ret) {
-		DRM_DEV_ERROR(dsi->dev, "Failed to enable grf_clk: %d\n", ret);
-		return ret;
-	}
-
-	dw_mipi_dsi_rockchip_config(dsi);
-	if (dsi->slave)
-		dw_mipi_dsi_rockchip_config(dsi->slave);
-
-	clk_disable_unprepare(dsi->grf_clk);
-
 	ret = rockchip_dsi_drm_create_encoder(dsi, drm_dev);
 	if (ret) {
 		DRM_DEV_ERROR(dev, "Failed to create drm encoder\n");
@@ -954,6 +998,20 @@ static int dw_mipi_dsi_rockchip_bind(struct device *dev,
 		return ret;
 	}
 
+	ret = drm_of_find_panel_or_bridge(dsi->dev->of_node, 1, 0,
+					  &dsi->panel, NULL);
+	if (ret)
+		dev_err(dsi->dev, "failed to find panel\n");
+
+	dw_mipi_dsi_get_dsc_info_from_sink(dsi, dsi->panel, NULL);
+
+	dsi->sub_dev.connector = dw_mipi_dsi_get_connector(dsi->dmd);
+	if (dsi->sub_dev.connector) {
+		dsi->sub_dev.of_node = dev->of_node;
+		dsi->sub_dev.loader_protect = dw_mipi_dsi_rockchip_encoder_loader_protect;
+		rockchip_drm_register_sub_dev(&dsi->sub_dev);
+	}
+
 	return 0;
 }
 
@@ -966,6 +1024,9 @@ static void dw_mipi_dsi_rockchip_unbind(struct device *dev,
 	if (dsi->is_slave)
 		return;
 
+	if (dsi->sub_dev.connector)
+		rockchip_drm_unregister_sub_dev(&dsi->sub_dev);
+
 	dw_mipi_dsi_unbind(dsi->dmd);
 
 	clk_disable_unprepare(dsi->pllref_clk);
@@ -1051,6 +1112,7 @@ static int dw_mipi_dsi_rockchip_probe(struct platform_device *pdev)
 	while (cdata[i].reg) {
 		if (cdata[i].reg == res->start) {
 			dsi->cdata = &cdata[i];
+			dsi->id = i;
 			break;
 		}
 
@@ -1070,6 +1132,13 @@ static int dw_mipi_dsi_rockchip_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	dsi->pclk = devm_clk_get(dev, "pclk");
+	if (IS_ERR(dsi->pclk)) {
+		ret = PTR_ERR(dsi->pclk);
+		dev_err(dev, "Unable to get pclk: %d\n", ret);
+		return ret;
+	}
+
 	dsi->pllref_clk = devm_clk_get(dev, "ref");
 	if (IS_ERR(dsi->pllref_clk)) {
 		if (dsi->phy) {
@@ -1106,6 +1175,15 @@ static int dw_mipi_dsi_rockchip_probe(struct platform_device *pdev)
 		}
 	}
 
+	if (dsi->cdata->flags & DW_MIPI_NEEDS_HCLK) {
+		dsi->hclk = devm_clk_get(dev, "hclk");
+		if (IS_ERR(dsi->hclk)) {
+			ret = PTR_ERR(dsi->hclk);
+			DRM_DEV_ERROR(dev, "Unable to get hclk: %d\n", ret);
+			return ret;
+		}
+	}
+
 	dsi->grf_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
 	if (IS_ERR(dsi->grf_regmap)) {
 		DRM_DEV_ERROR(dsi->dev, "Unable to get rockchip,grf\n");
@@ -1140,11 +1218,43 @@ static int dw_mipi_dsi_rockchip_remove(struct platform_device *pdev)
 {
 	struct dw_mipi_dsi_rockchip *dsi = platform_get_drvdata(pdev);
 
+	if (dsi->devcnt == 0)
+		component_del(dsi->dev, &dw_mipi_dsi_rockchip_ops);
+
 	dw_mipi_dsi_remove(dsi->dmd);
 
 	return 0;
 }
 
+static __maybe_unused int dw_mipi_dsi_runtime_suspend(struct device *dev)
+{
+	struct dw_mipi_dsi_rockchip *dsi = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(dsi->grf_clk);
+	clk_disable_unprepare(dsi->pclk);
+	clk_disable_unprepare(dsi->hclk);
+	clk_disable_unprepare(dsi->phy_cfg_clk);
+
+	return 0;
+}
+
+static __maybe_unused int dw_mipi_dsi_runtime_resume(struct device *dev)
+{
+	struct dw_mipi_dsi_rockchip *dsi = dev_get_drvdata(dev);
+
+	clk_prepare_enable(dsi->phy_cfg_clk);
+	clk_prepare_enable(dsi->hclk);
+	clk_prepare_enable(dsi->pclk);
+	clk_prepare_enable(dsi->grf_clk);
+
+	return 0;
+}
+
+static const struct dev_pm_ops dw_mipi_dsi_rockchip_pm_ops = {
+	SET_RUNTIME_PM_OPS(dw_mipi_dsi_runtime_suspend,
+			   dw_mipi_dsi_runtime_resume, NULL)
+};
+
 static const struct rockchip_dw_dsi_chip_data px30_chip_data[] = {
 	{
 		.reg = 0xff450000,
@@ -1159,6 +1269,8 @@ static const struct rockchip_dw_dsi_chip_data px30_chip_data[] = {
 					     PX30_DSI_FORCETXSTOPMODE),
 
 		.max_data_lanes = 4,
+		.max_bit_rate_per_lane = 1000000000UL,
+		.soc_type = PX30,
 	},
 	{ /* sentinel */ }
 };
@@ -1171,6 +1283,8 @@ static const struct rockchip_dw_dsi_chip_data rk3288_chip_data[] = {
 		.lcdsel_lit = HIWORD_UPDATE(RK3288_DSI0_LCDC_SEL, RK3288_DSI0_LCDC_SEL),
 
 		.max_data_lanes = 4,
+		.max_bit_rate_per_lane = 1500000000UL,
+		.soc_type = RK3288,
 	},
 	{
 		.reg = 0xff964000,
@@ -1179,6 +1293,8 @@ static const struct rockchip_dw_dsi_chip_data rk3288_chip_data[] = {
 		.lcdsel_lit = HIWORD_UPDATE(RK3288_DSI1_LCDC_SEL, RK3288_DSI1_LCDC_SEL),
 
 		.max_data_lanes = 4,
+		.max_bit_rate_per_lane = 1500000000UL,
+		.soc_type = RK3288,
 	},
 	{ /* sentinel */ }
 };
@@ -1199,6 +1315,8 @@ static const struct rockchip_dw_dsi_chip_data rk3399_chip_data[] = {
 
 		.flags = DW_MIPI_NEEDS_PHY_CFG_CLK | DW_MIPI_NEEDS_GRF_CLK,
 		.max_data_lanes = 4,
+		.max_bit_rate_per_lane = 1500000000UL,
+		.soc_type = RK3399,
 	},
 	{
 		.reg = 0xff968000,
@@ -1225,6 +1343,38 @@ static const struct rockchip_dw_dsi_chip_data rk3399_chip_data[] = {
 
 		.flags = DW_MIPI_NEEDS_PHY_CFG_CLK | DW_MIPI_NEEDS_GRF_CLK,
 		.max_data_lanes = 4,
+		.max_bit_rate_per_lane = 1500000000UL,
+		.soc_type = RK3399,
+	},
+	{ /* sentinel */ }
+};
+
+static const struct rockchip_dw_dsi_chip_data rk3568_chip_data[] = {
+	{
+		.reg = 0xfe060000,
+
+		.lanecfg1_grf_reg = RK3568_GRF_VO_CON2,
+		.lanecfg1 = HIWORD_UPDATE(0, RK3568_DSI_TURNDISABLE |
+					     RK3568_DSI_FORCERXMODE |
+					     RK3568_DSI_FORCETXSTOPMODE),
+
+		.flags = DW_MIPI_NEEDS_HCLK,
+		.max_data_lanes = 4,
+		.max_bit_rate_per_lane = 1200000000UL,
+		.soc_type = RK3568,
+	},
+	{
+		.reg = 0xfe070000,
+
+		.lanecfg1_grf_reg = RK3568_GRF_VO_CON3,
+		.lanecfg1 = HIWORD_UPDATE(0, RK3568_DSI_TURNDISABLE |
+					     RK3568_DSI_FORCERXMODE |
+					     RK3568_DSI_FORCETXSTOPMODE),
+
+		.flags = DW_MIPI_NEEDS_HCLK,
+		.max_data_lanes = 4,
+		.max_bit_rate_per_lane = 1200000000UL,
+		.soc_type = RK3568,
 	},
 	{ /* sentinel */ }
 };
@@ -1239,6 +1389,9 @@ static const struct of_device_id dw_mipi_dsi_rockchip_dt_ids[] = {
 	}, {
 	 .compatible = "rockchip,rk3399-mipi-dsi",
 	 .data = &rk3399_chip_data,
+	}, {
+	 .compatible = "rockchip,rk3568-mipi-dsi",
+	 .data = &rk3568_chip_data,
 	},
 	{ /* sentinel */ }
 };
@@ -1249,6 +1402,7 @@ struct platform_driver dw_mipi_dsi_rockchip_driver = {
 	.remove		= dw_mipi_dsi_rockchip_remove,
 	.driver		= {
 		.of_match_table = dw_mipi_dsi_rockchip_dt_ids,
+		.pm = &dw_mipi_dsi_rockchip_pm_ops,
 		.name	= "dw-mipi-dsi-rockchip",
 		/*
 		 * For dual-DSI display, one DSI pokes at the other DSI's
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 23de359a1..430a35995 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -4,21 +4,31 @@
  */
 
 #include <linux/clk.h>
+#include <linux/gpio/consumer.h>
 #include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/phy/phy.h>
 #include <linux/regmap.h>
+#include <linux/pm_runtime.h>
 
+#include <drm/drm_of.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dsc.h>
+#include <drm/drm_edid.h>
 #include <drm/bridge/dw_hdmi.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_of.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_simple_kms_helper.h>
 
+#include <uapi/linux/videodev2.h>
+
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_vop.h"
 
+#define HIWORD_UPDATE(val, mask)	(val | (mask) << 16)
+
 #define RK3228_GRF_SOC_CON2		0x0408
 #define RK3228_HDMI_SDAIN_MSK		BIT(14)
 #define RK3228_HDMI_SCLIN_MSK		BIT(13)
@@ -29,8 +39,11 @@
 
 #define RK3288_GRF_SOC_CON6		0x025C
 #define RK3288_HDMI_LCDC_SEL		BIT(4)
-#define RK3328_GRF_SOC_CON2		0x0408
+#define RK3288_GRF_SOC_CON16		0x03a8
+#define RK3288_HDMI_LCDC0_YUV420	BIT(2)
+#define RK3288_HDMI_LCDC1_YUV420	BIT(3)
 
+#define RK3328_GRF_SOC_CON2		0x0408
 #define RK3328_HDMI_SDAIN_MSK		BIT(11)
 #define RK3328_HDMI_SCLIN_MSK		BIT(10)
 #define RK3328_HDMI_HPD_IOE		BIT(2)
@@ -50,281 +63,2486 @@
 #define RK3399_GRF_SOC_CON20		0x6250
 #define RK3399_HDMI_LCDC_SEL		BIT(6)
 
-#define HIWORD_UPDATE(val, mask)	(val | (mask) << 16)
+#define RK3568_GRF_VO_CON1		0x0364
+#define RK3568_HDMI_SDAIN_MSK		BIT(15)
+#define RK3568_HDMI_SCLIN_MSK		BIT(14)
+
+#define RK3588_GRF_SOC_CON2		0x0308
+#define RK3588_HDMI1_HPD_INT_MSK	BIT(15)
+#define RK3588_HDMI1_HPD_INT_CLR	BIT(14)
+#define RK3588_HDMI0_HPD_INT_MSK	BIT(13)
+#define RK3588_HDMI0_HPD_INT_CLR	BIT(12)
+#define RK3588_GRF_SOC_CON7		0x031c
+#define RK3588_SET_HPD_PATH_MASK	(0x3 << 12)
+#define RK3588_GRF_SOC_STATUS1		0x0384
+#define RK3588_HDMI0_LOW_MORETHAN100MS	BIT(20)
+#define RK3588_HDMI0_HPD_PORT_LEVEL	BIT(19)
+#define RK3588_HDMI0_IHPD_PORT		BIT(18)
+#define RK3588_HDMI0_OHPD_INT		BIT(17)
+#define RK3588_HDMI0_LEVEL_INT		BIT(16)
+#define RK3588_HDMI0_INTR_CHANGE_CNT	(0x7 << 13)
+#define RK3588_HDMI1_LOW_MORETHAN100MS	BIT(28)
+#define RK3588_HDMI1_HPD_PORT_LEVEL	BIT(27)
+#define RK3588_HDMI1_IHPD_PORT		BIT(26)
+#define RK3588_HDMI1_OHPD_INT		BIT(25)
+#define RK3588_HDMI1_LEVEL_INT		BIT(24)
+#define RK3588_HDMI1_INTR_CHANGE_CNT	(0x7 << 21)
+
+#define RK3588_GRF_VO1_CON3		0x000c
+#define RK3588_COLOR_FORMAT_MASK	0xf
+#define RK3588_YUV444			0x2
+#define RK3588_YUV420			0x3
+#define RK3588_COMPRESSED_DATA		0xb
+#define RK3588_COLOR_DEPTH_MASK		(0xf << 4)
+#define RK3588_8BPC			(0x5 << 4)
+#define RK3588_10BPC			(0x6 << 4)
+#define RK3588_CECIN_MASK		BIT(8)
+#define RK3588_SCLIN_MASK		BIT(9)
+#define RK3588_SDAIN_MASK		BIT(10)
+#define RK3588_MODE_MASK		BIT(11)
+#define RK3588_COMPRESS_MODE_MASK	BIT(12)
+#define RK3588_I2S_SEL_MASK		BIT(13)
+#define RK3588_SPDIF_SEL_MASK		BIT(14)
+#define RK3588_GRF_VO1_CON4		0x0010
+#define RK3588_HDMI21_MASK		BIT(0)
+#define RK3588_GRF_VO1_CON9		0x0024
+#define RK3588_HDMI0_GRANT_SEL		BIT(10)
+#define RK3588_HDMI0_GRANT_SW		BIT(11)
+#define RK3588_HDMI1_GRANT_SEL		BIT(12)
+#define RK3588_HDMI1_GRANT_SW		BIT(13)
+#define RK3588_GRF_VO1_CON6		0x0018
+#define RK3588_GRF_VO1_CON7		0x001c
+
+#define RK_HDMI_COLORIMETRY_BT2020	(HDMI_COLORIMETRY_EXTENDED + \
+					 HDMI_EXTENDED_COLORIMETRY_BT2020)
+
+#define COLOR_DEPTH_10BIT		BIT(31)
+#define HDMI_FRL_MODE			BIT(30)
+#define HDMI_EARC_MODE			BIT(29)
+
+#define HDMI20_MAX_RATE			600000
+#define HDMI_8K60_RATE			2376000
 
 /**
  * struct rockchip_hdmi_chip_data - splite the grf setting of kind of chips
  * @lcdsel_grf_reg: grf register offset of lcdc select
+ * @ddc_en_reg: grf register offset of hdmi ddc enable
  * @lcdsel_big: reg value of selecting vop big for HDMI
  * @lcdsel_lit: reg value of selecting vop little for HDMI
  */
 struct rockchip_hdmi_chip_data {
 	int	lcdsel_grf_reg;
+	int	ddc_en_reg;
 	u32	lcdsel_big;
 	u32	lcdsel_lit;
 };
 
+/* HDMI output pixel format */
+enum drm_hdmi_output_type {
+	DRM_HDMI_OUTPUT_DEFAULT_RGB, /* default RGB */
+	DRM_HDMI_OUTPUT_YCBCR444, /* YCBCR 444 */
+	DRM_HDMI_OUTPUT_YCBCR422, /* YCBCR 422 */
+	DRM_HDMI_OUTPUT_YCBCR420, /* YCBCR 420 */
+	DRM_HDMI_OUTPUT_YCBCR_HQ, /* Highest subsampled YUV */
+	DRM_HDMI_OUTPUT_YCBCR_LQ, /* Lowest subsampled YUV */
+	DRM_HDMI_OUTPUT_INVALID, /* Guess what ? */
+};
+
+enum dw_hdmi_rockchip_color_depth {
+	ROCKCHIP_HDMI_DEPTH_8,
+	ROCKCHIP_HDMI_DEPTH_10,
+	ROCKCHIP_HDMI_DEPTH_12,
+	ROCKCHIP_HDMI_DEPTH_16,
+	ROCKCHIP_HDMI_DEPTH_420_10,
+	ROCKCHIP_HDMI_DEPTH_420_12,
+	ROCKCHIP_HDMI_DEPTH_420_16
+};
+
+enum hdmi_frl_rate_per_lane {
+	FRL_12G_PER_LANE = 12,
+	FRL_10G_PER_LANE = 10,
+	FRL_8G_PER_LANE = 8,
+	FRL_6G_PER_LANE = 6,
+	FRL_3G_PER_LANE = 3,
+};
+
 struct rockchip_hdmi {
 	struct device *dev;
 	struct regmap *regmap;
+	struct regmap *vo1_regmap;
 	struct drm_encoder encoder;
 	const struct rockchip_hdmi_chip_data *chip_data;
-	struct clk *vpll_clk;
+	struct clk *aud_clk;
+	struct clk *phyref_clk;
 	struct clk *grf_clk;
+	struct clk *hclk_vio;
+	struct clk *hclk_vo1;
+	struct clk *hclk_vop;
+	struct clk *hpd_clk;
+	struct clk *pclk;
+	struct clk *earc_clk;
+	struct clk *hdmitx_ref;
 	struct dw_hdmi *hdmi;
+	struct dw_hdmi_qp *hdmi_qp;
+
 	struct phy *phy;
+
+	u32 max_tmdsclk;
+	bool unsupported_yuv_input;
+	bool unsupported_deep_color;
+	bool skip_check_420_mode;
+	bool mode_changed;
+	u8 force_output;
+	u8 id;
+	bool hpd_stat;
+	bool is_hdmi_qp;
+
+	unsigned long bus_format;
+	unsigned long output_bus_format;
+	unsigned long enc_out_encoding;
+	int color_changed;
+	int hpd_irq;
+
+	struct drm_property *color_depth_property;
+	struct drm_property *hdmi_output_property;
+	struct drm_property *colordepth_capacity;
+	struct drm_property *outputmode_capacity;
+	struct drm_property *colorimetry_property;
+	struct drm_property *quant_range;
+	struct drm_property *hdr_panel_metadata_property;
+	struct drm_property *next_hdr_sink_data_property;
+	struct drm_property *output_hdmi_dvi;
+	struct drm_property *output_type_capacity;
+
+	struct drm_property_blob *hdr_panel_blob_ptr;
+	struct drm_property_blob *next_hdr_data_ptr;
+
+	unsigned int colordepth;
+	unsigned int colorimetry;
+	unsigned int hdmi_quant_range;
+	unsigned int phy_bus_width;
+	enum drm_hdmi_output_type hdmi_output;
+	struct rockchip_drm_sub_dev sub_dev;
+
+	u8 max_frl_rate_per_lane;
+	u8 max_lanes;
+	struct rockchip_drm_dsc_cap dsc_cap;
+	struct next_hdr_sink_data next_hdr_data;
+	struct dw_hdmi_link_config link_cfg;
+	struct gpio_desc *enable_gpio;
+
+	struct delayed_work work;
+	struct workqueue_struct *workqueue;
 };
 
 #define to_rockchip_hdmi(x)	container_of(x, struct rockchip_hdmi, x)
 
-static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
-	{
-		27000000, {
-			{ 0x00b3, 0x0000},
-			{ 0x2153, 0x0000},
-			{ 0x40f3, 0x0000}
-		},
-	}, {
-		36000000, {
-			{ 0x00b3, 0x0000},
-			{ 0x2153, 0x0000},
-			{ 0x40f3, 0x0000}
-		},
-	}, {
-		40000000, {
-			{ 0x00b3, 0x0000},
-			{ 0x2153, 0x0000},
-			{ 0x40f3, 0x0000}
-		},
-	}, {
-		54000000, {
-			{ 0x0072, 0x0001},
-			{ 0x2142, 0x0001},
-			{ 0x40a2, 0x0001},
-		},
-	}, {
-		65000000, {
-			{ 0x0072, 0x0001},
-			{ 0x2142, 0x0001},
-			{ 0x40a2, 0x0001},
-		},
-	}, {
-		66000000, {
-			{ 0x013e, 0x0003},
-			{ 0x217e, 0x0002},
-			{ 0x4061, 0x0002}
-		},
-	}, {
-		74250000, {
-			{ 0x0072, 0x0001},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
-		},
-	}, {
-		83500000, {
-			{ 0x0072, 0x0001},
-		},
-	}, {
-		108000000, {
-			{ 0x0051, 0x0002},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
-		},
-	}, {
-		106500000, {
-			{ 0x0051, 0x0002},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
-		},
-	}, {
-		146250000, {
-			{ 0x0051, 0x0002},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
-		},
-	}, {
-		148500000, {
-			{ 0x0051, 0x0003},
-			{ 0x214c, 0x0003},
-			{ 0x4064, 0x0003}
-		},
-	}, {
-		~0UL, {
-			{ 0x00a0, 0x000a },
-			{ 0x2001, 0x000f },
-			{ 0x4002, 0x000f },
-		},
+/*
+ * There are some rates that would be ranged for better clock jitter at
+ * Chrome OS tree, like 25.175Mhz would range to 25.170732Mhz. But due
+ * to the clock is aglined to KHz in struct drm_display_mode, this would
+ * bring some inaccurate error if we still run the compute_n math, so
+ * let's just code an const table for it until we can actually get the
+ * right clock rate.
+ */
+static const struct dw_hdmi_audio_tmds_n rockchip_werid_tmds_n_table[] = {
+	/* 25176471 for 25.175 MHz = 428000000 / 17. */
+	{ .tmds = 25177000, .n_32k = 4352, .n_44k1 = 14994, .n_48k = 6528, },
+	/* 57290323 for 57.284 MHz */
+	{ .tmds = 57291000, .n_32k = 3968, .n_44k1 = 4557, .n_48k = 5952, },
+	/* 74437500 for 74.44 MHz = 297750000 / 4 */
+	{ .tmds = 74438000, .n_32k = 8192, .n_44k1 = 18816, .n_48k = 4096, },
+	/* 118666667 for 118.68 MHz */
+	{ .tmds = 118667000, .n_32k = 4224, .n_44k1 = 5292, .n_48k = 6336, },
+	/* 121714286 for 121.75 MHz */
+	{ .tmds = 121715000, .n_32k = 4480, .n_44k1 = 6174, .n_48k = 6272, },
+	/* 136800000 for 136.75 MHz */
+	{ .tmds = 136800000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, },
+	/* End of table */
+	{ .tmds = 0,         .n_32k = 0,    .n_44k1 = 0,    .n_48k = 0, },
+};
+
+static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
+	{
+		30666000, {
+			{ 0x00b3, 0x0000 },
+			{ 0x2153, 0x0000 },
+			{ 0x40f3, 0x0000 },
+		},
+	},  {
+		36800000, {
+			{ 0x00b3, 0x0000 },
+			{ 0x2153, 0x0000 },
+			{ 0x40a2, 0x0001 },
+		},
+	},  {
+		46000000, {
+			{ 0x00b3, 0x0000 },
+			{ 0x2142, 0x0001 },
+			{ 0x40a2, 0x0001 },
+		},
+	},  {
+		61333000, {
+			{ 0x0072, 0x0001 },
+			{ 0x2142, 0x0001 },
+			{ 0x40a2, 0x0001 },
+		},
+	},  {
+		73600000, {
+			{ 0x0072, 0x0001 },
+			{ 0x2142, 0x0001 },
+			{ 0x4061, 0x0002 },
+		},
+	},  {
+		92000000, {
+			{ 0x0072, 0x0001 },
+			{ 0x2145, 0x0002 },
+			{ 0x4061, 0x0002 },
+		},
+	},  {
+		122666000, {
+			{ 0x0051, 0x0002 },
+			{ 0x2145, 0x0002 },
+			{ 0x4061, 0x0002 },
+		},
+	},  {
+		147200000, {
+			{ 0x0051, 0x0002 },
+			{ 0x2145, 0x0002 },
+			{ 0x4064, 0x0003 },
+		},
+	},  {
+		184000000, {
+			{ 0x0051, 0x0002 },
+			{ 0x214c, 0x0003 },
+			{ 0x4064, 0x0003 },
+		},
+	},  {
+		226666000, {
+			{ 0x0040, 0x0003 },
+			{ 0x214c, 0x0003 },
+			{ 0x4064, 0x0003 },
+		},
+	},  {
+		272000000, {
+			{ 0x0040, 0x0003 },
+			{ 0x214c, 0x0003 },
+			{ 0x5a64, 0x0003 },
+		},
+	},  {
+		340000000, {
+			{ 0x0040, 0x0003 },
+			{ 0x3b4c, 0x0003 },
+			{ 0x5a64, 0x0003 },
+		},
+	},  {
+		600000000, {
+			{ 0x1a40, 0x0003 },
+			{ 0x3b4c, 0x0003 },
+			{ 0x5a64, 0x0003 },
+		},
+	},  {
+		~0UL, {
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
+		},
+	}
+};
+
+static const struct dw_hdmi_mpll_config rockchip_mpll_cfg_420[] = {
+	{
+		30666000, {
+			{ 0x00b7, 0x0000 },
+			{ 0x2157, 0x0000 },
+			{ 0x40f7, 0x0000 },
+		},
+	},  {
+		92000000, {
+			{ 0x00b7, 0x0000 },
+			{ 0x2143, 0x0001 },
+			{ 0x40a3, 0x0001 },
+		},
+	},  {
+		184000000, {
+			{ 0x0073, 0x0001 },
+			{ 0x2146, 0x0002 },
+			{ 0x4062, 0x0002 },
+		},
+	},  {
+		340000000, {
+			{ 0x0052, 0x0003 },
+			{ 0x214d, 0x0003 },
+			{ 0x4065, 0x0003 },
+		},
+	},  {
+		600000000, {
+			{ 0x0041, 0x0003 },
+			{ 0x3b4d, 0x0003 },
+			{ 0x5a65, 0x0003 },
+		},
+	},  {
+		~0UL, {
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
+		},
+	}
+};
+
+static const struct dw_hdmi_mpll_config rockchip_rk3288w_mpll_cfg_420[] = {
+	{
+		30666000, {
+			{ 0x00b7, 0x0000 },
+			{ 0x2157, 0x0000 },
+			{ 0x40f7, 0x0000 },
+		},
+	},  {
+		92000000, {
+			{ 0x00b7, 0x0000 },
+			{ 0x2143, 0x0001 },
+			{ 0x40a3, 0x0001 },
+		},
+	},  {
+		184000000, {
+			{ 0x0073, 0x0001 },
+			{ 0x2146, 0x0002 },
+			{ 0x4062, 0x0002 },
+		},
+	},  {
+		340000000, {
+			{ 0x0052, 0x0003 },
+			{ 0x214d, 0x0003 },
+			{ 0x4065, 0x0003 },
+		},
+	},  {
+		600000000, {
+			{ 0x0040, 0x0003 },
+			{ 0x3b4c, 0x0003 },
+			{ 0x5a65, 0x0003 },
+		},
+	},  {
+		~0UL, {
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
+		},
+	}
+};
+
+static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
+	/*      pixelclk    bpp8    bpp10   bpp12 */
+	{
+		600000000, { 0x0000, 0x0000, 0x0000 },
+	},  {
+		~0UL,      { 0x0000, 0x0000, 0x0000},
+	}
+};
+
+static struct dw_hdmi_phy_config rockchip_phy_config[] = {
+	/*pixelclk   symbol   term   vlev*/
+	{ 74250000,  0x8009, 0x0004, 0x0272},
+	{ 165000000, 0x802b, 0x0004, 0x0209},
+	{ 297000000, 0x8039, 0x0005, 0x028d},
+	{ 594000000, 0x8039, 0x0000, 0x019d},
+	{ ~0UL,	     0x0000, 0x0000, 0x0000},
+	{ ~0UL,      0x0000, 0x0000, 0x0000},
+};
+
+enum ROW_INDEX_BPP {
+	ROW_INDEX_6BPP = 0,
+	ROW_INDEX_8BPP,
+	ROW_INDEX_10BPP,
+	ROW_INDEX_12BPP,
+	ROW_INDEX_23BPP,
+	MAX_ROW_INDEX
+};
+
+enum COLUMN_INDEX_BPC {
+	COLUMN_INDEX_8BPC = 0,
+	COLUMN_INDEX_10BPC,
+	COLUMN_INDEX_12BPC,
+	COLUMN_INDEX_14BPC,
+	COLUMN_INDEX_16BPC,
+	MAX_COLUMN_INDEX
+};
+
+#define PPS_TABLE_LEN 8
+#define PPS_BPP_LEN 4
+#define PPS_BPC_LEN 2
+
+/* From DSC_v1.11 spec, rc_parameter_Set syntax element typically constant */
+static const u16 rc_buf_thresh[] = {
+	0x0e, 0x1c, 0x2a, 0x38, 0x46, 0x54, 0x62,
+	0x69, 0x70, 0x77, 0x79, 0x7b, 0x7d, 0x7e,
+};
+
+struct rc_parameters {
+	u16 initial_xmit_delay;
+	u16 initial_dec_delay;
+	u8 initial_scale_value;
+	u16 scale_increment_interval;
+	u16 scale_decrement_interval;
+	u8 first_line_bpg_offset;
+	u16 nfl_bpg_offset;
+	u16 slice_bpg_offset;
+	u16 initial_offset;
+	u16 final_offset;
+	u8 flatness_min_qp;
+	u8 flatness_max_qp;
+	u16 rc_model_size;
+	u8 rc_edge_factor;
+	u8 rc_quant_incr_limit0;
+	u8 rc_quant_incr_limit1;
+	u8 rc_tgt_offset_hi;
+	u8 rc_tgt_offset_lo;
+	struct drm_dsc_rc_range_parameters rc_range_params[DSC_NUM_BUF_RANGES];
+};
+
+struct pps_data {
+	u32 pic_width;
+	u32 pic_height;
+	u32 slice_width;
+	u32 slice_height;
+	bool convert_rgb;
+	u8 bpc;
+	u8 bpp;
+	u8 raw_pps[128];
+};
+
+/*
+ * Selected Rate Control Related Parameter Recommended Values
+ * from DSC_v1.11 spec & C Model release: DSC_model_20161212
+ */
+static struct pps_data pps_datas[PPS_TABLE_LEN] = {
+	{
+		/* 7680x4320/960X96 rgb 8bpc 12bpp */
+		7680, 4320, 960, 96, 1, 8, 192,
+		{
+			0x12, 0x00, 0x00, 0x8d, 0x30, 0xc0, 0x10, 0xe0,
+			0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0xa0,
+			0x01, 0x55, 0x03, 0x90, 0x00, 0x0a, 0x05, 0xc9,
+			0x00, 0xa0, 0x00, 0x0f, 0x01, 0x44, 0x01, 0xaa,
+			0x08, 0x00, 0x10, 0xf4, 0x03, 0x0c, 0x20, 0x00,
+			0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
+			0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
+			0x7d, 0x7e, 0x00, 0x82, 0x00, 0xc0, 0x09, 0x00,
+			0x09, 0x7e, 0x19, 0xbc, 0x19, 0xba, 0x19, 0xf8,
+			0x1a, 0x38, 0x1a, 0x38, 0x1a, 0x76, 0x2a, 0x76,
+			0x2a, 0x76, 0x2a, 0x74, 0x3a, 0xb4, 0x52, 0xf4,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+		},
+	},
+	{
+		/* 7680x4320/960X96 rgb 8bpc 11bpp */
+		7680, 4320, 960, 96, 1, 8, 176,
+		{
+			0x12, 0x00, 0x00, 0x8d, 0x30, 0xb0, 0x10, 0xe0,
+			0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0x28,
+			0x01, 0x74, 0x03, 0x40, 0x00, 0x0f, 0x06, 0xe0,
+			0x00, 0x2d, 0x00, 0x0f, 0x01, 0x44, 0x01, 0x33,
+			0x0f, 0x00, 0x10, 0xf4, 0x03, 0x0c, 0x20, 0x00,
+			0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
+			0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
+			0x7d, 0x7e, 0x00, 0x82, 0x01, 0x00, 0x09, 0x40,
+			0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8,
+			0x1a, 0x38, 0x1a, 0x38, 0x1a, 0x76, 0x2a, 0x76,
+			0x2a, 0x76, 0x2a, 0xb4, 0x3a, 0xb4, 0x52, 0xf4,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+		},
+	},
+	{
+		/* 7680x4320/960X96 rgb 8bpc 10bpp */
+		7680, 4320, 960, 96, 1, 8, 160,
+		{
+			0x12, 0x00, 0x00, 0x8d, 0x30, 0xa0, 0x10, 0xe0,
+			0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0xb0,
+			0x01, 0x9a, 0x02, 0xe0, 0x00, 0x19, 0x09, 0xb0,
+			0x00, 0x12, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xbb,
+			0x16, 0x00, 0x10, 0xec, 0x03, 0x0c, 0x20, 0x00,
+			0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
+			0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
+			0x7d, 0x7e, 0x00, 0xc2, 0x01, 0x00, 0x09, 0x40,
+			0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8,
+			0x1a, 0x38, 0x1a, 0x78, 0x1a, 0x76, 0x2a, 0xb6,
+			0x2a, 0xb6, 0x2a, 0xf4, 0x3a, 0xf4, 0x5b, 0x34,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+		},
+	},
+	{
+		/* 7680x4320/960X96 rgb 8bpc 9bpp */
+		7680, 4320, 960, 96, 1, 8, 144,
+		{
+			0x12, 0x00, 0x00, 0x8d, 0x30, 0x90, 0x10, 0xe0,
+			0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0x38,
+			0x01, 0xc7, 0x03, 0x16, 0x00, 0x1c, 0x08, 0xc7,
+			0x00, 0x10, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xaa,
+			0x17, 0x00, 0x10, 0xf1, 0x03, 0x0c, 0x20, 0x00,
+			0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
+			0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
+			0x7d, 0x7e, 0x00, 0xc2, 0x01, 0x00, 0x09, 0x40,
+			0x09, 0xbe, 0x19, 0xfc, 0x19, 0xfa, 0x19, 0xf8,
+			0x1a, 0x38, 0x1a, 0x78, 0x1a, 0x76, 0x2a, 0xb6,
+			0x2a, 0xb6, 0x2a, 0xf4, 0x3a, 0xf4, 0x63, 0x74,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+		},
+	},
+	{
+		/* 7680x4320/960X96 rgb 10bpc 12bpp */
+		7680, 4320, 960, 96, 1, 10, 192,
+		{
+			0x12, 0x00, 0x00, 0xad, 0x30, 0xc0, 0x10, 0xe0,
+			0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0xa0,
+			0x01, 0x55, 0x03, 0x90, 0x00, 0x0a, 0x05, 0xc9,
+			0x00, 0xa0, 0x00, 0x0f, 0x01, 0x44, 0x01, 0xaa,
+			0x08, 0x00, 0x10, 0xf4, 0x07, 0x10, 0x20, 0x00,
+			0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
+			0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
+			0x7d, 0x7e, 0x01, 0x02, 0x11, 0x80, 0x22, 0x00,
+			0x22, 0x7e, 0x32, 0xbc, 0x32, 0xba, 0x3a, 0xf8,
+			0x3b, 0x38, 0x3b, 0x38, 0x3b, 0x76, 0x4b, 0x76,
+			0x4b, 0x76, 0x4b, 0x74, 0x5b, 0xb4, 0x73, 0xf4,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+		},
+	},
+	{
+		/* 7680x4320/960X96 rgb 10bpc 11bpp */
+		7680, 4320, 960, 96, 1, 10, 176,
+		{
+			0x12, 0x00, 0x00, 0xad, 0x30, 0xb0, 0x10, 0xe0,
+			0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x05, 0x28,
+			0x01, 0x74, 0x03, 0x40, 0x00, 0x0f, 0x06, 0xe0,
+			0x00, 0x2d, 0x00, 0x0f, 0x01, 0x44, 0x01, 0x33,
+			0x0f, 0x00, 0x10, 0xf4, 0x07, 0x10, 0x20, 0x00,
+			0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
+			0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
+			0x7d, 0x7e, 0x01, 0x42, 0x19, 0xc0, 0x2a, 0x40,
+			0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8,
+			0x3b, 0x38, 0x3b, 0x38, 0x3b, 0x76, 0x4b, 0x76,
+			0x4b, 0x76, 0x4b, 0xb4, 0x5b, 0xb4, 0x73, 0xf4,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+		},
+	},
+	{
+		/* 7680x4320/960X96 rgb 10bpc 10bpp */
+		7680, 4320, 960, 96, 1, 10, 160,
+		{
+			0x12, 0x00, 0x00, 0xad, 0x30, 0xa0, 0x10, 0xe0,
+			0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0xb0,
+			0x01, 0x9a, 0x02, 0xe0, 0x00, 0x19, 0x09, 0xb0,
+			0x00, 0x12, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xbb,
+			0x16, 0x00, 0x10, 0xec, 0x07, 0x10, 0x20, 0x00,
+			0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
+			0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
+			0x7d, 0x7e, 0x01, 0xc2, 0x22, 0x00, 0x2a, 0x40,
+			0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8,
+			0x3b, 0x38, 0x3b, 0x78, 0x3b, 0x76, 0x4b, 0xb6,
+			0x4b, 0xb6, 0x4b, 0xf4, 0x63, 0xf4, 0x7c, 0x34,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+		},
+	},
+	{
+		/* 7680x4320/960X96 rgb 10bpc 9bpp */
+		7680, 4320, 960, 96, 1, 10, 144,
+		{
+			0x12, 0x00, 0x00, 0xad, 0x30, 0x90, 0x10, 0xe0,
+			0x1e, 0x00, 0x00, 0x60, 0x03, 0xc0, 0x04, 0x38,
+			0x01, 0xc7, 0x03, 0x16, 0x00, 0x1c, 0x08, 0xc7,
+			0x00, 0x10, 0x00, 0x0f, 0x01, 0x44, 0x00, 0xaa,
+			0x17, 0x00, 0x10, 0xf1, 0x07, 0x10, 0x20, 0x00,
+			0x06, 0x0f, 0x0f, 0x33, 0x0e, 0x1c, 0x2a, 0x38,
+			0x46, 0x54, 0x62, 0x69, 0x70, 0x77, 0x79, 0x7b,
+			0x7d, 0x7e, 0x01, 0xc2, 0x22, 0x00, 0x2a, 0x40,
+			0x2a, 0xbe, 0x3a, 0xfc, 0x3a, 0xfa, 0x3a, 0xf8,
+			0x3b, 0x38, 0x3b, 0x78, 0x3b, 0x76, 0x4b, 0xb6,
+			0x4b, 0xb6, 0x4b, 0xf4, 0x63, 0xf4, 0x84, 0x74,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+		},
+	},
+};
+
+static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format)
+{
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_RGB888_1X24:
+	case MEDIA_BUS_FMT_RGB101010_1X30:
+	case MEDIA_BUS_FMT_RGB121212_1X36:
+	case MEDIA_BUS_FMT_RGB161616_1X48:
+		return true;
+
+	default:
+		return false;
+	}
+}
+
+static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format)
+{
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_YUV8_1X24:
+	case MEDIA_BUS_FMT_YUV10_1X30:
+	case MEDIA_BUS_FMT_YUV12_1X36:
+	case MEDIA_BUS_FMT_YUV16_1X48:
+		return true;
+
+	default:
+		return false;
+	}
+}
+
+static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format)
+{
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_UYVY8_1X16:
+	case MEDIA_BUS_FMT_UYVY10_1X20:
+	case MEDIA_BUS_FMT_UYVY12_1X24:
+		return true;
+
+	default:
+		return false;
+	}
+}
+
+static bool hdmi_bus_fmt_is_yuv420(unsigned int bus_format)
+{
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+	case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
+	case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
+		return true;
+
+	default:
+	return false;
+	}
+}
+
+static int hdmi_bus_fmt_color_depth(unsigned int bus_format)
+{
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_RGB888_1X24:
+	case MEDIA_BUS_FMT_YUV8_1X24:
+	case MEDIA_BUS_FMT_UYVY8_1X16:
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+		return 8;
+
+	case MEDIA_BUS_FMT_RGB101010_1X30:
+	case MEDIA_BUS_FMT_YUV10_1X30:
+	case MEDIA_BUS_FMT_UYVY10_1X20:
+	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+		return 10;
+
+	case MEDIA_BUS_FMT_RGB121212_1X36:
+	case MEDIA_BUS_FMT_YUV12_1X36:
+	case MEDIA_BUS_FMT_UYVY12_1X24:
+	case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
+		return 12;
+
+	case MEDIA_BUS_FMT_RGB161616_1X48:
+	case MEDIA_BUS_FMT_YUV16_1X48:
+	case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
+		return 16;
+
+	default:
+		return 0;
+	}
+}
+
+static unsigned int
+hdmi_get_tmdsclock(struct rockchip_hdmi *hdmi, unsigned long pixelclock)
+{
+	unsigned int tmdsclock = pixelclock;
+	unsigned int depth =
+		hdmi_bus_fmt_color_depth(hdmi->output_bus_format);
+
+	if (!hdmi_bus_fmt_is_yuv422(hdmi->output_bus_format)) {
+		switch (depth) {
+		case 16:
+			tmdsclock = pixelclock * 2;
+			break;
+		case 12:
+			tmdsclock = pixelclock * 3 / 2;
+			break;
+		case 10:
+			tmdsclock = pixelclock * 5 / 4;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return tmdsclock;
+}
+
+static void hdmi_select_link_config(struct rockchip_hdmi *hdmi,
+				    struct drm_crtc_state *crtc_state,
+				    unsigned int tmdsclk)
+{
+	struct drm_display_mode *mode = &crtc_state->mode;
+	int max_lanes, max_rate_per_lane;
+	int max_dsc_lanes, max_dsc_rate_per_lane;
+	unsigned long max_frl_rate;
+
+	max_lanes = hdmi->max_lanes;
+	max_rate_per_lane = hdmi->max_frl_rate_per_lane;
+	max_frl_rate = max_lanes * max_rate_per_lane * 1000000;
+
+	hdmi->link_cfg.dsc_mode = false;
+	hdmi->link_cfg.frl_lanes = max_lanes;
+	hdmi->link_cfg.rate_per_lane = max_rate_per_lane;
+
+	if (!max_frl_rate || tmdsclk < HDMI20_MAX_RATE) {
+		dev_info(hdmi->dev, "use tmds mode\n");
+		hdmi->link_cfg.frl_mode = false;
+		return;
+	}
+
+	hdmi->link_cfg.frl_mode = true;
+
+	if (!hdmi->dsc_cap.v_1p2)
+		return;
+
+	max_dsc_lanes = hdmi->dsc_cap.max_lanes;
+	max_dsc_rate_per_lane =
+		hdmi->dsc_cap.max_frl_rate_per_lane;
+
+	if (mode->clock >= HDMI_8K60_RATE &&
+	    !hdmi_bus_fmt_is_yuv420(hdmi->bus_format) &&
+	    !hdmi_bus_fmt_is_yuv422(hdmi->bus_format)) {
+		hdmi->link_cfg.dsc_mode = true;
+		hdmi->link_cfg.frl_lanes = max_dsc_lanes;
+		hdmi->link_cfg.rate_per_lane = max_dsc_rate_per_lane;
+	} else {
+		hdmi->link_cfg.dsc_mode = false;
+		hdmi->link_cfg.frl_lanes = max_lanes;
+		hdmi->link_cfg.rate_per_lane = max_rate_per_lane;
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+static int hdmi_dsc_get_slice_height(int vactive)
+{
+	int slice_height;
+
+	/*
+	 * Slice Height determination : HDMI2.1 Section 7.7.5.2
+	 * Select smallest slice height >=96, that results in a valid PPS and
+	 * requires minimum padding lines required for final slice.
+	 *
+	 * Assumption : Vactive is even.
+	 */
+	for (slice_height = 96; slice_height <= vactive; slice_height += 2)
+		if (vactive % slice_height == 0)
+			return slice_height;
+
+	return 0;
+}
+
+static int hdmi_dsc_get_num_slices(struct rockchip_hdmi *hdmi,
+				   struct drm_crtc_state *crtc_state,
+				   int src_max_slices, int src_max_slice_width,
+				   int hdmi_max_slices, int hdmi_throughput)
+{
+/* Pixel rates in KPixels/sec */
+#define HDMI_DSC_PEAK_PIXEL_RATE		2720000
+/*
+ * Rates at which the source and sink are required to process pixels in each
+ * slice, can be two levels: either at least 340000KHz or at least 40000KHz.
+ */
+#define HDMI_DSC_MAX_ENC_THROUGHPUT_0		340000
+#define HDMI_DSC_MAX_ENC_THROUGHPUT_1		400000
+
+/* Spec limits the slice width to 2720 pixels */
+#define MAX_HDMI_SLICE_WIDTH			2720
+	int kslice_adjust;
+	int adjusted_clk_khz;
+	int min_slices;
+	int target_slices;
+	int max_throughput; /* max clock freq. in khz per slice */
+	int max_slice_width;
+	int slice_width;
+	int pixel_clock = crtc_state->mode.clock;
+
+	if (!hdmi_throughput)
+		return 0;
+
+	/*
+	 * Slice Width determination : HDMI2.1 Section 7.7.5.1
+	 * kslice_adjust factor for 4:2:0, and 4:2:2 formats is 0.5, where as
+	 * for 4:4:4 is 1.0. Multiplying these factors by 10 and later
+	 * dividing adjusted clock value by 10.
+	 */
+	if (hdmi_bus_fmt_is_yuv444(hdmi->output_bus_format) ||
+	    hdmi_bus_fmt_is_rgb(hdmi->output_bus_format))
+		kslice_adjust = 10;
+	else
+		kslice_adjust = 5;
+
+	/*
+	 * As per spec, the rate at which the source and the sink process
+	 * the pixels per slice are at two levels: at least 340Mhz or 400Mhz.
+	 * This depends upon the pixel clock rate and output formats
+	 * (kslice adjust).
+	 * If pixel clock * kslice adjust >= 2720MHz slices can be processed
+	 * at max 340MHz, otherwise they can be processed at max 400MHz.
+	 */
+
+	adjusted_clk_khz = DIV_ROUND_UP(kslice_adjust * pixel_clock, 10);
+
+	if (adjusted_clk_khz <= HDMI_DSC_PEAK_PIXEL_RATE)
+		max_throughput = HDMI_DSC_MAX_ENC_THROUGHPUT_0;
+	else
+		max_throughput = HDMI_DSC_MAX_ENC_THROUGHPUT_1;
+
+	/*
+	 * Taking into account the sink's capability for maximum
+	 * clock per slice (in MHz) as read from HF-VSDB.
+	 */
+	max_throughput = min(max_throughput, hdmi_throughput * 1000);
+
+	min_slices = DIV_ROUND_UP(adjusted_clk_khz, max_throughput);
+	max_slice_width = min(MAX_HDMI_SLICE_WIDTH, src_max_slice_width);
+
+	/*
+	 * Keep on increasing the num of slices/line, starting from min_slices
+	 * per line till we get such a number, for which the slice_width is
+	 * just less than max_slice_width. The slices/line selected should be
+	 * less than or equal to the max horizontal slices that the combination
+	 * of PCON encoder and HDMI decoder can support.
+	 */
+	do {
+		if (min_slices <= 1 && src_max_slices >= 1 && hdmi_max_slices >= 1)
+			target_slices = 1;
+		else if (min_slices <= 2 && src_max_slices >= 2 && hdmi_max_slices >= 2)
+			target_slices = 2;
+		else if (min_slices <= 4 && src_max_slices >= 4 && hdmi_max_slices >= 4)
+			target_slices = 4;
+		else if (min_slices <= 8 && src_max_slices >= 8 && hdmi_max_slices >= 8)
+			target_slices = 8;
+		else if (min_slices <= 12 && src_max_slices >= 12 && hdmi_max_slices >= 12)
+			target_slices = 12;
+		else if (min_slices <= 16 && src_max_slices >= 16 && hdmi_max_slices >= 16)
+			target_slices = 16;
+		else
+			return 0;
+
+		slice_width = DIV_ROUND_UP(crtc_state->mode.hdisplay, target_slices);
+		if (slice_width > max_slice_width)
+			min_slices = target_slices + 1;
+	} while (slice_width > max_slice_width);
+
+	return target_slices;
+}
+
+static int hdmi_dsc_slices(struct rockchip_hdmi *hdmi,
+			   struct drm_crtc_state *crtc_state)
+{
+	int hdmi_throughput = hdmi->dsc_cap.clk_per_slice;
+	int hdmi_max_slices = hdmi->dsc_cap.max_slices;
+	int rk_max_slices = 8;
+	int rk_max_slice_width = 2048;
+
+	return hdmi_dsc_get_num_slices(hdmi, crtc_state, rk_max_slices,
+				       rk_max_slice_width,
+				       hdmi_max_slices, hdmi_throughput);
+}
+
+static int
+hdmi_dsc_get_bpp(struct rockchip_hdmi *hdmi, int src_fractional_bpp,
+		 int slice_width, int num_slices, bool hdmi_all_bpp,
+		 int hdmi_max_chunk_bytes)
+{
+	int max_dsc_bpp, min_dsc_bpp;
+	int target_bytes;
+	bool bpp_found = false;
+	int bpp_decrement_x16;
+	int bpp_target;
+	int bpp_target_x16;
+
+	/*
+	 * Get min bpp and max bpp as per Table 7.23, in HDMI2.1 spec
+	 * Start with the max bpp and keep on decrementing with
+	 * fractional bpp, if supported by PCON DSC encoder
+	 *
+	 * for each bpp we check if no of bytes can be supported by HDMI sink
+	 */
+
+	/* only 9\10\12 bpp was tested */
+	min_dsc_bpp = 9;
+	max_dsc_bpp = 12;
+
+	/*
+	 * Taking into account if all dsc_all_bpp supported by HDMI2.1 sink
+	 * Section 7.7.34 : Source shall not enable compressed Video
+	 * Transport with bpp_target settings above 12 bpp unless
+	 * DSC_all_bpp is set to 1.
+	 */
+	if (!hdmi_all_bpp)
+		max_dsc_bpp = min(max_dsc_bpp, 12);
+
+	/*
+	 * The Sink has a limit of compressed data in bytes for a scanline,
+	 * as described in max_chunk_bytes field in HFVSDB block of edid.
+	 * The no. of bytes depend on the target bits per pixel that the
+	 * source configures. So we start with the max_bpp and calculate
+	 * the target_chunk_bytes. We keep on decrementing the target_bpp,
+	 * till we get the target_chunk_bytes just less than what the sink's
+	 * max_chunk_bytes, or else till we reach the min_dsc_bpp.
+	 *
+	 * The decrement is according to the fractional support from PCON DSC
+	 * encoder. For fractional BPP we use bpp_target as a multiple of 16.
+	 *
+	 * bpp_target_x16 = bpp_target * 16
+	 * So we need to decrement by {1, 2, 4, 8, 16} for fractional bpps
+	 * {1/16, 1/8, 1/4, 1/2, 1} respectively.
+	 */
+
+	bpp_target = max_dsc_bpp;
+
+	/* src does not support fractional bpp implies decrement by 16 for bppx16 */
+	if (!src_fractional_bpp)
+		src_fractional_bpp = 1;
+	bpp_decrement_x16 = DIV_ROUND_UP(16, src_fractional_bpp);
+	bpp_target_x16 = bpp_target * 16;
+
+	while (bpp_target_x16 > (min_dsc_bpp * 16)) {
+		int bpp;
+
+		bpp = DIV_ROUND_UP(bpp_target_x16, 16);
+		target_bytes = DIV_ROUND_UP((num_slices * slice_width * bpp), 8);
+		if (target_bytes <= hdmi_max_chunk_bytes) {
+			bpp_found = true;
+			break;
+		}
+		bpp_target_x16 -= bpp_decrement_x16;
+	}
+	if (bpp_found)
+		return bpp_target_x16;
+
+	return 0;
+}
+
+static int
+dw_hdmi_dsc_bpp(struct rockchip_hdmi *hdmi,
+		int num_slices, int slice_width)
+{
+	bool hdmi_all_bpp = hdmi->dsc_cap.all_bpp;
+	int fractional_bpp = 0;
+	int hdmi_max_chunk_bytes = hdmi->dsc_cap.total_chunk_kbytes * 1024;
+
+	return hdmi_dsc_get_bpp(hdmi, fractional_bpp, slice_width,
+				num_slices, hdmi_all_bpp,
+				hdmi_max_chunk_bytes);
+}
+
+static int dw_hdmi_qp_set_link_cfg(struct rockchip_hdmi *hdmi,
+				   u16 pic_width, u16 pic_height,
+				   u16 slice_width, u16 slice_height,
+				   u16 bits_per_pixel, u8 bits_per_component)
+{
+	int i;
+
+	for (i = 0; i < PPS_TABLE_LEN; i++)
+		if (pic_width == pps_datas[i].pic_width &&
+		    pic_height == pps_datas[i].pic_height &&
+		    slice_width == pps_datas[i].slice_width &&
+		    slice_height == pps_datas[i].slice_height &&
+		    bits_per_component == pps_datas[i].bpc &&
+		    bits_per_pixel == pps_datas[i].bpp &&
+		    hdmi_bus_fmt_is_rgb(hdmi->output_bus_format) == pps_datas[i].convert_rgb)
+			break;
+
+	if (i == PPS_TABLE_LEN) {
+		dev_err(hdmi->dev, "can't find pps cfg!\n");
+		return -EINVAL;
+	}
+
+	memcpy(hdmi->link_cfg.pps_payload, pps_datas[i].raw_pps, 128);
+	hdmi->link_cfg.hcactive = DIV_ROUND_UP(slice_width * (bits_per_pixel / 16), 8) *
+		(pic_width / slice_width);
+
+	return 0;
+}
+
+static void dw_hdmi_qp_dsc_configure(struct rockchip_hdmi *hdmi,
+				     struct rockchip_crtc_state *s,
+				     struct drm_crtc_state *crtc_state)
+{
+	int ret;
+	int slice_height;
+	int slice_width;
+	int bits_per_pixel;
+	int slice_count;
+	bool hdmi_is_dsc_1_2;
+	unsigned int depth = hdmi_bus_fmt_color_depth(hdmi->output_bus_format);
+
+	if (!crtc_state)
+		return;
+
+	hdmi_is_dsc_1_2 = hdmi->dsc_cap.v_1p2;
+
+	if (!hdmi_is_dsc_1_2)
+		return;
+
+	slice_height = hdmi_dsc_get_slice_height(crtc_state->mode.vdisplay);
+	if (!slice_height)
+		return;
+
+	slice_count = hdmi_dsc_slices(hdmi, crtc_state);
+	if (!slice_count)
+		return;
+
+	slice_width = DIV_ROUND_UP(crtc_state->mode.hdisplay, slice_count);
+
+	bits_per_pixel = dw_hdmi_dsc_bpp(hdmi, slice_count, slice_width);
+	if (!bits_per_pixel)
+		return;
+
+	ret = dw_hdmi_qp_set_link_cfg(hdmi, crtc_state->mode.hdisplay,
+				      crtc_state->mode.vdisplay, slice_width,
+				      slice_height, bits_per_pixel, depth);
+
+	if (ret) {
+		dev_err(hdmi->dev, "set vdsc cfg failed\n");
+		return;
+	}
+	dev_info(hdmi->dev, "dsc_enable\n");
+	s->dsc_enable = 1;
+	s->dsc_sink_cap.version_major = 1;
+	s->dsc_sink_cap.version_minor = 2;
+	s->dsc_sink_cap.slice_width = slice_width;
+	s->dsc_sink_cap.slice_height = slice_height;
+	s->dsc_sink_cap.target_bits_per_pixel_x16 = bits_per_pixel;
+	s->dsc_sink_cap.block_pred = 1;
+	s->dsc_sink_cap.native_420 = 0;
+
+	memcpy(&s->pps, hdmi->link_cfg.pps_payload, 128);
+}
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int rockchip_hdmi_update_phy_table(struct rockchip_hdmi *hdmi,
+					  u32 *config,
+					  int phy_table_size)
+{
+	int i;
+
+	if (phy_table_size > ARRAY_SIZE(rockchip_phy_config)) {
+		dev_err(hdmi->dev, "phy table array number is out of range\n");
+		return -E2BIG;
+	}
+
+	for (i = 0; i < phy_table_size; i++) {
+		if (config[i * 4] != 0)
+			rockchip_phy_config[i].mpixelclock = (u64)config[i * 4];
+		else
+			rockchip_phy_config[i].mpixelclock = ~0UL;
+		rockchip_phy_config[i].sym_ctr = (u16)config[i * 4 + 1];
+		rockchip_phy_config[i].term = (u16)config[i * 4 + 2];
+		rockchip_phy_config[i].vlev_ctr = (u16)config[i * 4 + 3];
+	}
+
+	return 0;
+}
+
+static void repo_hpd_event(struct work_struct *p_work)
+{
+	struct rockchip_hdmi *hdmi = container_of(p_work, struct rockchip_hdmi, work.work);
+	bool change;
+
+	change = drm_helper_hpd_irq_event(hdmi->encoder.dev);
+	if (change) {
+		dev_dbg(hdmi->dev, "hpd stat changed:%d\n", hdmi->hpd_stat);
+		dw_hdmi_qp_cec_set_hpd(hdmi->hdmi_qp, hdmi->hpd_stat, change);
+	}
+}
+
+static irqreturn_t rockchip_hdmi_hardirq(int irq, void *dev_id)
+{
+	struct rockchip_hdmi *hdmi = dev_id;
+	u32 intr_stat, val;
+
+	regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
+
+	if (intr_stat) {
+		dev_dbg(hdmi->dev, "hpd irq %#x\n", intr_stat);
+
+		if (!hdmi->id)
+			val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK,
+					    RK3588_HDMI0_HPD_INT_MSK);
+		else
+			val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK,
+					    RK3588_HDMI1_HPD_INT_MSK);
+		regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+		return IRQ_WAKE_THREAD;
+	}
+
+	return IRQ_NONE;
+}
+
+static irqreturn_t rockchip_hdmi_irq(int irq, void *dev_id)
+{
+	struct rockchip_hdmi *hdmi = dev_id;
+	u32 intr_stat, val;
+	int msecs;
+	bool stat;
+
+	regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
+
+	if (!intr_stat)
+		return IRQ_NONE;
+
+	if (!hdmi->id) {
+		val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
+				    RK3588_HDMI0_HPD_INT_CLR);
+		if (intr_stat & RK3588_HDMI0_LEVEL_INT)
+			stat = true;
+		else
+			stat = false;
+	} else {
+		val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR,
+				    RK3588_HDMI1_HPD_INT_CLR);
+		if (intr_stat & RK3588_HDMI1_LEVEL_INT)
+			stat = true;
+		else
+			stat = false;
+	}
+
+	regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+
+	if (stat) {
+		hdmi->hpd_stat = true;
+		msecs = 150;
+	} else {
+		hdmi->hpd_stat = false;
+		msecs = 20;
+	}
+	mod_delayed_work(hdmi->workqueue, &hdmi->work, msecs_to_jiffies(msecs));
+
+	if (!hdmi->id) {
+		val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
+				    RK3588_HDMI0_HPD_INT_CLR) |
+		      HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK);
+	} else {
+		val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR,
+				    RK3588_HDMI1_HPD_INT_CLR) |
+		      HIWORD_UPDATE(0, RK3588_HDMI1_HPD_INT_MSK);
+	}
+
+	regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+
+	return IRQ_HANDLED;
+}
+
+static void init_hpd_work(struct rockchip_hdmi *hdmi)
+{
+	hdmi->workqueue = create_workqueue("hpd_queue");
+	INIT_DELAYED_WORK(&hdmi->work, repo_hpd_event);
+}
+
+static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
+{
+	int ret, val, phy_table_size;
+	u32 *phy_config;
+	struct device_node *np = hdmi->dev->of_node;
+
+	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+	if (IS_ERR(hdmi->regmap)) {
+		DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,grf\n");
+		return PTR_ERR(hdmi->regmap);
+	}
+
+	if (hdmi->is_hdmi_qp) {
+		hdmi->vo1_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,vo1_grf");
+		if (IS_ERR(hdmi->vo1_regmap)) {
+			DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,vo1_grf\n");
+			return PTR_ERR(hdmi->vo1_regmap);
+		}
+	}
+
+	hdmi->phyref_clk = devm_clk_get(hdmi->dev, "vpll");
+	if (PTR_ERR(hdmi->phyref_clk) == -ENOENT)
+		hdmi->phyref_clk = devm_clk_get(hdmi->dev, "ref");
+
+	if (PTR_ERR(hdmi->phyref_clk) == -ENOENT) {
+		hdmi->phyref_clk = NULL;
+	} else if (PTR_ERR(hdmi->phyref_clk) == -EPROBE_DEFER) {
+		return -EPROBE_DEFER;
+	} else if (IS_ERR(hdmi->phyref_clk)) {
+		DRM_DEV_ERROR(hdmi->dev, "failed to get grf clock\n");
+		return PTR_ERR(hdmi->phyref_clk);
+	}
+
+	hdmi->grf_clk = devm_clk_get(hdmi->dev, "grf");
+	if (PTR_ERR(hdmi->grf_clk) == -ENOENT) {
+		hdmi->grf_clk = NULL;
+	} else if (PTR_ERR(hdmi->grf_clk) == -EPROBE_DEFER) {
+		return -EPROBE_DEFER;
+	} else if (IS_ERR(hdmi->grf_clk)) {
+		DRM_DEV_ERROR(hdmi->dev, "failed to get grf clock\n");
+		return PTR_ERR(hdmi->grf_clk);
+	}
+
+	hdmi->hclk_vio = devm_clk_get(hdmi->dev, "hclk_vio");
+	if (PTR_ERR(hdmi->hclk_vio) == -ENOENT) {
+		hdmi->hclk_vio = NULL;
+	} else if (PTR_ERR(hdmi->hclk_vio) == -EPROBE_DEFER) {
+		return -EPROBE_DEFER;
+	} else if (IS_ERR(hdmi->hclk_vio)) {
+		dev_err(hdmi->dev, "failed to get hclk_vio clock\n");
+		return PTR_ERR(hdmi->hclk_vio);
+	}
+
+	hdmi->hclk_vop = devm_clk_get(hdmi->dev, "hclk");
+	if (PTR_ERR(hdmi->hclk_vop) == -ENOENT) {
+		hdmi->hclk_vop = NULL;
+	} else if (PTR_ERR(hdmi->hclk_vop) == -EPROBE_DEFER) {
+		return -EPROBE_DEFER;
+	} else if (IS_ERR(hdmi->hclk_vop)) {
+		dev_err(hdmi->dev, "failed to get hclk_vop clock\n");
+		return PTR_ERR(hdmi->hclk_vop);
+	}
+
+	hdmi->aud_clk = devm_clk_get_optional(hdmi->dev, "aud");
+	if (IS_ERR(hdmi->aud_clk)) {
+		dev_err_probe(hdmi->dev, PTR_ERR(hdmi->aud_clk),
+			      "failed to get aud_clk clock\n");
+		return PTR_ERR(hdmi->aud_clk);
+	}
+
+	hdmi->hpd_clk = devm_clk_get_optional(hdmi->dev, "hpd");
+	if (IS_ERR(hdmi->hpd_clk)) {
+		dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hpd_clk),
+			      "failed to get hpd_clk clock\n");
+		return PTR_ERR(hdmi->hpd_clk);
+	}
+
+	hdmi->hclk_vo1 = devm_clk_get_optional(hdmi->dev, "hclk_vo1");
+	if (IS_ERR(hdmi->hclk_vo1)) {
+		dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hclk_vo1),
+			      "failed to get hclk_vo1 clock\n");
+		return PTR_ERR(hdmi->hclk_vo1);
+	}
+
+	hdmi->earc_clk = devm_clk_get_optional(hdmi->dev, "earc");
+	if (IS_ERR(hdmi->earc_clk)) {
+		dev_err_probe(hdmi->dev, PTR_ERR(hdmi->earc_clk),
+			      "failed to get earc_clk clock\n");
+		return PTR_ERR(hdmi->earc_clk);
+	}
+
+	hdmi->hdmitx_ref = devm_clk_get_optional(hdmi->dev, "hdmitx_ref");
+	if (IS_ERR(hdmi->hdmitx_ref)) {
+		dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hdmitx_ref),
+			      "failed to get hdmitx_ref clock\n");
+		return PTR_ERR(hdmi->hdmitx_ref);
+	}
+
+	hdmi->pclk = devm_clk_get_optional(hdmi->dev, "pclk");
+	if (IS_ERR(hdmi->pclk)) {
+		dev_err_probe(hdmi->dev, PTR_ERR(hdmi->pclk),
+			      "failed to get pclk clock\n");
+		return PTR_ERR(hdmi->pclk);
+	}
+
+	hdmi->enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable",
+						    GPIOD_OUT_HIGH);
+	if (IS_ERR(hdmi->enable_gpio)) {
+		ret = PTR_ERR(hdmi->enable_gpio);
+		dev_err(hdmi->dev, "failed to request enable GPIO: %d\n", ret);
+		return ret;
+	}
+
+	hdmi->skip_check_420_mode =
+		of_property_read_bool(np, "skip-check-420-mode");
+
+	if (of_get_property(np, "rockchip,phy-table", &val)) {
+		phy_config = kmalloc(val, GFP_KERNEL);
+		if (!phy_config) {
+			/* use default table when kmalloc failed. */
+			dev_err(hdmi->dev, "kmalloc phy table failed\n");
+
+			return -ENOMEM;
+		}
+		phy_table_size = val / 16;
+		of_property_read_u32_array(np, "rockchip,phy-table",
+					   phy_config, val / sizeof(u32));
+		ret = rockchip_hdmi_update_phy_table(hdmi, phy_config,
+						     phy_table_size);
+		if (ret) {
+			kfree(phy_config);
+			return ret;
+		}
+		kfree(phy_config);
+	} else {
+		dev_dbg(hdmi->dev, "use default hdmi phy table\n");
+	}
+
+	return 0;
+}
+
+static enum drm_mode_status
+dw_hdmi_rockchip_mode_valid(struct drm_connector *connector, void *data,
+			    const struct drm_display_info *info,
+			    const struct drm_display_mode *mode)
+{
+	struct drm_encoder *encoder = connector->encoder;
+	enum drm_mode_status status = MODE_OK;
+	struct drm_device *dev = connector->dev;
+	struct rockchip_drm_private *priv = dev->dev_private;
+	struct drm_crtc *crtc;
+	struct rockchip_hdmi *hdmi;
+
+	/*
+	 * Pixel clocks we support are always < 2GHz and so fit in an
+	 * int.  We should make sure source rate does too so we don't get
+	 * overflow when we multiply by 1000.
+	 */
+	if (mode->clock > INT_MAX / 1000)
+		return MODE_BAD;
+
+	if (!encoder) {
+		const struct drm_connector_helper_funcs *funcs;
+
+		funcs = connector->helper_private;
+		if (funcs->atomic_best_encoder)
+			encoder = funcs->atomic_best_encoder(connector,
+							     connector->state);
+		else
+			encoder = funcs->best_encoder(connector);
+	}
+
+	if (!encoder || !encoder->possible_crtcs)
+		return MODE_BAD;
+
+	hdmi = to_rockchip_hdmi(encoder);
+
+	/*
+	 * If sink max TMDS clock < 340MHz, we should check the mode pixel
+	 * clock > 340MHz is YCbCr420 or not and whether the platform supports
+	 * YCbCr420.
+	 */
+	if (!hdmi->skip_check_420_mode) {
+		if (mode->clock > 340000 &&
+		    connector->display_info.max_tmds_clock < 340000 &&
+		    (!drm_mode_is_420(&connector->display_info, mode) ||
+		     !connector->ycbcr_420_allowed))
+			return MODE_BAD;
+
+		if (hdmi->max_tmdsclk <= 340000 && mode->clock > 340000 &&
+		    !drm_mode_is_420(&connector->display_info, mode))
+			return MODE_BAD;
+	};
+
+	if (hdmi->phy) {
+		if (hdmi->is_hdmi_qp)
+			phy_set_bus_width(hdmi->phy, mode->clock * 10);
+		else
+			phy_set_bus_width(hdmi->phy, 8);
+	}
+
+	/*
+	 * ensure all drm display mode can work, if someone want support more
+	 * resolutions, please limit the possible_crtc, only connect to
+	 * needed crtc.
+	 */
+	drm_for_each_crtc(crtc, connector->dev) {
+		int pipe = drm_crtc_index(crtc);
+		const struct rockchip_crtc_funcs *funcs =
+						priv->crtc_funcs[pipe];
+
+		if (!(encoder->possible_crtcs & drm_crtc_mask(crtc)))
+			continue;
+		if (!funcs || !funcs->mode_valid)
+			continue;
+
+		status = funcs->mode_valid(crtc, mode,
+					   DRM_MODE_CONNECTOR_HDMIA);
+		if (status != MODE_OK)
+			return status;
+	}
+
+	return status;
+}
+
+static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
+{
+	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
+	struct drm_crtc *crtc = encoder->crtc;
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state);
+
+	if (!hdmi->mode_changed) {
+		if (!hdmi->id)
+			s->output_if &= ~VOP_OUTPUT_IF_HDMI1;
+		else
+			s->output_if &= ~VOP_OUTPUT_IF_HDMI0;
+	}
+	/*
+	 * when plug out hdmi it will be switch cvbs and then phy bus width
+	 * must be set as 8
+	 */
+	if (hdmi->phy)
+		phy_set_bus_width(hdmi->phy, 8);
+}
+
+static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
+{
+	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
+	struct drm_crtc *crtc = encoder->crtc;
+	u32 val;
+	int mux;
+	int ret;
+
+	if (WARN_ON(!crtc || !crtc->state))
+		return;
+
+	if (hdmi->phy)
+		phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width);
+
+	clk_set_rate(hdmi->phyref_clk,
+		     crtc->state->adjusted_mode.crtc_clock * 1000);
+
+	if (hdmi->chip_data->lcdsel_grf_reg < 0)
+		return;
+
+	mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder);
+	if (mux)
+		val = hdmi->chip_data->lcdsel_lit;
+	else
+		val = hdmi->chip_data->lcdsel_big;
+
+	ret = clk_prepare_enable(hdmi->grf_clk);
+	if (ret < 0) {
+		DRM_DEV_ERROR(hdmi->dev, "failed to enable grfclk %d\n", ret);
+		return;
+	}
+
+	ret = regmap_write(hdmi->regmap, hdmi->chip_data->lcdsel_grf_reg, val);
+	if (ret != 0)
+		DRM_DEV_ERROR(hdmi->dev, "Could not write to GRF: %d\n", ret);
+
+	if (hdmi->chip_data->lcdsel_grf_reg == RK3288_GRF_SOC_CON6) {
+		struct rockchip_crtc_state *s =
+				to_rockchip_crtc_state(crtc->state);
+		u32 mode_mask = mux ? RK3288_HDMI_LCDC1_YUV420 :
+					RK3288_HDMI_LCDC0_YUV420;
+
+		if (s->output_mode == ROCKCHIP_OUT_MODE_YUV420)
+			val = HIWORD_UPDATE(mode_mask, mode_mask);
+		else
+			val = HIWORD_UPDATE(0, mode_mask);
+
+		regmap_write(hdmi->regmap, RK3288_GRF_SOC_CON16, val);
+	}
+
+	clk_disable_unprepare(hdmi->grf_clk);
+	DRM_DEV_DEBUG(hdmi->dev, "vop %s output to hdmi\n",
+		      ret ? "LIT" : "BIG");
+}
+
+static void rk3588_set_link_mode(struct rockchip_hdmi *hdmi)
+{
+	int val;
+	bool is_hdmi0;
+
+	if (!hdmi->id)
+		is_hdmi0 = true;
+	else
+		is_hdmi0 = false;
+
+	if (!hdmi->link_cfg.frl_mode) {
+		val = HIWORD_UPDATE(0, RK3588_HDMI21_MASK);
+		if (is_hdmi0)
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON4, val);
+		else
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON7, val);
+
+		val = HIWORD_UPDATE(0, RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK);
+		if (is_hdmi0)
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val);
+		else
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val);
+
+		return;
+	}
+
+	val = HIWORD_UPDATE(RK3588_HDMI21_MASK, RK3588_HDMI21_MASK);
+	if (is_hdmi0)
+		regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON4, val);
+	else
+		regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON7, val);
+
+	if (hdmi->link_cfg.dsc_mode) {
+		val = HIWORD_UPDATE(RK3588_COMPRESS_MODE_MASK | RK3588_COMPRESSED_DATA,
+				    RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK);
+		if (is_hdmi0)
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val);
+		else
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val);
+	} else {
+		val = HIWORD_UPDATE(0, RK3588_COMPRESS_MODE_MASK | RK3588_COLOR_FORMAT_MASK);
+		if (is_hdmi0)
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val);
+		else
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val);
+	}
+}
+
+static void rk3588_set_color_format(struct rockchip_hdmi *hdmi, u64 bus_format,
+				    u32 depth)
+{
+	u32 val = 0;
+
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_RGB888_1X24:
+	case MEDIA_BUS_FMT_RGB101010_1X30:
+		val = HIWORD_UPDATE(0, RK3588_COLOR_FORMAT_MASK);
+		break;
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+		val = HIWORD_UPDATE(RK3588_YUV420, RK3588_COLOR_FORMAT_MASK);
+		break;
+	case MEDIA_BUS_FMT_YUV8_1X24:
+	case MEDIA_BUS_FMT_YUV10_1X30:
+		val = HIWORD_UPDATE(RK3588_YUV444, RK3588_COLOR_FORMAT_MASK);
+		break;
+	default:
+		dev_err(hdmi->dev, "can't set correct color format\n");
+		return;
+	}
+
+	if (hdmi->link_cfg.dsc_mode)
+		val = HIWORD_UPDATE(RK3588_COMPRESSED_DATA, RK3588_COLOR_FORMAT_MASK);
+
+	if (depth == 8)
+		val |= HIWORD_UPDATE(RK3588_8BPC, RK3588_COLOR_DEPTH_MASK);
+	else
+		val |= HIWORD_UPDATE(RK3588_10BPC, RK3588_COLOR_DEPTH_MASK);
+
+	if (!hdmi->id)
+		regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val);
+	else
+		regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val);
+}
+
+static void rk3588_set_grf_cfg(void *data)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+	int color_depth;
+
+	rk3588_set_link_mode(hdmi);
+	color_depth = hdmi_bus_fmt_color_depth(hdmi->bus_format);
+	rk3588_set_color_format(hdmi, hdmi->bus_format, color_depth);
+}
+
+static void
+dw_hdmi_rockchip_select_output(struct drm_connector_state *conn_state,
+			       struct drm_crtc_state *crtc_state,
+			       struct rockchip_hdmi *hdmi,
+			       unsigned int *color_format,
+			       unsigned int *output_mode,
+			       unsigned long *bus_format,
+			       unsigned int *bus_width,
+			       unsigned long *enc_out_encoding,
+			       unsigned int *eotf)
+{
+	struct drm_display_info *info = &conn_state->connector->display_info;
+	struct drm_display_mode *mode = &crtc_state->mode;
+	struct hdr_output_metadata *hdr_metadata;
+	u32 vic = drm_match_cea_mode(mode);
+	unsigned long tmdsclock, pixclock = mode->crtc_clock;
+	unsigned int color_depth;
+	bool support_dc = false;
+	bool sink_is_hdmi = true;
+	u32 max_tmds_clock = info->max_tmds_clock;
+	int output_eotf;
+
+	if (!hdmi->is_hdmi_qp)
+		sink_is_hdmi = dw_hdmi_get_output_whether_hdmi(hdmi->hdmi);
+
+	*color_format = DRM_HDMI_OUTPUT_DEFAULT_RGB;
+
+	switch (hdmi->hdmi_output) {
+	case DRM_HDMI_OUTPUT_YCBCR_HQ:
+		if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+			*color_format = DRM_HDMI_OUTPUT_YCBCR444;
+		else if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+			*color_format = DRM_HDMI_OUTPUT_YCBCR422;
+		else if (conn_state->connector->ycbcr_420_allowed &&
+			 drm_mode_is_420(info, mode) &&
+			 (pixclock >= 594000 && !hdmi->is_hdmi_qp))
+			*color_format = DRM_HDMI_OUTPUT_YCBCR420;
+		break;
+	case DRM_HDMI_OUTPUT_YCBCR_LQ:
+		if (conn_state->connector->ycbcr_420_allowed &&
+		    drm_mode_is_420(info, mode) && pixclock >= 594000)
+			*color_format = DRM_HDMI_OUTPUT_YCBCR420;
+		else if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+			*color_format = DRM_HDMI_OUTPUT_YCBCR422;
+		else if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+			*color_format = DRM_HDMI_OUTPUT_YCBCR444;
+		break;
+	case DRM_HDMI_OUTPUT_YCBCR420:
+		if (conn_state->connector->ycbcr_420_allowed &&
+		    drm_mode_is_420(info, mode) && pixclock >= 594000)
+			*color_format = DRM_HDMI_OUTPUT_YCBCR420;
+		break;
+	case DRM_HDMI_OUTPUT_YCBCR422:
+		if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+			*color_format = DRM_HDMI_OUTPUT_YCBCR422;
+		break;
+	case DRM_HDMI_OUTPUT_YCBCR444:
+		if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+			*color_format = DRM_HDMI_OUTPUT_YCBCR444;
+		break;
+	case DRM_HDMI_OUTPUT_DEFAULT_RGB:
+	default:
+		break;
+	}
+
+	if (*color_format == DRM_HDMI_OUTPUT_DEFAULT_RGB &&
+	    info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30)
+		support_dc = true;
+	if (*color_format == DRM_HDMI_OUTPUT_YCBCR444 &&
+	    info->edid_hdmi_dc_modes &
+	    (DRM_EDID_HDMI_DC_Y444 | DRM_EDID_HDMI_DC_30))
+		support_dc = true;
+	if (*color_format == DRM_HDMI_OUTPUT_YCBCR422)
+		support_dc = true;
+	if (*color_format == DRM_HDMI_OUTPUT_YCBCR420 &&
+	    info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)
+		support_dc = true;
+
+	if (hdmi->colordepth > 8 && support_dc)
+		color_depth = 10;
+	else
+		color_depth = 8;
+
+	if (!sink_is_hdmi) {
+		*color_format = DRM_HDMI_OUTPUT_DEFAULT_RGB;
+		color_depth = 8;
+	}
+
+	*eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR;
+	if (conn_state->hdr_output_metadata) {
+		hdr_metadata = (struct hdr_output_metadata *)
+			conn_state->hdr_output_metadata->data;
+		output_eotf = hdr_metadata->hdmi_metadata_type1.eotf;
+		if (output_eotf > HDMI_EOTF_TRADITIONAL_GAMMA_SDR &&
+		    output_eotf <= HDMI_EOTF_BT_2100_HLG)
+			*eotf = output_eotf;
+	}
+
+	if ((*eotf > HDMI_EOTF_TRADITIONAL_GAMMA_SDR &&
+	     conn_state->connector->hdr_sink_metadata.hdmi_type1.eotf &
+	     BIT(*eotf)) || (hdmi->colorimetry ==
+	     RK_HDMI_COLORIMETRY_BT2020))
+		*enc_out_encoding = V4L2_YCBCR_ENC_BT2020;
+	else if ((vic == 6) || (vic == 7) || (vic == 21) || (vic == 22) ||
+		 (vic == 2) || (vic == 3) || (vic == 17) || (vic == 18))
+		*enc_out_encoding = V4L2_YCBCR_ENC_601;
+	else
+		*enc_out_encoding = V4L2_YCBCR_ENC_709;
+
+	if (*enc_out_encoding == V4L2_YCBCR_ENC_BT2020) {
+		/* BT2020 require color depth at lest 10bit */
+		color_depth = 10;
+		/* We prefer use YCbCr422 to send 10bit */
+		if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+			*color_format = DRM_HDMI_OUTPUT_YCBCR422;
+		if (hdmi->is_hdmi_qp) {
+			if (info->color_formats & DRM_COLOR_FORMAT_YCRCB420) {
+				if (mode->clock >= 340000)
+					*color_format = DRM_HDMI_OUTPUT_YCBCR420;
+				else
+					*color_format = DRM_HDMI_OUTPUT_DEFAULT_RGB;
+			} else {
+				*color_format = DRM_HDMI_OUTPUT_DEFAULT_RGB;
+			}
+		}
+	}
+
+	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+		pixclock *= 2;
+	if ((mode->flags & DRM_MODE_FLAG_3D_MASK) ==
+		DRM_MODE_FLAG_3D_FRAME_PACKING)
+		pixclock *= 2;
+
+	if (*color_format == DRM_HDMI_OUTPUT_YCBCR422 || color_depth == 8)
+		tmdsclock = pixclock;
+	else
+		tmdsclock = pixclock * (color_depth) / 8;
+
+	if (*color_format == DRM_HDMI_OUTPUT_YCBCR420)
+		tmdsclock /= 2;
+
+	/* XXX: max_tmds_clock of some sink is 0, we think it is 340MHz. */
+	if (!max_tmds_clock)
+		max_tmds_clock = 340000;
+
+	max_tmds_clock = min(max_tmds_clock, hdmi->max_tmdsclk);
+
+	if ((tmdsclock > max_tmds_clock) && !hdmi->is_hdmi_qp) {
+		if (max_tmds_clock >= 594000) {
+			color_depth = 8;
+		} else if (max_tmds_clock > 340000) {
+			if (drm_mode_is_420(info, mode) || tmdsclock >= 594000)
+				*color_format = DRM_HDMI_OUTPUT_YCBCR420;
+		} else {
+			color_depth = 8;
+			if (drm_mode_is_420(info, mode) || tmdsclock >= 594000)
+				*color_format = DRM_HDMI_OUTPUT_YCBCR420;
+		}
+	}
+
+	if (mode->clock >= 340000 && hdmi->is_hdmi_qp)
+		*color_format = DRM_HDMI_OUTPUT_YCBCR420;
+
+	if (*color_format == DRM_HDMI_OUTPUT_YCBCR420) {
+		*output_mode = ROCKCHIP_OUT_MODE_YUV420;
+		if (color_depth > 8)
+			*bus_format = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
+		else
+			*bus_format = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
+		*bus_width = color_depth / 2;
+	} else {
+		*output_mode = ROCKCHIP_OUT_MODE_AAAA;
+		if (color_depth > 8) {
+			if (*color_format != DRM_HDMI_OUTPUT_DEFAULT_RGB &&
+			    !hdmi->unsupported_yuv_input)
+				*bus_format = MEDIA_BUS_FMT_YUV10_1X30;
+			else
+				*bus_format = MEDIA_BUS_FMT_RGB101010_1X30;
+		} else {
+			if (*color_format != DRM_HDMI_OUTPUT_DEFAULT_RGB &&
+			    !hdmi->unsupported_yuv_input)
+				*bus_format = MEDIA_BUS_FMT_YUV8_1X24;
+			else
+				*bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+		}
+		if (*color_format == DRM_HDMI_OUTPUT_YCBCR422)
+			*bus_width = 8;
+		else
+			*bus_width = color_depth;
+	}
+
+	hdmi->bus_format = *bus_format;
+
+	if (*color_format == DRM_HDMI_OUTPUT_YCBCR422) {
+		if (color_depth == 12)
+			hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY12_1X24;
+		else if (color_depth == 10)
+			hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY10_1X20;
+		else
+			hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY8_1X16;
+	} else {
+		hdmi->output_bus_format = *bus_format;
+	}
+}
+
+static bool
+dw_hdmi_rockchip_check_color(struct drm_connector_state *conn_state,
+			     struct rockchip_hdmi *hdmi)
+{
+	struct drm_crtc_state *crtc_state = conn_state->crtc->state;
+	unsigned int colorformat;
+	unsigned long bus_format;
+	unsigned long output_bus_format = hdmi->output_bus_format;
+	unsigned long enc_out_encoding = hdmi->enc_out_encoding;
+	unsigned int eotf, bus_width;
+	unsigned int output_mode;
+
+	dw_hdmi_rockchip_select_output(conn_state, crtc_state, hdmi,
+				       &colorformat,
+				       &output_mode, &bus_format, &bus_width,
+				       &hdmi->enc_out_encoding, &eotf);
+
+	if (output_bus_format != hdmi->output_bus_format ||
+	    enc_out_encoding != hdmi->enc_out_encoding)
+		return true;
+	else
+		return false;
+}
+
+static int
+dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
+				      struct drm_crtc_state *crtc_state,
+				      struct drm_connector_state *conn_state)
+{
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
+	unsigned int colorformat, bus_width, tmdsclk;
+	unsigned int output_mode;
+	unsigned long bus_format;
+	int color_depth;
+
+	dw_hdmi_rockchip_select_output(conn_state, crtc_state, hdmi,
+				       &colorformat,
+				       &output_mode, &bus_format, &bus_width,
+				       &hdmi->enc_out_encoding, &s->eotf);
+
+	s->bus_format = bus_format;
+	if (hdmi->is_hdmi_qp) {
+		color_depth = hdmi_bus_fmt_color_depth(bus_format);
+		tmdsclk = hdmi_get_tmdsclock(hdmi, crtc_state->mode.clock);
+		if (hdmi_bus_fmt_is_yuv420(hdmi->output_bus_format))
+			tmdsclk /= 2;
+		hdmi_select_link_config(hdmi, crtc_state, tmdsclk);
+
+		if (hdmi->link_cfg.frl_mode) {
+			gpiod_set_value(hdmi->enable_gpio, 0);
+			/* in the current version, support max 40G frl */
+			if (hdmi->link_cfg.rate_per_lane >= 10) {
+				hdmi->link_cfg.frl_lanes = 4;
+				hdmi->link_cfg.rate_per_lane = 10;
+			}
+			bus_width = hdmi->link_cfg.frl_lanes *
+				hdmi->link_cfg.rate_per_lane * 1000000;
+			/* 10 bit color depth and frl mode */
+			if (color_depth == 10)
+				bus_width |=
+					COLOR_DEPTH_10BIT | HDMI_FRL_MODE;
+			else
+				bus_width |= HDMI_FRL_MODE;
+		} else {
+			gpiod_set_value(hdmi->enable_gpio, 1);
+			bus_width = hdmi_get_tmdsclock(hdmi,
+						       crtc_state->mode.clock * 10);
+			if (hdmi_bus_fmt_is_yuv420(hdmi->output_bus_format))
+				bus_width /= 2;
+
+			if (color_depth == 10)
+				bus_width |= COLOR_DEPTH_10BIT;
+		}
+	}
+
+	hdmi->phy_bus_width = bus_width;
+
+	if (hdmi->phy)
+		phy_set_bus_width(hdmi->phy, bus_width);
+
+	s->output_type = DRM_MODE_CONNECTOR_HDMIA;
+	s->tv_state = &conn_state->tv;
+
+	if (!hdmi->id)
+		s->output_if |= VOP_OUTPUT_IF_HDMI0;
+	else
+		s->output_if |= VOP_OUTPUT_IF_HDMI1;
+
+	s->output_mode = output_mode;
+	hdmi->bus_format = s->bus_format;
+
+	hdmi->mode_changed = crtc_state->mode_changed;
+
+	if (hdmi->enc_out_encoding == V4L2_YCBCR_ENC_BT2020)
+		s->color_space = V4L2_COLORSPACE_BT2020;
+	else if (colorformat == DRM_HDMI_OUTPUT_DEFAULT_RGB)
+		s->color_space = V4L2_COLORSPACE_DEFAULT;
+	else if (hdmi->enc_out_encoding == V4L2_YCBCR_ENC_709)
+		s->color_space = V4L2_COLORSPACE_REC709;
+	else
+		s->color_space = V4L2_COLORSPACE_SMPTE170M;
+
+	return 0;
+}
+
+
+static unsigned long
+dw_hdmi_rockchip_get_input_bus_format(void *data)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	return hdmi->bus_format;
+}
+
+static unsigned long
+dw_hdmi_rockchip_get_output_bus_format(void *data)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	return hdmi->output_bus_format;
+}
+
+static unsigned long
+dw_hdmi_rockchip_get_enc_in_encoding(void *data)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	return hdmi->enc_out_encoding;
+}
+
+static unsigned long
+dw_hdmi_rockchip_get_enc_out_encoding(void *data)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	return hdmi->enc_out_encoding;
+}
+
+static unsigned long
+dw_hdmi_rockchip_get_quant_range(void *data)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	return hdmi->hdmi_quant_range;
+}
+
+static struct drm_property *
+dw_hdmi_rockchip_get_hdr_property(void *data)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	return hdmi->hdr_panel_metadata_property;
+}
+
+static struct drm_property_blob *
+dw_hdmi_rockchip_get_hdr_blob(void *data)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	return hdmi->hdr_panel_blob_ptr;
+}
+
+static bool
+dw_hdmi_rockchip_get_color_changed(void *data)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+	bool ret = false;
+
+	if (hdmi->color_changed)
+		ret = true;
+	hdmi->color_changed = 0;
+
+	return ret;
+}
+
+static int
+dw_hdmi_rockchip_get_yuv422_format(struct drm_connector *connector,
+				   struct edid *edid)
+{
+	if (!connector || !edid)
+		return -EINVAL;
+
+	return rockchip_drm_get_yuv422_format(connector, edid);
+}
+
+static int
+dw_hdmi_rockchip_get_edid_dsc_info(void *data, struct edid *edid)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	if (!edid)
+		return -EINVAL;
+
+	return rockchip_drm_parse_cea_ext(&hdmi->dsc_cap,
+					  &hdmi->max_frl_rate_per_lane,
+					  &hdmi->max_lanes, edid);
+}
+
+static int
+dw_hdmi_rockchip_get_next_hdr_data(void *data, struct edid *edid,
+				   struct drm_connector *connector)
+{
+	int ret;
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+	struct next_hdr_sink_data *sink_data = &hdmi->next_hdr_data;
+	size_t size = sizeof(*sink_data);
+	struct drm_property *property = hdmi->next_hdr_sink_data_property;
+	struct drm_property_blob *blob = hdmi->hdr_panel_blob_ptr;
+
+	if (!edid)
+		return -EINVAL;
+
+	rockchip_drm_parse_next_hdr(sink_data, edid);
+
+	ret = drm_property_replace_global_blob(connector->dev, &blob, size, sink_data,
+					       &connector->base, property);
+
+	return ret;
+};
+
+static
+struct dw_hdmi_link_config *dw_hdmi_rockchip_get_link_cfg(void *data)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	return &hdmi->link_cfg;
+}
+
+static const struct drm_prop_enum_list color_depth_enum_list[] = {
+	{ 0, "Automatic" }, /* Prefer highest color depth */
+	{ 8, "24bit" },
+	{ 10, "30bit" },
+};
+
+static const struct drm_prop_enum_list drm_hdmi_output_enum_list[] = {
+	{ DRM_HDMI_OUTPUT_DEFAULT_RGB, "output_rgb" },
+	{ DRM_HDMI_OUTPUT_YCBCR444, "output_ycbcr444" },
+	{ DRM_HDMI_OUTPUT_YCBCR422, "output_ycbcr422" },
+	{ DRM_HDMI_OUTPUT_YCBCR420, "output_ycbcr420" },
+	{ DRM_HDMI_OUTPUT_YCBCR_HQ, "output_ycbcr_high_subsampling" },
+	{ DRM_HDMI_OUTPUT_YCBCR_LQ, "output_ycbcr_low_subsampling" },
+	{ DRM_HDMI_OUTPUT_INVALID, "invalid_output" },
+};
+
+static const struct drm_prop_enum_list quant_range_enum_list[] = {
+	{ HDMI_QUANTIZATION_RANGE_DEFAULT, "default" },
+	{ HDMI_QUANTIZATION_RANGE_LIMITED, "limit" },
+	{ HDMI_QUANTIZATION_RANGE_FULL, "full" },
+};
+
+static const struct drm_prop_enum_list colorimetry_enum_list[] = {
+	{ HDMI_COLORIMETRY_NONE, "None" },
+	{ RK_HDMI_COLORIMETRY_BT2020, "ITU_2020" },
+};
+
+static const struct drm_prop_enum_list output_hdmi_dvi_enum_list[] = {
+	{ 0, "auto" },
+	{ 1, "force_hdmi" },
+	{ 2, "force_dvi" },
+};
+
+static const struct drm_prop_enum_list output_type_cap_list[] = {
+	{ 0, "DVI" },
+	{ 1, "HDMI" },
+};
+
+static void
+dw_hdmi_rockchip_attach_properties(struct drm_connector *connector,
+				   unsigned int color, int version,
+				   void *data)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+	struct drm_property *prop;
+	struct rockchip_drm_private *private = connector->dev->dev_private;
+
+	switch (color) {
+	case MEDIA_BUS_FMT_RGB101010_1X30:
+		hdmi->hdmi_output = DRM_HDMI_OUTPUT_DEFAULT_RGB;
+		hdmi->colordepth = 10;
+		break;
+	case MEDIA_BUS_FMT_YUV8_1X24:
+		hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR444;
+		hdmi->colordepth = 8;
+		break;
+	case MEDIA_BUS_FMT_YUV10_1X30:
+		hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR444;
+		hdmi->colordepth = 10;
+		break;
+	case MEDIA_BUS_FMT_UYVY10_1X20:
+		hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR422;
+		hdmi->colordepth = 10;
+		break;
+	case MEDIA_BUS_FMT_UYVY8_1X16:
+		hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR422;
+		hdmi->colordepth = 8;
+		break;
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+		hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR420;
+		hdmi->colordepth = 8;
+		break;
+	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+		hdmi->hdmi_output = DRM_HDMI_OUTPUT_YCBCR420;
+		hdmi->colordepth = 10;
+		break;
+	default:
+		hdmi->hdmi_output = DRM_HDMI_OUTPUT_DEFAULT_RGB;
+		hdmi->colordepth = 8;
+	}
+
+	hdmi->bus_format = color;
+
+	if (hdmi->hdmi_output == DRM_HDMI_OUTPUT_YCBCR422) {
+		if (hdmi->colordepth == 12)
+			hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY12_1X24;
+		else if (hdmi->colordepth == 10)
+			hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY10_1X20;
+		else
+			hdmi->output_bus_format = MEDIA_BUS_FMT_UYVY8_1X16;
+	} else {
+		hdmi->output_bus_format = hdmi->bus_format;
+	}
+
+	/* RK3368 does not support deep color mode */
+	if (!hdmi->color_depth_property && !hdmi->unsupported_deep_color) {
+		prop = drm_property_create_enum(connector->dev, 0,
+						"hdmi_output_depth",
+						color_depth_enum_list,
+						ARRAY_SIZE(color_depth_enum_list));
+		if (prop) {
+			hdmi->color_depth_property = prop;
+			drm_object_attach_property(&connector->base, prop, 0);
+		}
+	}
+
+	prop = drm_property_create_enum(connector->dev, 0, "hdmi_output_format",
+					drm_hdmi_output_enum_list,
+					ARRAY_SIZE(drm_hdmi_output_enum_list));
+	if (prop) {
+		hdmi->hdmi_output_property = prop;
+		drm_object_attach_property(&connector->base, prop, 0);
+	}
+
+	prop = drm_property_create_enum(connector->dev, 0,
+					"hdmi_output_colorimetry",
+					colorimetry_enum_list,
+					ARRAY_SIZE(colorimetry_enum_list));
+	if (prop) {
+		hdmi->colorimetry_property = prop;
+		drm_object_attach_property(&connector->base, prop, 0);
+	}
+
+	prop = drm_property_create_range(connector->dev, DRM_MODE_PROP_IMMUTABLE,
+					 "hdmi_color_depth_capacity",
+					 0, 0xff);
+	if (prop) {
+		hdmi->colordepth_capacity = prop;
+		drm_object_attach_property(&connector->base, prop, 0);
+	}
+
+	prop = drm_property_create_range(connector->dev, DRM_MODE_PROP_IMMUTABLE,
+					 "hdmi_output_mode_capacity",
+					 0, 0xf);
+	if (prop) {
+		hdmi->outputmode_capacity = prop;
+		drm_object_attach_property(&connector->base, prop, 0);
+	}
+
+	prop = drm_property_create(connector->dev,
+				   DRM_MODE_PROP_BLOB |
+				   DRM_MODE_PROP_IMMUTABLE,
+				   "HDR_PANEL_METADATA", 0);
+	if (prop) {
+		hdmi->hdr_panel_metadata_property = prop;
+		drm_object_attach_property(&connector->base, prop, 0);
+	}
+
+	prop = drm_property_create(connector->dev,
+				   DRM_MODE_PROP_BLOB |
+				   DRM_MODE_PROP_IMMUTABLE,
+				   "NEXT_HDR_SINK_DATA", 0);
+	if (prop) {
+		hdmi->next_hdr_sink_data_property = prop;
+		drm_object_attach_property(&connector->base, prop, 0);
+	}
+
+	if (!hdmi->is_hdmi_qp) {
+		prop = drm_property_create_enum(connector->dev, 0,
+						"output_hdmi_dvi",
+						output_hdmi_dvi_enum_list,
+						ARRAY_SIZE(output_hdmi_dvi_enum_list));
+		if (prop) {
+			hdmi->output_hdmi_dvi = prop;
+			drm_object_attach_property(&connector->base, prop, 0);
+		}
+
+		prop = drm_property_create_enum(connector->dev, DRM_MODE_PROP_IMMUTABLE,
+						 "output_type_capacity",
+						 output_type_cap_list,
+						 ARRAY_SIZE(output_type_cap_list));
+		if (prop) {
+			hdmi->output_type_capacity = prop;
+			drm_object_attach_property(&connector->base, prop, 0);
+		}
+
+		prop = drm_property_create_enum(connector->dev, 0,
+						"hdmi_quant_range",
+						quant_range_enum_list,
+						ARRAY_SIZE(quant_range_enum_list));
+		if (prop) {
+			hdmi->quant_range = prop;
+			drm_object_attach_property(&connector->base, prop, 0);
+		}
+	}
+
+	prop = connector->dev->mode_config.hdr_output_metadata_property;
+	if (version >= 0x211a || hdmi->is_hdmi_qp)
+		drm_object_attach_property(&connector->base, prop, 0);
+	drm_object_attach_property(&connector->base, private->connector_id_prop, 0);
+}
+
+static void
+dw_hdmi_rockchip_destroy_properties(struct drm_connector *connector,
+				    void *data)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	if (hdmi->color_depth_property) {
+		drm_property_destroy(connector->dev,
+				     hdmi->color_depth_property);
+		hdmi->color_depth_property = NULL;
+	}
+
+	if (hdmi->hdmi_output_property) {
+		drm_property_destroy(connector->dev,
+				     hdmi->hdmi_output_property);
+		hdmi->hdmi_output_property = NULL;
+	}
+
+	if (hdmi->colordepth_capacity) {
+		drm_property_destroy(connector->dev,
+				     hdmi->colordepth_capacity);
+		hdmi->colordepth_capacity = NULL;
 	}
-};
 
-static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
-	/*      pixelclk    bpp8    bpp10   bpp12 */
-	{
-		40000000,  { 0x0018, 0x0018, 0x0018 },
-	}, {
-		65000000,  { 0x0028, 0x0028, 0x0028 },
-	}, {
-		66000000,  { 0x0038, 0x0038, 0x0038 },
-	}, {
-		74250000,  { 0x0028, 0x0038, 0x0038 },
-	}, {
-		83500000,  { 0x0028, 0x0038, 0x0038 },
-	}, {
-		146250000, { 0x0038, 0x0038, 0x0038 },
-	}, {
-		148500000, { 0x0000, 0x0038, 0x0038 },
-	}, {
-		~0UL,      { 0x0000, 0x0000, 0x0000},
+	if (hdmi->outputmode_capacity) {
+		drm_property_destroy(connector->dev,
+				     hdmi->outputmode_capacity);
+		hdmi->outputmode_capacity = NULL;
 	}
-};
 
-static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
-	/*pixelclk   symbol   term   vlev*/
-	{ 74250000,  0x8009, 0x0004, 0x0272},
-	{ 148500000, 0x802b, 0x0004, 0x028d},
-	{ 297000000, 0x8039, 0x0005, 0x028d},
-	{ ~0UL,	     0x0000, 0x0000, 0x0000}
-};
+	if (hdmi->quant_range) {
+		drm_property_destroy(connector->dev,
+				     hdmi->quant_range);
+		hdmi->quant_range = NULL;
+	}
 
-static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
-{
-	struct device_node *np = hdmi->dev->of_node;
+	if (hdmi->colorimetry_property) {
+		drm_property_destroy(connector->dev,
+				     hdmi->colorimetry_property);
+		hdmi->colordepth_capacity = NULL;
+	}
 
-	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
-	if (IS_ERR(hdmi->regmap)) {
-		DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,grf\n");
-		return PTR_ERR(hdmi->regmap);
+	if (hdmi->hdr_panel_metadata_property) {
+		drm_property_destroy(connector->dev,
+				     hdmi->hdr_panel_metadata_property);
+		hdmi->hdr_panel_metadata_property = NULL;
 	}
 
-	hdmi->vpll_clk = devm_clk_get(hdmi->dev, "vpll");
-	if (PTR_ERR(hdmi->vpll_clk) == -ENOENT) {
-		hdmi->vpll_clk = NULL;
-	} else if (PTR_ERR(hdmi->vpll_clk) == -EPROBE_DEFER) {
-		return -EPROBE_DEFER;
-	} else if (IS_ERR(hdmi->vpll_clk)) {
-		DRM_DEV_ERROR(hdmi->dev, "failed to get grf clock\n");
-		return PTR_ERR(hdmi->vpll_clk);
+	if (hdmi->next_hdr_sink_data_property) {
+		drm_property_destroy(connector->dev,
+				     hdmi->next_hdr_sink_data_property);
+		hdmi->next_hdr_sink_data_property = NULL;
 	}
 
-	hdmi->grf_clk = devm_clk_get(hdmi->dev, "grf");
-	if (PTR_ERR(hdmi->grf_clk) == -ENOENT) {
-		hdmi->grf_clk = NULL;
-	} else if (PTR_ERR(hdmi->grf_clk) == -EPROBE_DEFER) {
-		return -EPROBE_DEFER;
-	} else if (IS_ERR(hdmi->grf_clk)) {
-		DRM_DEV_ERROR(hdmi->dev, "failed to get grf clock\n");
-		return PTR_ERR(hdmi->grf_clk);
+	if (hdmi->output_hdmi_dvi) {
+		drm_property_destroy(connector->dev,
+				     hdmi->output_hdmi_dvi);
+		hdmi->output_hdmi_dvi = NULL;
 	}
 
-	return 0;
+	if (hdmi->output_type_capacity) {
+		drm_property_destroy(connector->dev,
+				     hdmi->output_type_capacity);
+		hdmi->output_type_capacity = NULL;
+	}
 }
 
-static enum drm_mode_status
-dw_hdmi_rockchip_mode_valid(struct dw_hdmi *hdmi, void *data,
-			    const struct drm_display_info *info,
-			    const struct drm_display_mode *mode)
+static int
+dw_hdmi_rockchip_set_property(struct drm_connector *connector,
+			      struct drm_connector_state *state,
+			      struct drm_property *property,
+			      u64 val,
+			      void *data)
 {
-	const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg;
-	int pclk = mode->clock * 1000;
-	bool valid = false;
-	int i;
-
-	for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) {
-		if (pclk == mpll_cfg[i].mpixelclock) {
-			valid = true;
-			break;
-		}
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+	struct drm_mode_config *config = &connector->dev->mode_config;
+
+	if (property == hdmi->color_depth_property) {
+		hdmi->colordepth = val;
+		/* If hdmi is disconnected, state->crtc is null */
+		if (!state->crtc)
+			return 0;
+		if (dw_hdmi_rockchip_check_color(state, hdmi))
+			hdmi->color_changed++;
+		return 0;
+	} else if (property == hdmi->hdmi_output_property) {
+		hdmi->hdmi_output = val;
+		if (!state->crtc)
+			return 0;
+		if (dw_hdmi_rockchip_check_color(state, hdmi))
+			hdmi->color_changed++;
+		return 0;
+	} else if (property == hdmi->quant_range) {
+		u64 quant_range = hdmi->hdmi_quant_range;
+
+		hdmi->hdmi_quant_range = val;
+		if (quant_range != hdmi->hdmi_quant_range)
+			dw_hdmi_set_quant_range(hdmi->hdmi);
+		return 0;
+	} else if (property == config->hdr_output_metadata_property) {
+		return 0;
+	} else if (property == hdmi->colorimetry_property) {
+		hdmi->colorimetry = val;
+		return 0;
+	} else if (property == hdmi->output_hdmi_dvi) {
+		if (hdmi->force_output != val)
+			hdmi->color_changed++;
+		hdmi->force_output = val;
+		dw_hdmi_set_output_type(hdmi->hdmi, val);
+		return 0;
 	}
 
-	return (valid) ? MODE_OK : MODE_BAD;
-}
+	DRM_ERROR("Unknown property [PROP:%d:%s]\n",
+		  property->base.id, property->name);
 
-static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder)
-{
+	return -EINVAL;
 }
 
-static bool
-dw_hdmi_rockchip_encoder_mode_fixup(struct drm_encoder *encoder,
-				    const struct drm_display_mode *mode,
-				    struct drm_display_mode *adj_mode)
+static int
+dw_hdmi_rockchip_get_property(struct drm_connector *connector,
+			      const struct drm_connector_state *state,
+			      struct drm_property *property,
+			      u64 *val,
+			      void *data)
 {
-	return true;
-}
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+	struct drm_display_info *info = &connector->display_info;
+	struct drm_mode_config *config = &connector->dev->mode_config;
+	struct rockchip_drm_private *private = connector->dev->dev_private;
+
+	if (property == hdmi->color_depth_property) {
+		*val = hdmi->colordepth;
+		return 0;
+	} else if (property == hdmi->hdmi_output_property) {
+		*val = hdmi->hdmi_output;
+		return 0;
+	} else if (property == hdmi->colordepth_capacity) {
+		*val = BIT(ROCKCHIP_HDMI_DEPTH_8);
+		/* RK3368 only support 8bit */
+		if (hdmi->unsupported_deep_color)
+			return 0;
+		if (info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30)
+			*val |= BIT(ROCKCHIP_HDMI_DEPTH_10);
+		if (info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_36)
+			*val |= BIT(ROCKCHIP_HDMI_DEPTH_12);
+		if (info->edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_48)
+			*val |= BIT(ROCKCHIP_HDMI_DEPTH_16);
+		if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)
+			*val |= BIT(ROCKCHIP_HDMI_DEPTH_420_10);
+		if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36)
+			*val |= BIT(ROCKCHIP_HDMI_DEPTH_420_12);
+		if (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48)
+			*val |= BIT(ROCKCHIP_HDMI_DEPTH_420_16);
+		return 0;
+	} else if (property == hdmi->outputmode_capacity) {
+		*val = BIT(DRM_HDMI_OUTPUT_DEFAULT_RGB);
+		if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+			*val |= BIT(DRM_HDMI_OUTPUT_YCBCR444);
+		if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+			*val |= BIT(DRM_HDMI_OUTPUT_YCBCR422);
+		if (connector->ycbcr_420_allowed &&
+		    info->color_formats & DRM_COLOR_FORMAT_YCRCB420)
+			*val |= BIT(DRM_HDMI_OUTPUT_YCBCR420);
+		return 0;
+	} else if (property == hdmi->quant_range) {
+		*val = hdmi->hdmi_quant_range;
+		return 0;
+	} else if (property == config->hdr_output_metadata_property) {
+		*val = state->hdr_output_metadata ?
+			state->hdr_output_metadata->base.id : 0;
+		return 0;
+	} else if (property == hdmi->colorimetry_property) {
+		*val = hdmi->colorimetry;
+		return 0;
+	} else if (property == private->connector_id_prop) {
+		*val = hdmi->id;
+		return 0;
+	} else if (property == hdmi->output_hdmi_dvi) {
+		*val = hdmi->force_output;
+		return 0;
+	} else if (property == hdmi->output_type_capacity) {
+		*val = dw_hdmi_get_output_type_cap(hdmi->hdmi);
+		return 0;
+	}
 
-static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
-					      struct drm_display_mode *mode,
-					      struct drm_display_mode *adj_mode)
-{
-	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
+	DRM_ERROR("Unknown property [PROP:%d:%s]\n",
+		  property->base.id, property->name);
 
-	clk_set_rate(hdmi->vpll_clk, adj_mode->clock * 1000);
+	return -EINVAL;
 }
 
-static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
+static const struct dw_hdmi_property_ops dw_hdmi_rockchip_property_ops = {
+	.attach_properties	= dw_hdmi_rockchip_attach_properties,
+	.destroy_properties	= dw_hdmi_rockchip_destroy_properties,
+	.set_property		= dw_hdmi_rockchip_set_property,
+	.get_property		= dw_hdmi_rockchip_get_property,
+};
+
+static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
+					      struct drm_display_mode *mode,
+					      struct drm_display_mode *adj)
 {
 	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
-	u32 val;
-	int ret;
-
-	if (hdmi->chip_data->lcdsel_grf_reg < 0)
+	struct drm_crtc *crtc;
+	struct rockchip_crtc_state *s;
+	if (!encoder->crtc)
 		return;
-
-	ret = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder);
-	if (ret)
-		val = hdmi->chip_data->lcdsel_lit;
-	else
-		val = hdmi->chip_data->lcdsel_big;
-
-	ret = clk_prepare_enable(hdmi->grf_clk);
-	if (ret < 0) {
-		DRM_DEV_ERROR(hdmi->dev, "failed to enable grfclk %d\n", ret);
+	crtc = encoder->crtc;
+	if (!crtc->state)
 		return;
-	}
-
-	ret = regmap_write(hdmi->regmap, hdmi->chip_data->lcdsel_grf_reg, val);
-	if (ret != 0)
-		DRM_DEV_ERROR(hdmi->dev, "Could not write to GRF: %d\n", ret);
-
-	clk_disable_unprepare(hdmi->grf_clk);
-	DRM_DEV_DEBUG(hdmi->dev, "vop %s output to hdmi\n",
-		      ret ? "LIT" : "BIG");
-}
+	s = to_rockchip_crtc_state(crtc->state);
 
-static int
-dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
-				      struct drm_crtc_state *crtc_state,
-				      struct drm_connector_state *conn_state)
-{
-	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+	if (!s)
+		return;
+	if (hdmi->is_hdmi_qp) {
+		s->dsc_enable = 0;
+		if (hdmi->link_cfg.dsc_mode)
+			dw_hdmi_qp_dsc_configure(hdmi, s, crtc->state);
 
-	s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
-	s->output_type = DRM_MODE_CONNECTOR_HDMIA;
+		phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width);
+	}
 
-	return 0;
+	clk_set_rate(hdmi->phyref_clk, adj->crtc_clock * 1000);
 }
 
 static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = {
-	.mode_fixup = dw_hdmi_rockchip_encoder_mode_fixup,
-	.mode_set   = dw_hdmi_rockchip_encoder_mode_set,
 	.enable     = dw_hdmi_rockchip_encoder_enable,
 	.disable    = dw_hdmi_rockchip_encoder_disable,
 	.atomic_check = dw_hdmi_rockchip_encoder_atomic_check,
+	.mode_set = dw_hdmi_rockchip_encoder_mode_set,
 };
 
-static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data,
-					const struct drm_display_info *display,
-					const struct drm_display_mode *mode)
+static void
+dw_hdmi_rockchip_genphy_disable(struct dw_hdmi *dw_hdmi, void *data)
 {
 	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
 
-	return phy_power_on(hdmi->phy);
+	while (hdmi->phy->power_count > 0)
+		phy_power_off(hdmi->phy);
 }
 
-static void dw_hdmi_rockchip_genphy_disable(struct dw_hdmi *dw_hdmi, void *data)
+static int
+dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data,
+			     const struct drm_display_info *display,
+			     const struct drm_display_mode *mode)
 {
 	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
 
-	phy_power_off(hdmi->phy);
+	dw_hdmi_rockchip_genphy_disable(dw_hdmi, data);
+	dw_hdmi_set_high_tmds_clock_ratio(dw_hdmi, display);
+	return phy_power_on(hdmi->phy);
 }
 
 static void dw_hdmi_rk3228_setup_hpd(struct dw_hdmi *dw_hdmi, void *data)
@@ -391,6 +2609,90 @@ static void dw_hdmi_rk3328_setup_hpd(struct dw_hdmi *dw_hdmi, void *data)
 			      RK3328_HDMI_HPD_IOE));
 }
 
+static void dw_hdmi_qp_rockchip_phy_disable(struct dw_hdmi_qp *dw_hdmi,
+					    void *data)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	while (hdmi->phy->power_count > 0)
+		phy_power_off(hdmi->phy);
+}
+
+static int dw_hdmi_qp_rockchip_genphy_init(struct dw_hdmi_qp *dw_hdmi, void *data,
+					   struct drm_display_mode *mode)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	dw_hdmi_qp_rockchip_phy_disable(dw_hdmi, data);
+
+	return phy_power_on(hdmi->phy);
+}
+
+static enum drm_connector_status
+dw_hdmi_rk3588_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
+{
+	u32 val;
+	int ret;
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &val);
+
+	if (!hdmi->id) {
+		if (val & RK3588_HDMI0_LEVEL_INT) {
+			hdmi->hpd_stat = true;
+			ret = connector_status_connected;
+		} else {
+			hdmi->hpd_stat = false;
+			ret = connector_status_disconnected;
+		}
+	} else {
+		if (val & RK3588_HDMI1_LEVEL_INT) {
+			hdmi->hpd_stat = true;
+			ret = connector_status_connected;
+		} else {
+			hdmi->hpd_stat = false;
+			ret = connector_status_disconnected;
+		}
+	}
+
+	return ret;
+}
+
+static void dw_hdmi_rk3588_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+	u32 val;
+
+	if (!hdmi->id) {
+		val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
+				    RK3588_HDMI0_HPD_INT_CLR) |
+		      HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK);
+	} else {
+		val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR,
+				    RK3588_HDMI1_HPD_INT_CLR) |
+		      HIWORD_UPDATE(0, RK3588_HDMI1_HPD_INT_MSK);
+	}
+
+	regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+}
+
+static void dw_hdmi_rk3588_phy_set_mode(struct dw_hdmi_qp *dw_hdmi, void *data,
+					u32 mode_mask, bool enable)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	if (!hdmi->phy)
+		return;
+
+	/* set phy earc/frl mode */
+	if (enable)
+		hdmi->phy_bus_width |= mode_mask;
+	else
+		hdmi->phy_bus_width &= ~mode_mask;
+
+	phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width);
+}
+
 static const struct dw_hdmi_phy_ops rk3228_hdmi_phy_ops = {
 	.init		= dw_hdmi_rockchip_genphy_init,
 	.disable	= dw_hdmi_rockchip_genphy_disable,
@@ -412,6 +2714,8 @@ static const struct dw_hdmi_plat_data rk3228_hdmi_drv_data = {
 	.phy_ops = &rk3228_hdmi_phy_ops,
 	.phy_name = "inno_dw_hdmi_phy2",
 	.phy_force_vendor = true,
+	.max_tmdsclk = 371250,
+	.ycbcr_420_allowed = true,
 };
 
 static struct rockchip_hdmi_chip_data rk3288_chip_data = {
@@ -423,9 +2727,13 @@ static struct rockchip_hdmi_chip_data rk3288_chip_data = {
 static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = {
 	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg   = rockchip_mpll_cfg,
+	.mpll_cfg_420 = rockchip_rk3288w_mpll_cfg_420,
 	.cur_ctr    = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
 	.phy_data = &rk3288_chip_data,
+	.tmds_n_table = rockchip_werid_tmds_n_table,
+	.unsupported_yuv_input = true,
+	.ycbcr_420_allowed = true,
 };
 
 static const struct dw_hdmi_phy_ops rk3328_hdmi_phy_ops = {
@@ -450,6 +2758,24 @@ static const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = {
 	.phy_name = "inno_dw_hdmi_phy2",
 	.phy_force_vendor = true,
 	.use_drm_infoframe = true,
+	.max_tmdsclk = 371250,
+	.ycbcr_420_allowed = true,
+};
+
+static struct rockchip_hdmi_chip_data rk3368_chip_data = {
+	.lcdsel_grf_reg = -1,
+};
+
+static const struct dw_hdmi_plat_data rk3368_hdmi_drv_data = {
+	.mode_valid = dw_hdmi_rockchip_mode_valid,
+	.mpll_cfg   = rockchip_mpll_cfg,
+	.mpll_cfg_420 = rockchip_mpll_cfg_420,
+	.cur_ctr    = rockchip_cur_ctr,
+	.phy_config = rockchip_phy_config,
+	.phy_data = &rk3368_chip_data,
+	.unsupported_deep_color = true,
+	.max_tmdsclk = 340000,
+	.ycbcr_420_allowed = true,
 };
 
 static struct rockchip_hdmi_chip_data rk3399_chip_data = {
@@ -461,10 +2787,51 @@ static struct rockchip_hdmi_chip_data rk3399_chip_data = {
 static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
 	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg   = rockchip_mpll_cfg,
+	.mpll_cfg_420 = rockchip_mpll_cfg_420,
 	.cur_ctr    = rockchip_cur_ctr,
 	.phy_config = rockchip_phy_config,
 	.phy_data = &rk3399_chip_data,
 	.use_drm_infoframe = true,
+	.ycbcr_420_allowed = true,
+};
+
+static struct rockchip_hdmi_chip_data rk3568_chip_data = {
+	.lcdsel_grf_reg = -1,
+	.ddc_en_reg = RK3568_GRF_VO_CON1,
+};
+
+static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = {
+	.mode_valid = dw_hdmi_rockchip_mode_valid,
+	.mpll_cfg   = rockchip_mpll_cfg,
+	.mpll_cfg_420 = rockchip_mpll_cfg_420,
+	.cur_ctr    = rockchip_cur_ctr,
+	.phy_config = rockchip_phy_config,
+	.phy_data = &rk3568_chip_data,
+	.ycbcr_420_allowed = true,
+	.use_drm_infoframe = true,
+};
+
+static const struct dw_hdmi_qp_phy_ops rk3588_hdmi_phy_ops = {
+	.init		= dw_hdmi_qp_rockchip_genphy_init,
+	.disable	= dw_hdmi_qp_rockchip_phy_disable,
+	.read_hpd	= dw_hdmi_rk3588_read_hpd,
+	.setup_hpd	= dw_hdmi_rk3588_setup_hpd,
+	.set_mode       = dw_hdmi_rk3588_phy_set_mode,
+};
+
+struct rockchip_hdmi_chip_data rk3588_hdmi_chip_data = {
+	.lcdsel_grf_reg = -1,
+	.ddc_en_reg = RK3588_GRF_VO1_CON3,
+};
+
+static const struct dw_hdmi_plat_data rk3588_hdmi_drv_data = {
+	.phy_data = &rk3588_hdmi_chip_data,
+	.qp_phy_ops = &rk3588_hdmi_phy_ops,
+	.phy_name = "samsung_hdptx_phy",
+	.phy_force_vendor = true,
+	.ycbcr_420_allowed = true,
+	.is_hdmi_qp = true,
+	.use_drm_infoframe = true,
 };
 
 static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
@@ -477,9 +2844,19 @@ static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
 	{ .compatible = "rockchip,rk3328-dw-hdmi",
 	  .data = &rk3328_hdmi_drv_data
 	},
+	{
+	 .compatible = "rockchip,rk3368-dw-hdmi",
+	 .data = &rk3368_hdmi_drv_data
+	},
 	{ .compatible = "rockchip,rk3399-dw-hdmi",
 	  .data = &rk3399_hdmi_drv_data
 	},
+	{ .compatible = "rockchip,rk3568-dw-hdmi",
+	  .data = &rk3568_hdmi_drv_data
+	},
+	{ .compatible = "rockchip,rk3588-dw-hdmi",
+	  .data = &rk3588_hdmi_drv_data
+	},
 	{},
 };
 MODULE_DEVICE_TABLE(of, dw_hdmi_rockchip_dt_ids);
@@ -493,7 +2870,8 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	struct drm_device *drm = data;
 	struct drm_encoder *encoder;
 	struct rockchip_hdmi *hdmi;
-	int ret;
+	int ret, id;
+	u32 val;
 
 	if (!pdev->dev.of_node)
 		return -ENODEV;
@@ -508,12 +2886,44 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	if (!plat_data)
 		return -ENOMEM;
 
+	id = of_alias_get_id(dev->of_node, "hdmi");
+	if (id < 0)
+		id = 0;
+	hdmi->id = id;
 	hdmi->dev = &pdev->dev;
 	hdmi->chip_data = plat_data->phy_data;
+
 	plat_data->phy_data = hdmi;
+	plat_data->get_input_bus_format =
+		dw_hdmi_rockchip_get_input_bus_format;
+	plat_data->get_output_bus_format =
+		dw_hdmi_rockchip_get_output_bus_format;
+	plat_data->get_enc_in_encoding =
+		dw_hdmi_rockchip_get_enc_in_encoding;
+	plat_data->get_enc_out_encoding =
+		dw_hdmi_rockchip_get_enc_out_encoding;
+	plat_data->get_quant_range =
+		dw_hdmi_rockchip_get_quant_range;
+	plat_data->get_hdr_property =
+		dw_hdmi_rockchip_get_hdr_property;
+	plat_data->get_hdr_blob =
+		dw_hdmi_rockchip_get_hdr_blob;
+	plat_data->get_color_changed =
+		dw_hdmi_rockchip_get_color_changed;
+	plat_data->get_yuv422_format =
+		dw_hdmi_rockchip_get_yuv422_format;
+	plat_data->get_edid_dsc_info =
+		dw_hdmi_rockchip_get_edid_dsc_info;
+	plat_data->get_next_hdr_data =
+		dw_hdmi_rockchip_get_next_hdr_data;
+	plat_data->get_link_cfg = dw_hdmi_rockchip_get_link_cfg;
+	plat_data->set_grf_cfg = rk3588_set_grf_cfg;
+
+	plat_data->property_ops = &dw_hdmi_rockchip_property_ops;
+
 	encoder = &hdmi->encoder;
 
-	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+	encoder->possible_crtcs = rockchip_drm_of_find_possible_crtcs(drm, dev->of_node);
 	/*
 	 * If we failed to find the CRTC(s) which this encoder is
 	 * supposed to be connected to, it's because the CRTC has
@@ -523,25 +2933,150 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	if (encoder->possible_crtcs == 0)
 		return -EPROBE_DEFER;
 
+	if (!plat_data->max_tmdsclk)
+		hdmi->max_tmdsclk = 594000;
+	else
+		hdmi->max_tmdsclk = plat_data->max_tmdsclk;
+
+	hdmi->is_hdmi_qp = plat_data->is_hdmi_qp;
+
+	hdmi->unsupported_yuv_input = plat_data->unsupported_yuv_input;
+	hdmi->unsupported_deep_color = plat_data->unsupported_deep_color;
+
 	ret = rockchip_hdmi_parse_dt(hdmi);
 	if (ret) {
 		DRM_DEV_ERROR(hdmi->dev, "Unable to parse OF data\n");
 		return ret;
 	}
 
-	ret = clk_prepare_enable(hdmi->vpll_clk);
+	ret = clk_prepare_enable(hdmi->aud_clk);
+	if (ret) {
+		dev_err(hdmi->dev, "Failed to enable HDMI aud_clk: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(hdmi->hpd_clk);
+	if (ret) {
+		dev_err(hdmi->dev, "Failed to enable HDMI hpd_clk: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(hdmi->hclk_vo1);
+	if (ret) {
+		dev_err(hdmi->dev, "Failed to enable HDMI hclk_vo1: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(hdmi->earc_clk);
+	if (ret) {
+		dev_err(hdmi->dev, "Failed to enable HDMI earc_clk: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(hdmi->hdmitx_ref);
+	if (ret) {
+		dev_err(hdmi->dev, "Failed to enable HDMI hdmitx_ref: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(hdmi->pclk);
+	if (ret) {
+		dev_err(hdmi->dev, "Failed to enable HDMI pclk: %d\n", ret);
+		return ret;
+	}
+
+	if (hdmi->chip_data->ddc_en_reg == RK3568_GRF_VO_CON1) {
+		regmap_write(hdmi->regmap, RK3568_GRF_VO_CON1,
+			     HIWORD_UPDATE(RK3568_HDMI_SDAIN_MSK |
+					   RK3568_HDMI_SCLIN_MSK,
+					   RK3568_HDMI_SDAIN_MSK |
+					   RK3568_HDMI_SCLIN_MSK));
+	}
+
+	if (hdmi->is_hdmi_qp) {
+		if (!hdmi->id) {
+			val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) |
+			      HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) |
+			      HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) |
+			      HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK);
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val);
+
+			val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
+					    RK3588_SET_HPD_PATH_MASK);
+			regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val);
+
+			val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL,
+					    RK3588_HDMI0_GRANT_SEL);
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val);
+		} else {
+			val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) |
+			      HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) |
+			      HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) |
+			      HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK);
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val);
+
+			val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
+					    RK3588_SET_HPD_PATH_MASK);
+			regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val);
+
+			val = HIWORD_UPDATE(RK3588_HDMI1_GRANT_SEL,
+					    RK3588_HDMI1_GRANT_SEL);
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val);
+		}
+		init_hpd_work(hdmi);
+	}
+
+	ret = clk_prepare_enable(hdmi->phyref_clk);
 	if (ret) {
 		DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI vpll: %d\n",
 			      ret);
 		return ret;
 	}
 
+	ret = clk_prepare_enable(hdmi->hclk_vio);
+	if (ret) {
+		dev_err(hdmi->dev, "Failed to enable HDMI hclk_vio: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(hdmi->hclk_vop);
+	if (ret) {
+		dev_err(hdmi->dev, "Failed to enable HDMI hclk_vop: %d\n",
+			ret);
+		return ret;
+	}
+
+	if (!hdmi->id)
+		val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, RK3588_HDMI0_HPD_INT_MSK);
+	else
+		val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK, RK3588_HDMI1_HPD_INT_MSK);
+	regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+
+	if (hdmi->is_hdmi_qp) {
+		hdmi->hpd_irq = platform_get_irq(pdev, 4);
+		if (hdmi->hpd_irq < 0)
+			return hdmi->hpd_irq;
+
+		ret = devm_request_threaded_irq(hdmi->dev, hdmi->hpd_irq,
+						rockchip_hdmi_hardirq,
+						rockchip_hdmi_irq,
+						IRQF_SHARED, "dw-hdmi-qp-hpd",
+						hdmi);
+		if (ret)
+			return ret;
+	}
+
 	hdmi->phy = devm_phy_optional_get(dev, "hdmi");
 	if (IS_ERR(hdmi->phy)) {
-		ret = PTR_ERR(hdmi->phy);
-		if (ret != -EPROBE_DEFER)
-			DRM_DEV_ERROR(hdmi->dev, "failed to get phy\n");
-		return ret;
+		hdmi->phy = devm_phy_optional_get(dev, "hdmi_phy");
+		if (IS_ERR(hdmi->phy)) {
+			ret = PTR_ERR(hdmi->phy);
+			if (ret != -EPROBE_DEFER)
+				DRM_DEV_ERROR(hdmi->dev, "failed to get phy\n");
+			return ret;
+		}
 	}
 
 	drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs);
@@ -549,6 +3084,23 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 
 	platform_set_drvdata(pdev, hdmi);
 
+	if (hdmi->is_hdmi_qp) {
+		hdmi->hdmi_qp = dw_hdmi_qp_bind(pdev, encoder, plat_data);
+
+		if (IS_ERR(hdmi->hdmi_qp)) {
+			ret = PTR_ERR(hdmi->hdmi_qp);
+			drm_encoder_cleanup(encoder);
+		}
+
+		if (plat_data->connector) {
+			hdmi->sub_dev.connector = plat_data->connector;
+			hdmi->sub_dev.of_node = dev->of_node;
+			rockchip_drm_register_sub_dev(&hdmi->sub_dev);
+		}
+
+		return ret;
+	}
+
 	hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
 
 	/*
@@ -558,7 +3110,20 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	if (IS_ERR(hdmi->hdmi)) {
 		ret = PTR_ERR(hdmi->hdmi);
 		drm_encoder_cleanup(encoder);
-		clk_disable_unprepare(hdmi->vpll_clk);
+		clk_disable_unprepare(hdmi->aud_clk);
+		clk_disable_unprepare(hdmi->phyref_clk);
+		clk_disable_unprepare(hdmi->hclk_vop);
+		clk_disable_unprepare(hdmi->hpd_clk);
+		clk_disable_unprepare(hdmi->hclk_vo1);
+		clk_disable_unprepare(hdmi->earc_clk);
+		clk_disable_unprepare(hdmi->hdmitx_ref);
+		clk_disable_unprepare(hdmi->pclk);
+	}
+
+	if (plat_data->connector) {
+		hdmi->sub_dev.connector = plat_data->connector;
+		hdmi->sub_dev.of_node = dev->of_node;
+		rockchip_drm_register_sub_dev(&hdmi->sub_dev);
 	}
 
 	return ret;
@@ -569,8 +3134,27 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master,
 {
 	struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
 
-	dw_hdmi_unbind(hdmi->hdmi);
-	clk_disable_unprepare(hdmi->vpll_clk);
+	if (hdmi->is_hdmi_qp) {
+		cancel_delayed_work(&hdmi->work);
+		flush_workqueue(hdmi->workqueue);
+		destroy_workqueue(hdmi->workqueue);
+	}
+
+	if (hdmi->sub_dev.connector)
+		rockchip_drm_unregister_sub_dev(&hdmi->sub_dev);
+
+	if (hdmi->is_hdmi_qp)
+		dw_hdmi_qp_unbind(hdmi->hdmi_qp);
+	else
+		dw_hdmi_unbind(hdmi->hdmi);
+	clk_disable_unprepare(hdmi->aud_clk);
+	clk_disable_unprepare(hdmi->phyref_clk);
+	clk_disable_unprepare(hdmi->hclk_vop);
+	clk_disable_unprepare(hdmi->hpd_clk);
+	clk_disable_unprepare(hdmi->hclk_vo1);
+	clk_disable_unprepare(hdmi->earc_clk);
+	clk_disable_unprepare(hdmi->hdmitx_ref);
+	clk_disable_unprepare(hdmi->pclk);
 }
 
 static const struct component_ops dw_hdmi_rockchip_ops = {
@@ -580,32 +3164,105 @@ static const struct component_ops dw_hdmi_rockchip_ops = {
 
 static int dw_hdmi_rockchip_probe(struct platform_device *pdev)
 {
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_get_sync(&pdev->dev);
+
 	return component_add(&pdev->dev, &dw_hdmi_rockchip_ops);
 }
 
+static void dw_hdmi_rockchip_shutdown(struct platform_device *pdev)
+{
+	struct rockchip_hdmi *hdmi = dev_get_drvdata(&pdev->dev);
+
+	if (!hdmi)
+		return;
+
+	if (hdmi->is_hdmi_qp) {
+		cancel_delayed_work(&hdmi->work);
+		flush_workqueue(hdmi->workqueue);
+		dw_hdmi_qp_suspend(hdmi->dev, hdmi->hdmi_qp);
+	} else {
+		dw_hdmi_suspend(hdmi->hdmi);
+	}
+	pm_runtime_put_sync(&pdev->dev);
+}
+
 static int dw_hdmi_rockchip_remove(struct platform_device *pdev)
 {
 	component_del(&pdev->dev, &dw_hdmi_rockchip_ops);
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+static int dw_hdmi_rockchip_suspend(struct device *dev)
+{
+	struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
+
+	if (hdmi->is_hdmi_qp)
+		dw_hdmi_qp_suspend(dev, hdmi->hdmi_qp);
+	else
+		dw_hdmi_suspend(hdmi->hdmi);
+	pm_runtime_put_sync(dev);
 
 	return 0;
 }
 
-static int __maybe_unused dw_hdmi_rockchip_resume(struct device *dev)
+static int dw_hdmi_rockchip_resume(struct device *dev)
 {
 	struct rockchip_hdmi *hdmi = dev_get_drvdata(dev);
+	u32 val;
+
+	if (hdmi->is_hdmi_qp) {
+		if (!hdmi->id) {
+			val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) |
+			      HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) |
+			      HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) |
+			      HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK);
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON3, val);
+
+			val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
+					    RK3588_SET_HPD_PATH_MASK);
+			regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val);
+
+			val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL,
+					    RK3588_HDMI0_GRANT_SEL);
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val);
+		} else {
+			val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) |
+			      HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) |
+			      HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) |
+			      HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK);
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val);
+
+			val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
+					    RK3588_SET_HPD_PATH_MASK);
+			regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val);
+
+			val = HIWORD_UPDATE(RK3588_HDMI1_GRANT_SEL,
+					    RK3588_HDMI1_GRANT_SEL);
+			regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON9, val);
+		}
 
-	dw_hdmi_resume(hdmi->hdmi);
+		dw_hdmi_qp_resume(dev, hdmi->hdmi_qp);
+		drm_helper_hpd_irq_event(hdmi->encoder.dev);
+	} else {
+		dw_hdmi_resume(hdmi->hdmi);
+	}
+	pm_runtime_get_sync(dev);
 
 	return 0;
 }
 
 static const struct dev_pm_ops dw_hdmi_rockchip_pm = {
-	SET_SYSTEM_SLEEP_PM_OPS(NULL, dw_hdmi_rockchip_resume)
+	SET_SYSTEM_SLEEP_PM_OPS(dw_hdmi_rockchip_suspend,
+				dw_hdmi_rockchip_resume)
 };
 
 struct platform_driver dw_hdmi_rockchip_pltfm_driver = {
 	.probe  = dw_hdmi_rockchip_probe,
 	.remove = dw_hdmi_rockchip_remove,
+	.shutdown = dw_hdmi_rockchip_shutdown,
 	.driver = {
 		.name = "dwhdmi-rockchip",
 		.pm = &dw_hdmi_rockchip_pm,
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 78120da5e..898a99fe1 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -602,7 +602,7 @@ static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)
 	struct drm_encoder *encoder = &hdmi->encoder;
 	struct device *dev = hdmi->dev;
 
-	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+	encoder->possible_crtcs = rockchip_drm_of_find_possible_crtcs(drm, dev->of_node);
 
 	/*
 	 * If we failed to find the CRTC(s) which this encoder is
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index 0f3eb392f..35944088d 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -6,17 +6,24 @@
  * based on exynos_drm_drv.c
  */
 
+#include <linux/dma-buf-cache.h>
 #include <linux/dma-mapping.h>
 #include <linux/dma-iommu.h>
+#include <linux/genalloc.h>
 #include <linux/pm_runtime.h>
 #include <linux/module.h>
+#include <linux/of_address.h>
 #include <linux/of_graph.h>
 #include <linux/of_platform.h>
+#include <linux/clk.h>
 #include <linux/component.h>
 #include <linux/console.h>
 #include <linux/iommu.h>
+#include <linux/of_reserved_mem.h>
 
+#include <drm/drm_debugfs.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_displayid.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_of.h>
@@ -27,16 +34,820 @@
 #include "rockchip_drm_fb.h"
 #include "rockchip_drm_fbdev.h"
 #include "rockchip_drm_gem.h"
+#include <drm/rockchip_drm_logo.h>
+
+#include "../drm_crtc_internal.h"
 
 #define DRIVER_NAME	"rockchip"
 #define DRIVER_DESC	"RockChip Soc DRM"
 #define DRIVER_DATE	"20140818"
-#define DRIVER_MAJOR	1
+#define DRIVER_MAJOR	3
 #define DRIVER_MINOR	0
 
 static bool is_support_iommu = true;
 static struct drm_driver rockchip_drm_driver;
 
+void drm_mode_convert_to_split_mode(struct drm_display_mode *mode)
+{
+	u16 hactive, hfp, hsync, hbp;
+
+	hactive = mode->hdisplay;
+	hfp = mode->hsync_start - mode->hdisplay;
+	hsync = mode->hsync_end - mode->hsync_start;
+	hbp = mode->htotal - mode->hsync_end;
+
+	mode->clock *= 2;
+	mode->hdisplay = hactive * 2;
+	mode->hsync_start = mode->hdisplay + hfp * 2;
+	mode->hsync_end = mode->hsync_start + hsync * 2;
+	mode->htotal = mode->hsync_end + hbp * 2;
+	drm_mode_set_name(mode);
+}
+EXPORT_SYMBOL(drm_mode_convert_to_split_mode);
+
+void drm_mode_convert_to_origin_mode(struct drm_display_mode *mode)
+{
+	u16 hactive, hfp, hsync, hbp;
+
+	hactive = mode->hdisplay;
+	hfp = mode->hsync_start - mode->hdisplay;
+	hsync = mode->hsync_end - mode->hsync_start;
+	hbp = mode->htotal - mode->hsync_end;
+
+	mode->clock /= 2;
+	mode->hdisplay = hactive / 2;
+	mode->hsync_start = mode->hdisplay + hfp / 2;
+	mode->hsync_end = mode->hsync_start + hsync / 2;
+	mode->htotal = mode->hsync_end + hbp / 2;
+}
+EXPORT_SYMBOL(drm_mode_convert_to_origin_mode);
+
+/**
+ * drm_connector_oob_hotplug_event - Report out-of-band hotplug event to connector
+ * @connector: connector to report the event on
+ *
+ * On some hardware a hotplug event notification may come from outside the display
+ * driver / device. An example of this is some USB Type-C setups where the hardware
+ * muxes the DisplayPort data and aux-lines but does not pass the altmode HPD
+ * status bit to the GPU's DP HPD pin.
+ *
+ * This function can be used to report these out-of-band events after obtaining
+ * a drm_connector reference through calling drm_connector_find_by_fwnode().
+ */
+void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode)
+{
+	struct rockchip_drm_sub_dev *sub_dev;
+
+	if (!connector_fwnode || !connector_fwnode->dev)
+		return;
+
+	sub_dev = rockchip_drm_get_sub_dev(dev_of_node(connector_fwnode->dev));
+
+	if (sub_dev && sub_dev->connector && sub_dev->oob_hotplug_event)
+		sub_dev->oob_hotplug_event(sub_dev->connector);
+}
+EXPORT_SYMBOL(drm_connector_oob_hotplug_event);
+
+uint32_t rockchip_drm_get_bpp(const struct drm_format_info *info)
+{
+	/* use whatever a driver has set */
+	if (info->cpp[0])
+		return info->cpp[0] * 8;
+
+	switch (info->format) {
+	case DRM_FORMAT_YUV420_8BIT:
+		return 12;
+	case DRM_FORMAT_YUV420_10BIT:
+		return 15;
+	case DRM_FORMAT_VUY101010:
+		return 30;
+	default:
+		break;
+	}
+
+	/* all attempts failed */
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_get_bpp);
+
+/**
+ * rockchip_drm_of_find_possible_crtcs - find the possible CRTCs for an active
+ * encoder port
+ * @dev: DRM device
+ * @port: encoder port to scan for endpoints
+ *
+ * Scan all active endpoints attached to a port, locate their attached CRTCs,
+ * and generate the DRM mask of CRTCs which may be attached to this
+ * encoder.
+ *
+ * See Documentation/devicetree/bindings/graph.txt for the bindings.
+ */
+uint32_t rockchip_drm_of_find_possible_crtcs(struct drm_device *dev,
+					     struct device_node *port)
+{
+	struct device_node *remote_port, *ep;
+	uint32_t possible_crtcs = 0;
+
+	for_each_endpoint_of_node(port, ep) {
+		if (!of_device_is_available(ep))
+			continue;
+
+		remote_port = of_graph_get_remote_port(ep);
+		if (!remote_port) {
+			of_node_put(ep);
+			return 0;
+		}
+
+		possible_crtcs |= drm_of_crtc_port_mask(dev, remote_port);
+
+		of_node_put(remote_port);
+	}
+
+	return possible_crtcs;
+}
+EXPORT_SYMBOL(rockchip_drm_of_find_possible_crtcs);
+
+static DEFINE_MUTEX(rockchip_drm_sub_dev_lock);
+static LIST_HEAD(rockchip_drm_sub_dev_list);
+
+void rockchip_drm_register_sub_dev(struct rockchip_drm_sub_dev *sub_dev)
+{
+	mutex_lock(&rockchip_drm_sub_dev_lock);
+	list_add_tail(&sub_dev->list, &rockchip_drm_sub_dev_list);
+	mutex_unlock(&rockchip_drm_sub_dev_lock);
+}
+EXPORT_SYMBOL(rockchip_drm_register_sub_dev);
+
+void rockchip_drm_unregister_sub_dev(struct rockchip_drm_sub_dev *sub_dev)
+{
+	mutex_lock(&rockchip_drm_sub_dev_lock);
+	list_del(&sub_dev->list);
+	mutex_unlock(&rockchip_drm_sub_dev_lock);
+}
+EXPORT_SYMBOL(rockchip_drm_unregister_sub_dev);
+
+struct rockchip_drm_sub_dev *rockchip_drm_get_sub_dev(struct device_node *node)
+{
+	struct rockchip_drm_sub_dev *sub_dev = NULL;
+	bool found = false;
+
+	mutex_lock(&rockchip_drm_sub_dev_lock);
+	list_for_each_entry(sub_dev, &rockchip_drm_sub_dev_list, list) {
+		if (sub_dev->of_node == node) {
+			found = true;
+			break;
+		}
+	}
+	mutex_unlock(&rockchip_drm_sub_dev_lock);
+
+	return found ? sub_dev : NULL;
+}
+EXPORT_SYMBOL(rockchip_drm_get_sub_dev);
+
+int rockchip_drm_get_sub_dev_type(void)
+{
+	int connector_type = DRM_MODE_CONNECTOR_Unknown;
+	struct rockchip_drm_sub_dev *sub_dev = NULL;
+
+	mutex_lock(&rockchip_drm_sub_dev_lock);
+	list_for_each_entry(sub_dev, &rockchip_drm_sub_dev_list, list) {
+		if (sub_dev->connector->encoder) {
+			connector_type = sub_dev->connector->connector_type;
+			break;
+		}
+	}
+	mutex_unlock(&rockchip_drm_sub_dev_lock);
+
+	return connector_type;
+}
+EXPORT_SYMBOL(rockchip_drm_get_sub_dev_type);
+
+void rockchip_drm_te_handle(struct drm_crtc *crtc)
+{
+	struct rockchip_drm_private *priv = crtc->dev->dev_private;
+	int pipe = drm_crtc_index(crtc);
+
+	if (priv->crtc_funcs[pipe] && priv->crtc_funcs[pipe]->te_handler)
+		priv->crtc_funcs[pipe]->te_handler(crtc);
+}
+EXPORT_SYMBOL(rockchip_drm_te_handle);
+
+static const struct drm_display_mode rockchip_drm_default_modes[] = {
+	/* 4 - 1280x720@60Hz 16:9 */
+	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
+		   1430, 1650, 0, 720, 725, 730, 750, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+	/* 16 - 1920x1080@60Hz 16:9 */
+	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
+		   2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+	/* 31 - 1920x1080@50Hz 16:9 */
+	{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
+		   2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+	/* 19 - 1280x720@50Hz 16:9 */
+	{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,
+		   1760, 1980, 0, 720, 725, 730, 750, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
+	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
+	/* 0x10 - 1024x768@60Hz */
+	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
+		   1184, 1344, 0,  768, 771, 777, 806, 0,
+		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+	/* 17 - 720x576@50Hz 4:3 */
+	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,
+		   796, 864, 0, 576, 581, 586, 625, 0,
+		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
+	/* 2 - 720x480@60Hz 4:3 */
+	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,
+		   798, 858, 0, 480, 489, 495, 525, 0,
+		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
+	  .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, },
+};
+
+int rockchip_drm_add_modes_noedid(struct drm_connector *connector)
+{
+	struct drm_device *dev = connector->dev;
+	struct drm_display_mode *mode;
+	int i, count, num_modes = 0;
+
+	mutex_lock(&rockchip_drm_sub_dev_lock);
+	count = ARRAY_SIZE(rockchip_drm_default_modes);
+
+	for (i = 0; i < count; i++) {
+		const struct drm_display_mode *ptr = &rockchip_drm_default_modes[i];
+
+		mode = drm_mode_duplicate(dev, ptr);
+		if (mode) {
+			if (!i)
+				mode->type = DRM_MODE_TYPE_PREFERRED;
+			drm_mode_probed_add(connector, mode);
+			num_modes++;
+		}
+	}
+	mutex_unlock(&rockchip_drm_sub_dev_lock);
+
+	return num_modes;
+}
+EXPORT_SYMBOL(rockchip_drm_add_modes_noedid);
+
+static int
+cea_db_tag(const u8 *db)
+{
+	return db[0] >> 5;
+}
+
+static int
+cea_db_payload_len(const u8 *db)
+{
+	return db[0] & 0x1f;
+}
+
+#define for_each_cea_db(cea, i, start, end) \
+	for ((i) = (start); \
+	     (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); \
+	     (i) += cea_db_payload_len(&(cea)[(i)]) + 1)
+
+#define HDMI_NEXT_HDR_VSDB_OUI 0xd04601
+
+static bool cea_db_is_hdmi_next_hdr_block(const u8 *db)
+{
+	unsigned int oui;
+
+	if (cea_db_tag(db) != 0x07)
+		return false;
+
+	if (cea_db_payload_len(db) < 11)
+		return false;
+
+	oui = db[3] << 16 | db[2] << 8 | db[1];
+
+	return oui == HDMI_NEXT_HDR_VSDB_OUI;
+}
+
+static bool cea_db_is_hdmi_forum_vsdb(const u8 *db)
+{
+	unsigned int oui;
+
+	if (cea_db_tag(db) != 0x03)
+		return false;
+
+	if (cea_db_payload_len(db) < 7)
+		return false;
+
+	oui = db[3] << 16 | db[2] << 8 | db[1];
+
+	return oui == HDMI_FORUM_IEEE_OUI;
+}
+
+static int
+cea_db_offsets(const u8 *cea, int *start, int *end)
+{
+	/* DisplayID CTA extension blocks and top-level CEA EDID
+	 * block header definitions differ in the following bytes:
+	 *   1) Byte 2 of the header specifies length differently,
+	 *   2) Byte 3 is only present in the CEA top level block.
+	 *
+	 * The different definitions for byte 2 follow.
+	 *
+	 * DisplayID CTA extension block defines byte 2 as:
+	 *   Number of payload bytes
+	 *
+	 * CEA EDID block defines byte 2 as:
+	 *   Byte number (decimal) within this block where the 18-byte
+	 *   DTDs begin. If no non-DTD data is present in this extension
+	 *   block, the value should be set to 04h (the byte after next).
+	 *   If set to 00h, there are no DTDs present in this block and
+	 *   no non-DTD data.
+	 */
+	if (cea[0] == 0x81) {
+		/*
+		 * for_each_displayid_db() has already verified
+		 * that these stay within expected bounds.
+		 */
+		*start = 3;
+		*end = *start + cea[2];
+	} else if (cea[0] == 0x02) {
+		/* Data block offset in CEA extension block */
+		*start = 4;
+		*end = cea[2];
+		if (*end == 0)
+			*end = 127;
+		if (*end < 4 || *end > 127)
+			return -ERANGE;
+	} else {
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static u8 *find_edid_extension(const struct edid *edid,
+			       int ext_id, int *ext_index)
+{
+	u8 *edid_ext = NULL;
+	int i;
+
+	/* No EDID or EDID extensions */
+	if (edid == NULL || edid->extensions == 0)
+		return NULL;
+
+	/* Find CEA extension */
+	for (i = *ext_index; i < edid->extensions; i++) {
+		edid_ext = (u8 *)edid + EDID_LENGTH * (i + 1);
+		if (edid_ext[0] == ext_id)
+			break;
+	}
+
+	if (i >= edid->extensions)
+		return NULL;
+
+	*ext_index = i + 1;
+
+	return edid_ext;
+}
+
+static int validate_displayid(u8 *displayid, int length, int idx)
+{
+	int i, dispid_length;
+	u8 csum = 0;
+	struct displayid_hdr *base;
+
+	base = (struct displayid_hdr *)&displayid[idx];
+
+	DRM_DEBUG_KMS("base revision 0x%x, length %d, %d %d\n",
+		      base->rev, base->bytes, base->prod_id, base->ext_count);
+
+	/* +1 for DispID checksum */
+	dispid_length = sizeof(*base) + base->bytes + 1;
+	if (dispid_length > length - idx)
+		return -EINVAL;
+
+	for (i = 0; i < dispid_length; i++)
+		csum += displayid[idx + i];
+	if (csum) {
+		DRM_NOTE("DisplayID checksum invalid, remainder is %d\n", csum);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static u8 *find_displayid_extension(const struct edid *edid,
+				    int *length, int *idx,
+				    int *ext_index)
+{
+	u8 *displayid = find_edid_extension(edid, 0x70, ext_index);
+	struct displayid_hdr *base;
+	int ret;
+
+	if (!displayid)
+		return NULL;
+
+	/* EDID extensions block checksum isn't for us */
+	*length = EDID_LENGTH - 1;
+	*idx = 1;
+
+	ret = validate_displayid(displayid, *length, *idx);
+	if (ret)
+		return NULL;
+
+	base = (struct displayid_hdr *)&displayid[*idx];
+	*length = *idx + sizeof(*base) + base->bytes;
+
+	return displayid;
+}
+
+static u8 *find_cea_extension(const struct edid *edid)
+{
+	int length, idx;
+	struct displayid_block *block;
+	u8 *cea;
+	u8 *displayid;
+	int ext_index;
+
+	/* Look for a top level CEA extension block */
+	/* FIXME: make callers iterate through multiple CEA ext blocks? */
+	ext_index = 0;
+	cea = find_edid_extension(edid, 0x02, &ext_index);
+	if (cea)
+		return cea;
+
+	/* CEA blocks can also be found embedded in a DisplayID block */
+	ext_index = 0;
+	for (;;) {
+		displayid = find_displayid_extension(edid, &length, &idx,
+						     &ext_index);
+		if (!displayid)
+			return NULL;
+
+		idx += sizeof(struct displayid_hdr);
+		for_each_displayid_db(displayid, block, idx, length) {
+			if (block->tag == 0x81)
+				return (u8 *)block;
+		}
+	}
+
+	return NULL;
+}
+
+#define EDID_CEA_YCRCB422	(1 << 4)
+
+int rockchip_drm_get_yuv422_format(struct drm_connector *connector,
+				   struct edid *edid)
+{
+	struct drm_display_info *info;
+	const u8 *edid_ext;
+
+	if (!connector || !edid)
+		return -EINVAL;
+
+	info = &connector->display_info;
+
+	edid_ext = find_cea_extension(edid);
+	if (!edid_ext)
+		return -EINVAL;
+
+	if (edid_ext[3] & EDID_CEA_YCRCB422)
+		info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
+
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_get_yuv422_format);
+
+static
+void get_max_frl_rate(int max_frl_rate, u8 *max_lanes, u8 *max_rate_per_lane)
+{
+	switch (max_frl_rate) {
+	case 1:
+		*max_lanes = 3;
+		*max_rate_per_lane = 3;
+		break;
+	case 2:
+		*max_lanes = 3;
+		*max_rate_per_lane = 6;
+		break;
+	case 3:
+		*max_lanes = 4;
+		*max_rate_per_lane = 6;
+		break;
+	case 4:
+		*max_lanes = 4;
+		*max_rate_per_lane = 8;
+		break;
+	case 5:
+		*max_lanes = 4;
+		*max_rate_per_lane = 10;
+		break;
+	case 6:
+		*max_lanes = 4;
+		*max_rate_per_lane = 12;
+		break;
+	case 0:
+	default:
+		*max_lanes = 0;
+		*max_rate_per_lane = 0;
+	}
+}
+
+#define EDID_DSC_10BPC			(1 << 0)
+#define EDID_DSC_12BPC			(1 << 1)
+#define EDID_DSC_16BPC			(1 << 2)
+#define EDID_DSC_ALL_BPP		(1 << 3)
+#define EDID_DSC_NATIVE_420		(1 << 6)
+#define EDID_DSC_1P2			(1 << 7)
+#define EDID_DSC_MAX_FRL_RATE_MASK	0xf0
+#define EDID_DSC_MAX_SLICES		0xf
+#define EDID_DSC_TOTAL_CHUNK_KBYTES	0x3f
+#define EDID_MAX_FRL_RATE_MASK		0xf0
+
+static
+void parse_edid_forum_vsdb(struct rockchip_drm_dsc_cap *dsc_cap,
+			   u8 *max_frl_rate_per_lane, u8 *max_lanes,
+			   const u8 *hf_vsdb)
+{
+	u8 max_frl_rate;
+	u8 dsc_max_frl_rate;
+	u8 dsc_max_slices;
+
+	if (!hf_vsdb[7])
+		return;
+
+	DRM_DEBUG_KMS("hdmi_21 sink detected. parsing edid\n");
+	max_frl_rate = (hf_vsdb[7] & EDID_MAX_FRL_RATE_MASK) >> 4;
+	get_max_frl_rate(max_frl_rate, max_lanes,
+			 max_frl_rate_per_lane);
+
+	if (cea_db_payload_len(hf_vsdb) < 13)
+		return;
+
+	dsc_cap->v_1p2 = hf_vsdb[11] & EDID_DSC_1P2;
+
+	if (!dsc_cap->v_1p2)
+		return;
+
+	dsc_cap->native_420 = hf_vsdb[11] & EDID_DSC_NATIVE_420;
+	dsc_cap->all_bpp = hf_vsdb[11] & EDID_DSC_ALL_BPP;
+
+	if (hf_vsdb[11] & EDID_DSC_16BPC)
+		dsc_cap->bpc_supported = 16;
+	else if (hf_vsdb[11] & EDID_DSC_12BPC)
+		dsc_cap->bpc_supported = 12;
+	else if (hf_vsdb[11] & EDID_DSC_10BPC)
+		dsc_cap->bpc_supported = 10;
+	else
+		dsc_cap->bpc_supported = 0;
+
+	dsc_max_frl_rate = (hf_vsdb[12] & EDID_DSC_MAX_FRL_RATE_MASK) >> 4;
+	get_max_frl_rate(dsc_max_frl_rate, &dsc_cap->max_lanes,
+			 &dsc_cap->max_frl_rate_per_lane);
+	dsc_cap->total_chunk_kbytes = hf_vsdb[13] & EDID_DSC_TOTAL_CHUNK_KBYTES;
+
+	dsc_max_slices = hf_vsdb[12] & EDID_DSC_MAX_SLICES;
+	switch (dsc_max_slices) {
+	case 1:
+		dsc_cap->max_slices = 1;
+		dsc_cap->clk_per_slice = 340;
+		break;
+	case 2:
+		dsc_cap->max_slices = 2;
+		dsc_cap->clk_per_slice = 340;
+		break;
+	case 3:
+		dsc_cap->max_slices = 4;
+		dsc_cap->clk_per_slice = 340;
+		break;
+	case 4:
+		dsc_cap->max_slices = 8;
+		dsc_cap->clk_per_slice = 340;
+		break;
+	case 5:
+		dsc_cap->max_slices = 8;
+		dsc_cap->clk_per_slice = 400;
+		break;
+	case 6:
+		dsc_cap->max_slices = 12;
+		dsc_cap->clk_per_slice = 400;
+		break;
+	case 7:
+		dsc_cap->max_slices = 16;
+		dsc_cap->clk_per_slice = 400;
+		break;
+	case 0:
+	default:
+		dsc_cap->max_slices = 0;
+		dsc_cap->clk_per_slice = 0;
+	}
+}
+
+enum {
+	VER_26_BYTE_V0,
+	VER_15_BYTE_V1,
+	VER_12_BYTE_V1,
+	VER_12_BYTE_V2,
+};
+
+static int check_next_hdr_version(const u8 *next_hdr_db)
+{
+	u16 ver;
+
+	ver = (next_hdr_db[5] & 0xf0) << 8 | next_hdr_db[0];
+
+	switch (ver) {
+	case 0x00f9:
+		return VER_26_BYTE_V0;
+	case 0x20ee:
+		return VER_15_BYTE_V1;
+	case 0x20eb:
+		return VER_12_BYTE_V1;
+	case 0x40eb:
+		return VER_12_BYTE_V2;
+	default:
+		return -ENOENT;
+	}
+}
+
+static void parse_ver_26_v0_data(struct ver_26_v0 *hdr, const u8 *data)
+{
+	hdr->yuv422_12bit = data[5] & BIT(0);
+	hdr->support_2160p_60 = (data[5] & BIT(1)) >> 1;
+	hdr->global_dimming = (data[5] & BIT(2)) >> 2;
+
+	hdr->dm_major_ver = (data[21] & 0xf0) >> 4;
+	hdr->dm_minor_ver = data[21] & 0xf;
+
+	hdr->t_min_pq = (data[19] << 4) | ((data[18] & 0xf0) >> 4);
+	hdr->t_max_pq = (data[20] << 4) | (data[18] & 0xf);
+
+	hdr->rx = (data[7] << 4) | ((data[6] & 0xf0) >> 4);
+	hdr->ry = (data[8] << 4) | (data[6] & 0xf);
+	hdr->gx = (data[10] << 4) | ((data[9] & 0xf0) >> 4);
+	hdr->gy = (data[11] << 4) | (data[9] & 0xf);
+	hdr->bx = (data[13] << 4) | ((data[12] & 0xf0) >> 4);
+	hdr->by = (data[14] << 4) | (data[12] & 0xf);
+	hdr->wx = (data[16] << 4) | ((data[15] & 0xf0) >> 4);
+	hdr->wy = (data[17] << 4) | (data[15] & 0xf);
+}
+
+static void parse_ver_15_v1_data(struct ver_15_v1 *hdr, const u8 *data)
+{
+	hdr->yuv422_12bit = data[5] & BIT(0);
+	hdr->support_2160p_60 = (data[5] & BIT(1)) >> 1;
+	hdr->global_dimming = data[6] & BIT(0);
+
+	hdr->dm_version = (data[5] & 0x1c) >> 2;
+
+	hdr->colorimetry = data[7] & BIT(0);
+
+	hdr->t_max_lum = (data[6] & 0xfe) >> 1;
+	hdr->t_min_lum = (data[7] & 0xfe) >> 1;
+
+	hdr->rx = data[9];
+	hdr->ry = data[10];
+	hdr->gx = data[11];
+	hdr->gy = data[12];
+	hdr->bx = data[13];
+	hdr->by = data[14];
+}
+
+static void parse_ver_12_v1_data(struct ver_12_v1 *hdr, const u8 *data)
+{
+	hdr->yuv422_12bit = data[5] & BIT(0);
+	hdr->support_2160p_60 = (data[5] & BIT(1)) >> 1;
+	hdr->global_dimming = data[6] & BIT(0);
+
+	hdr->dm_version = (data[5] & 0x1c) >> 2;
+
+	hdr->colorimetry = data[7] & BIT(0);
+
+	hdr->t_max_lum = (data[6] & 0xfe) >> 1;
+	hdr->t_min_lum = (data[7] & 0xfe) >> 1;
+
+	hdr->low_latency = data[8] & 0x3;
+
+	hdr->unique_rx = (data[11] & 0xf8) >> 3;
+	hdr->unique_ry = (data[11] & 0x7) << 2 | (data[10] & BIT(0)) << 1 |
+		(data[9] & BIT(0));
+	hdr->unique_gx = (data[9] & 0xfe) >> 1;
+	hdr->unique_gy = (data[10] & 0xfe) >> 1;
+	hdr->unique_bx = (data[8] & 0xe0) >> 5;
+	hdr->unique_by = (data[8] & 0x1c) >> 2;
+}
+
+static void parse_ver_12_v2_data(struct ver_12_v2 *hdr, const u8 *data)
+{
+	hdr->yuv422_12bit = data[5] & BIT(0);
+	hdr->backlt_ctrl = (data[5] & BIT(1)) >> 1;
+	hdr->global_dimming = (data[6] & BIT(2)) >> 2;
+
+	hdr->dm_version = (data[5] & 0x1c) >> 2;
+	hdr->backlt_min_luma = data[6] & 0x3;
+	hdr->interface = data[7] & 0x3;
+	hdr->yuv444_10b_12b = (data[8] & BIT(0)) << 1 | (data[9] & BIT(0));
+
+	hdr->t_min_pq_v2 = (data[6] & 0xf8) >> 3;
+	hdr->t_max_pq_v2 = (data[7] & 0xf8) >> 3;
+
+	hdr->unique_rx = (data[10] & 0xf8) >> 3;
+	hdr->unique_ry = (data[11] & 0xf8) >> 3;
+	hdr->unique_gx = (data[8] & 0xfe) >> 1;
+	hdr->unique_gy = (data[9] & 0xfe) >> 1;
+	hdr->unique_bx = data[10] & 0x7;
+	hdr->unique_by = data[11] & 0x7;
+}
+
+static
+void parse_next_hdr_block(struct next_hdr_sink_data *sink_data,
+			  const u8 *next_hdr_db)
+{
+	int version;
+
+	version = check_next_hdr_version(next_hdr_db);
+	if (version < 0)
+		return;
+
+	sink_data->version = version;
+
+	switch (version) {
+	case VER_26_BYTE_V0:
+		parse_ver_26_v0_data(&sink_data->ver_26_v0, next_hdr_db);
+		break;
+	case VER_15_BYTE_V1:
+		parse_ver_15_v1_data(&sink_data->ver_15_v1, next_hdr_db);
+		break;
+	case VER_12_BYTE_V1:
+		parse_ver_12_v1_data(&sink_data->ver_12_v1, next_hdr_db);
+		break;
+	case VER_12_BYTE_V2:
+		parse_ver_12_v2_data(&sink_data->ver_12_v2, next_hdr_db);
+		break;
+	default:
+		break;
+	}
+}
+
+int rockchip_drm_parse_cea_ext(struct rockchip_drm_dsc_cap *dsc_cap,
+			       u8 *max_frl_rate_per_lane, u8 *max_lanes,
+			       const struct edid *edid)
+{
+	const u8 *edid_ext;
+	int i, start, end;
+
+	if (!dsc_cap || !max_frl_rate_per_lane || !max_lanes || !edid)
+		return -EINVAL;
+
+	edid_ext = find_cea_extension(edid);
+	if (!edid_ext)
+		return -EINVAL;
+
+	if (cea_db_offsets(edid_ext, &start, &end))
+		return -EINVAL;
+
+	for_each_cea_db(edid_ext, i, start, end) {
+		const u8 *db = &edid_ext[i];
+
+		if (cea_db_is_hdmi_forum_vsdb(db))
+			parse_edid_forum_vsdb(dsc_cap, max_frl_rate_per_lane,
+					      max_lanes, db);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_parse_cea_ext);
+
+int rockchip_drm_parse_next_hdr(struct next_hdr_sink_data *sink_data,
+				const struct edid *edid)
+{
+	const u8 *edid_ext;
+	int i, start, end;
+
+	if (!sink_data || !edid)
+		return -EINVAL;
+
+	memset(sink_data, 0, sizeof(struct next_hdr_sink_data));
+
+	edid_ext = find_cea_extension(edid);
+	if (!edid_ext)
+		return -EINVAL;
+
+	if (cea_db_offsets(edid_ext, &start, &end))
+		return -EINVAL;
+
+	for_each_cea_db(edid_ext, i, start, end) {
+		const u8 *db = &edid_ext[i];
+
+		if (cea_db_is_hdmi_next_hdr_block(db))
+			parse_next_hdr_block(sink_data, db);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_parse_next_hdr);
+
 /*
  * Attach a (component) device to the shared drm dma mapping from master drm
  * device.  This is used by the VOPs to map GEM buffers to a common DMA
@@ -72,6 +883,55 @@ void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
 	iommu_detach_device(domain, dev);
 }
 
+int rockchip_register_crtc_funcs(struct drm_crtc *crtc,
+				 const struct rockchip_crtc_funcs *crtc_funcs)
+{
+	int pipe = drm_crtc_index(crtc);
+	struct rockchip_drm_private *priv = crtc->dev->dev_private;
+
+	if (pipe >= ROCKCHIP_MAX_CRTC)
+		return -EINVAL;
+
+	priv->crtc_funcs[pipe] = crtc_funcs;
+
+	return 0;
+}
+
+void rockchip_unregister_crtc_funcs(struct drm_crtc *crtc)
+{
+	int pipe = drm_crtc_index(crtc);
+	struct rockchip_drm_private *priv = crtc->dev->dev_private;
+
+	if (pipe >= ROCKCHIP_MAX_CRTC)
+		return;
+
+	priv->crtc_funcs[pipe] = NULL;
+}
+
+static int rockchip_drm_fault_handler(struct iommu_domain *iommu,
+				      struct device *dev,
+				      unsigned long iova, int flags, void *arg)
+{
+	struct drm_device *drm_dev = arg;
+	struct rockchip_drm_private *priv = drm_dev->dev_private;
+	struct drm_crtc *crtc;
+
+	DRM_ERROR("iommu fault handler flags: 0x%x\n", flags);
+	drm_for_each_crtc(crtc, drm_dev) {
+		int pipe = drm_crtc_index(crtc);
+
+		if (priv->crtc_funcs[pipe] &&
+		    priv->crtc_funcs[pipe]->regs_dump)
+			priv->crtc_funcs[pipe]->regs_dump(crtc, NULL);
+
+		if (priv->crtc_funcs[pipe] &&
+		    priv->crtc_funcs[pipe]->debugfs_dump)
+			priv->crtc_funcs[pipe]->debugfs_dump(crtc, NULL);
+	}
+
+	return 0;
+}
+
 static int rockchip_drm_init_iommu(struct drm_device *drm_dev)
 {
 	struct rockchip_drm_private *private = drm_dev->dev_private;
@@ -94,6 +954,9 @@ static int rockchip_drm_init_iommu(struct drm_device *drm_dev)
 	drm_mm_init(&private->mm, start, end - start + 1);
 	mutex_init(&private->mm_lock);
 
+	iommu_set_fault_handler(private->domain, rockchip_drm_fault_handler,
+				drm_dev);
+
 	return 0;
 }
 
@@ -108,6 +971,229 @@ static void rockchip_iommu_cleanup(struct drm_device *drm_dev)
 	iommu_domain_free(private->domain);
 }
 
+#ifdef CONFIG_DEBUG_FS
+static int rockchip_drm_mm_dump(struct seq_file *s, void *data)
+{
+	struct drm_info_node *node = s->private;
+	struct drm_minor *minor = node->minor;
+	struct drm_device *drm_dev = minor->dev;
+	struct rockchip_drm_private *priv = drm_dev->dev_private;
+	struct drm_printer p = drm_seq_file_printer(s);
+
+	if (!priv->domain)
+		return 0;
+	mutex_lock(&priv->mm_lock);
+	drm_mm_print(&priv->mm, &p);
+	mutex_unlock(&priv->mm_lock);
+
+	return 0;
+}
+
+static int rockchip_drm_summary_show(struct seq_file *s, void *data)
+{
+	struct drm_info_node *node = s->private;
+	struct drm_minor *minor = node->minor;
+	struct drm_device *drm_dev = minor->dev;
+	struct rockchip_drm_private *priv = drm_dev->dev_private;
+	struct drm_crtc *crtc;
+
+	drm_for_each_crtc(crtc, drm_dev) {
+		int pipe = drm_crtc_index(crtc);
+
+		if (priv->crtc_funcs[pipe] &&
+		    priv->crtc_funcs[pipe]->debugfs_dump)
+			priv->crtc_funcs[pipe]->debugfs_dump(crtc, s);
+	}
+
+	return 0;
+}
+
+static struct drm_info_list rockchip_debugfs_files[] = {
+	{ "summary", rockchip_drm_summary_show, 0, NULL },
+	{ "mm_dump", rockchip_drm_mm_dump, 0, NULL },
+};
+
+static void rockchip_drm_debugfs_init(struct drm_minor *minor)
+{
+	struct drm_device *dev = minor->dev;
+	struct rockchip_drm_private *priv = dev->dev_private;
+	struct drm_crtc *crtc;
+
+	drm_debugfs_create_files(rockchip_debugfs_files,
+				 ARRAY_SIZE(rockchip_debugfs_files),
+				 minor->debugfs_root, minor);
+
+	drm_for_each_crtc(crtc, dev) {
+		int pipe = drm_crtc_index(crtc);
+
+		if (priv->crtc_funcs[pipe] &&
+		    priv->crtc_funcs[pipe]->debugfs_init)
+			priv->crtc_funcs[pipe]->debugfs_init(minor, crtc);
+	}
+}
+#endif
+
+static int rockchip_drm_create_properties(struct drm_device *dev)
+{
+	struct drm_property *prop;
+	struct rockchip_drm_private *private = dev->dev_private;
+
+	prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+					 "EOTF", 0, 5);
+	if (!prop)
+		return -ENOMEM;
+	private->eotf_prop = prop;
+
+	prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+					 "COLOR_SPACE", 0, 12);
+	if (!prop)
+		return -ENOMEM;
+	private->color_space_prop = prop;
+
+	prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+					 "ASYNC_COMMIT", 0, 1);
+	if (!prop)
+		return -ENOMEM;
+	private->async_commit_prop = prop;
+
+	prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+					 "SHARE_ID", 0, UINT_MAX);
+	if (!prop)
+		return -ENOMEM;
+	private->share_id_prop = prop;
+
+	prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_IMMUTABLE,
+					 "CONNECTOR_ID", 0, 0xf);
+	if (!prop)
+		return -ENOMEM;
+	private->connector_id_prop = prop;
+
+	prop = drm_property_create_object(dev,
+					  DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_IMMUTABLE,
+					  "SOC_ID", DRM_MODE_OBJECT_CRTC);
+	private->soc_id_prop = prop;
+
+	prop = drm_property_create_object(dev,
+					  DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_IMMUTABLE,
+					  "PORT_ID", DRM_MODE_OBJECT_CRTC);
+	private->port_id_prop = prop;
+
+	private->aclk_prop = drm_property_create_range(dev, 0, "ACLK", 0, UINT_MAX);
+	private->bg_prop = drm_property_create_range(dev, 0, "BACKGROUND", 0, UINT_MAX);
+	private->line_flag_prop = drm_property_create_range(dev, 0, "LINE_FLAG1", 0, UINT_MAX);
+
+	return drm_mode_create_tv_properties(dev, 0, NULL);
+}
+
+static void rockchip_attach_connector_property(struct drm_device *drm)
+{
+	struct drm_connector *connector;
+	struct drm_mode_config *conf = &drm->mode_config;
+	struct drm_connector_list_iter conn_iter;
+
+	mutex_lock(&drm->mode_config.mutex);
+
+#define ROCKCHIP_PROP_ATTACH(prop, v) \
+		drm_object_attach_property(&connector->base, prop, v)
+
+	drm_connector_list_iter_begin(drm, &conn_iter);
+	drm_for_each_connector_iter(connector, &conn_iter) {
+		ROCKCHIP_PROP_ATTACH(conf->tv_brightness_property, 50);
+		ROCKCHIP_PROP_ATTACH(conf->tv_contrast_property, 50);
+		ROCKCHIP_PROP_ATTACH(conf->tv_saturation_property, 50);
+		ROCKCHIP_PROP_ATTACH(conf->tv_hue_property, 50);
+	}
+	drm_connector_list_iter_end(&conn_iter);
+#undef ROCKCHIP_PROP_ATTACH
+
+	mutex_unlock(&drm->mode_config.mutex);
+}
+
+static void rockchip_drm_set_property_default(struct drm_device *drm)
+{
+	struct drm_connector *connector;
+	struct drm_mode_config *conf = &drm->mode_config;
+	struct drm_atomic_state *state;
+	int ret;
+	struct drm_connector_list_iter conn_iter;
+
+	drm_modeset_lock_all(drm);
+
+	state = drm_atomic_helper_duplicate_state(drm, conf->acquire_ctx);
+	if (!state) {
+		DRM_ERROR("failed to alloc atomic state\n");
+		goto err_unlock;
+	}
+	state->acquire_ctx = conf->acquire_ctx;
+
+	drm_connector_list_iter_begin(drm, &conn_iter);
+	drm_for_each_connector_iter(connector, &conn_iter) {
+		struct drm_connector_state *connector_state;
+
+		connector_state = drm_atomic_get_connector_state(state,
+								 connector);
+		if (IS_ERR(connector_state)) {
+			DRM_ERROR("Connector[%d]: Failed to get state\n", connector->base.id);
+			continue;
+		}
+
+		connector_state->tv.brightness = 50;
+		connector_state->tv.contrast = 50;
+		connector_state->tv.saturation = 50;
+		connector_state->tv.hue = 50;
+	}
+	drm_connector_list_iter_end(&conn_iter);
+
+	ret = drm_atomic_commit(state);
+	WARN_ON(ret == -EDEADLK);
+	if (ret)
+		DRM_ERROR("Failed to update properties\n");
+	drm_atomic_state_put(state);
+
+err_unlock:
+	drm_modeset_unlock_all(drm);
+}
+
+static int rockchip_gem_pool_init(struct drm_device *drm)
+{
+	struct rockchip_drm_private *private = drm->dev_private;
+	struct device_node *np = drm->dev->of_node;
+	struct device_node *node;
+	phys_addr_t start, size;
+	struct resource res;
+	int ret;
+
+	node = of_parse_phandle(np, "secure-memory-region", 0);
+	if (!node)
+		return -ENXIO;
+
+	ret = of_address_to_resource(node, 0, &res);
+	if (ret)
+		return ret;
+	start = res.start;
+	size = resource_size(&res);
+	if (!size)
+		return -ENOMEM;
+
+	private->secure_buffer_pool = gen_pool_create(PAGE_SHIFT, -1);
+	if (!private->secure_buffer_pool)
+		return -ENOMEM;
+
+	gen_pool_add(private->secure_buffer_pool, start, size, -1);
+
+	return 0;
+}
+
+static void rockchip_gem_pool_destroy(struct drm_device *drm)
+{
+	struct rockchip_drm_private *private = drm->dev_private;
+
+	if (!private->secure_buffer_pool)
+		return;
+
+	gen_pool_destroy(private->secure_buffer_pool);
+}
+
 static int rockchip_drm_bind(struct device *dev)
 {
 	struct drm_device *drm_dev;
@@ -126,10 +1212,32 @@ static int rockchip_drm_bind(struct device *dev)
 		goto err_free;
 	}
 
+	mutex_init(&private->ovl_lock);
+
 	drm_dev->dev_private = private;
 
 	INIT_LIST_HEAD(&private->psr_list);
 	mutex_init(&private->psr_list_lock);
+	mutex_init(&private->commit_lock);
+
+	private->hdmi_pll.pll = devm_clk_get_optional(dev, "hdmi-tmds-pll");
+	if (PTR_ERR(private->hdmi_pll.pll) == -EPROBE_DEFER) {
+		ret = -EPROBE_DEFER;
+		goto err_free;
+	} else if (IS_ERR(private->hdmi_pll.pll)) {
+		dev_err(dev, "failed to get hdmi-tmds-pll\n");
+		ret = PTR_ERR(private->hdmi_pll.pll);
+		goto err_free;
+	}
+	private->default_pll.pll = devm_clk_get_optional(dev, "default-vop-pll");
+	if (PTR_ERR(private->default_pll.pll) == -EPROBE_DEFER) {
+		ret = -EPROBE_DEFER;
+		goto err_free;
+	} else if (IS_ERR(private->default_pll.pll)) {
+		dev_err(dev, "failed to get default vop pll\n");
+		ret = PTR_ERR(private->default_pll.pll);
+		goto err_free;
+	}
 
 	ret = rockchip_drm_init_iommu(drm_dev);
 	if (ret)
@@ -140,17 +1248,19 @@ static int rockchip_drm_bind(struct device *dev)
 		goto err_iommu_cleanup;
 
 	rockchip_drm_mode_config_init(drm_dev);
-
+	rockchip_drm_create_properties(drm_dev);
 	/* Try to bind all sub drivers. */
 	ret = component_bind_all(dev, drm_dev);
 	if (ret)
-		goto err_iommu_cleanup;
+		goto err_mode_config_cleanup;
 
+	rockchip_attach_connector_property(drm_dev);
 	ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc);
 	if (ret)
 		goto err_unbind_all;
 
 	drm_mode_config_reset(drm_dev);
+	rockchip_drm_set_property_default(drm_dev);
 
 	/*
 	 * enable drm irq mode.
@@ -158,12 +1268,21 @@ static int rockchip_drm_bind(struct device *dev)
 	 */
 	drm_dev->irq_enabled = true;
 
+	/* init kms poll for handling hpd */
+	drm_kms_helper_poll_init(drm_dev);
+
+	rockchip_gem_pool_init(drm_dev);
+	ret = of_reserved_mem_device_init(drm_dev->dev);
+	if (ret)
+		DRM_DEBUG_KMS("No reserved memory region assign to drm\n");
+
+	rockchip_drm_show_logo(drm_dev);
+
 	ret = rockchip_drm_fbdev_init(drm_dev);
 	if (ret)
 		goto err_unbind_all;
 
-	/* init kms poll for handling hpd */
-	drm_kms_helper_poll_init(drm_dev);
+	drm_dev->mode_config.allow_fb_modifiers = true;
 
 	ret = drm_dev_register(drm_dev, 0);
 	if (ret)
@@ -171,13 +1290,18 @@ static int rockchip_drm_bind(struct device *dev)
 
 	return 0;
 err_kms_helper_poll_fini:
+	rockchip_gem_pool_destroy(drm_dev);
 	drm_kms_helper_poll_fini(drm_dev);
 	rockchip_drm_fbdev_fini(drm_dev);
 err_unbind_all:
 	component_unbind_all(dev, drm_dev);
+err_mode_config_cleanup:
+	drm_mode_config_cleanup(drm_dev);
 err_iommu_cleanup:
 	rockchip_iommu_cleanup(drm_dev);
 err_free:
+	drm_dev->dev_private = NULL;
+	dev_set_drvdata(dev, NULL);
 	drm_dev_put(drm_dev);
 	return ret;
 }
@@ -189,15 +1313,121 @@ static void rockchip_drm_unbind(struct device *dev)
 	drm_dev_unregister(drm_dev);
 
 	rockchip_drm_fbdev_fini(drm_dev);
+	rockchip_gem_pool_destroy(drm_dev);
 	drm_kms_helper_poll_fini(drm_dev);
 
 	drm_atomic_helper_shutdown(drm_dev);
 	component_unbind_all(dev, drm_dev);
+	drm_mode_config_cleanup(drm_dev);
 	rockchip_iommu_cleanup(drm_dev);
 
+	drm_dev->dev_private = NULL;
+	dev_set_drvdata(dev, NULL);
 	drm_dev_put(drm_dev);
 }
 
+static void rockchip_drm_crtc_cancel_pending_vblank(struct drm_crtc *crtc,
+						    struct drm_file *file_priv)
+{
+	struct rockchip_drm_private *priv = crtc->dev->dev_private;
+	int pipe = drm_crtc_index(crtc);
+
+	if (pipe < ROCKCHIP_MAX_CRTC &&
+	    priv->crtc_funcs[pipe] &&
+	    priv->crtc_funcs[pipe]->cancel_pending_vblank)
+		priv->crtc_funcs[pipe]->cancel_pending_vblank(crtc, file_priv);
+}
+
+static int rockchip_drm_open(struct drm_device *dev, struct drm_file *file)
+{
+	struct drm_crtc *crtc;
+
+	drm_for_each_crtc(crtc, dev)
+		crtc->primary->fb = NULL;
+
+	return 0;
+}
+
+static void rockchip_drm_postclose(struct drm_device *dev,
+				   struct drm_file *file_priv)
+{
+	struct drm_crtc *crtc;
+
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+		rockchip_drm_crtc_cancel_pending_vblank(crtc, file_priv);
+}
+
+static void rockchip_drm_lastclose(struct drm_device *dev)
+{
+	struct rockchip_drm_private *priv = dev->dev_private;
+
+	if (!priv->logo)
+		drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev_helper);
+}
+
+static struct drm_pending_vblank_event *
+rockchip_drm_add_vcnt_event(struct drm_crtc *crtc, struct drm_file *file_priv)
+{
+	struct drm_pending_vblank_event *e;
+	struct drm_device *dev = crtc->dev;
+	unsigned long flags;
+
+	e = kzalloc(sizeof(*e), GFP_KERNEL);
+	if (!e)
+		return NULL;
+
+	e->pipe = drm_crtc_index(crtc);
+	e->event.base.type = DRM_EVENT_ROCKCHIP_CRTC_VCNT;
+	e->event.base.length = sizeof(e->event.vbl);
+	e->event.vbl.crtc_id = crtc->base.id;
+	/* store crtc pipe id */
+	e->event.vbl.user_data = e->pipe;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	drm_event_reserve_init_locked(dev, file_priv, &e->base, &e->event.base);
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	return e;
+}
+
+static int rockchip_drm_get_vcnt_event_ioctl(struct drm_device *dev, void *data,
+					     struct drm_file *file_priv)
+{
+	struct rockchip_drm_private *priv = dev->dev_private;
+	union drm_wait_vblank *vblwait = data;
+	struct drm_pending_vblank_event *e;
+	struct drm_crtc *crtc;
+	unsigned int flags, pipe;
+
+	flags = vblwait->request.type & (_DRM_VBLANK_FLAGS_MASK | _DRM_ROCKCHIP_VCNT_EVENT);
+	pipe = (vblwait->request.type & _DRM_VBLANK_HIGH_CRTC_MASK);
+	if (pipe)
+		pipe = pipe >> _DRM_VBLANK_HIGH_CRTC_SHIFT;
+	else
+		pipe = flags & _DRM_VBLANK_SECONDARY ? 1 : 0;
+
+	crtc = drm_crtc_from_index(dev, pipe);
+
+	if (flags & _DRM_ROCKCHIP_VCNT_EVENT) {
+		e = rockchip_drm_add_vcnt_event(crtc, file_priv);
+		priv->vcnt[pipe].event = e;
+	}
+
+	return 0;
+}
+
+static const struct drm_ioctl_desc rockchip_ioctls[] = {
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CREATE, rockchip_gem_create_ioctl,
+			  DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MAP_OFFSET,
+			  rockchip_gem_map_offset_ioctl,
+			  DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_GET_PHYS, rockchip_gem_get_phys_ioctl,
+			  DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
+	DRM_IOCTL_DEF_DRV(ROCKCHIP_GET_VCNT_EVENT, rockchip_drm_get_vcnt_event_ioctl,
+			  DRM_UNLOCKED),
+};
+
 static const struct file_operations rockchip_drm_driver_fops = {
 	.owner = THIS_MODULE,
 	.open = drm_open,
@@ -209,19 +1439,160 @@ static const struct file_operations rockchip_drm_driver_fops = {
 	.release = drm_release,
 };
 
+static int rockchip_drm_gem_dmabuf_begin_cpu_access(struct dma_buf *dma_buf,
+						    enum dma_data_direction dir)
+{
+	struct drm_gem_object *obj = dma_buf->priv;
+
+	return rockchip_gem_prime_begin_cpu_access(obj, dir);
+}
+
+static int rockchip_drm_gem_dmabuf_end_cpu_access(struct dma_buf *dma_buf,
+						  enum dma_data_direction dir)
+{
+	struct drm_gem_object *obj = dma_buf->priv;
+
+	return rockchip_gem_prime_end_cpu_access(obj, dir);
+}
+
+static int rockchip_drm_gem_begin_cpu_access_partial(
+	struct dma_buf *dma_buf,
+	enum dma_data_direction dir,
+	unsigned int offset, unsigned int len)
+{
+	struct drm_gem_object *obj = dma_buf->priv;
+
+	return rockchip_gem_prime_begin_cpu_access_partial(obj, dir, offset, len);
+}
+
+static int rockchip_drm_gem_end_cpu_access_partial(
+	struct dma_buf *dma_buf,
+	enum dma_data_direction dir,
+	unsigned int offset, unsigned int len)
+{
+	struct drm_gem_object *obj = dma_buf->priv;
+
+	return rockchip_gem_prime_end_cpu_access_partial(obj, dir, offset, len);
+}
+
+static const struct dma_buf_ops rockchip_drm_gem_prime_dmabuf_ops = {
+	.cache_sgt_mapping = true,
+	.attach = drm_gem_map_attach,
+	.detach = drm_gem_map_detach,
+	.map_dma_buf = drm_gem_map_dma_buf,
+	.unmap_dma_buf = drm_gem_unmap_dma_buf,
+	.release = drm_gem_dmabuf_release,
+	.mmap = drm_gem_dmabuf_mmap,
+	.vmap = drm_gem_dmabuf_vmap,
+	.vunmap = drm_gem_dmabuf_vunmap,
+	.get_uuid = drm_gem_dmabuf_get_uuid,
+	.begin_cpu_access = rockchip_drm_gem_dmabuf_begin_cpu_access,
+	.end_cpu_access = rockchip_drm_gem_dmabuf_end_cpu_access,
+	.begin_cpu_access_partial = rockchip_drm_gem_begin_cpu_access_partial,
+	.end_cpu_access_partial = rockchip_drm_gem_end_cpu_access_partial,
+};
+
+static struct drm_gem_object *rockchip_drm_gem_prime_import_dev(struct drm_device *dev,
+								struct dma_buf *dma_buf,
+								struct device *attach_dev)
+{
+	struct dma_buf_attachment *attach;
+	struct sg_table *sgt;
+	struct drm_gem_object *obj;
+	int ret;
+
+	if (dma_buf->ops == &rockchip_drm_gem_prime_dmabuf_ops) {
+		obj = dma_buf->priv;
+		if (obj->dev == dev) {
+			/*
+			 * Importing dmabuf exported from out own gem increases
+			 * refcount on gem itself instead of f_count of dmabuf.
+			 */
+			drm_gem_object_get(obj);
+			return obj;
+		}
+	}
+
+	if (!dev->driver->gem_prime_import_sg_table)
+		return ERR_PTR(-EINVAL);
+
+	attach = dma_buf_attach(dma_buf, attach_dev);
+	if (IS_ERR(attach))
+		return ERR_CAST(attach);
+
+	get_dma_buf(dma_buf);
+
+	sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
+	if (IS_ERR(sgt)) {
+		ret = PTR_ERR(sgt);
+		goto fail_detach;
+	}
+
+	obj = dev->driver->gem_prime_import_sg_table(dev, attach, sgt);
+	if (IS_ERR(obj)) {
+		ret = PTR_ERR(obj);
+		goto fail_unmap;
+	}
+
+	obj->import_attach = attach;
+	obj->resv = dma_buf->resv;
+
+	return obj;
+
+fail_unmap:
+	dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL);
+fail_detach:
+	dma_buf_detach(dma_buf, attach);
+	dma_buf_put(dma_buf);
+
+	return ERR_PTR(ret);
+}
+
+static struct drm_gem_object *rockchip_drm_gem_prime_import(struct drm_device *dev,
+							    struct dma_buf *dma_buf)
+{
+	return rockchip_drm_gem_prime_import_dev(dev, dma_buf, dev->dev);
+}
+
+static struct dma_buf *rockchip_drm_gem_prime_export(struct drm_gem_object *obj,
+						     int flags)
+{
+	struct drm_device *dev = obj->dev;
+	struct dma_buf_export_info exp_info = {
+		.exp_name = KBUILD_MODNAME, /* white lie for debug */
+		.owner = dev->driver->fops->owner,
+		.ops = &rockchip_drm_gem_prime_dmabuf_ops,
+		.size = obj->size,
+		.flags = flags,
+		.priv = obj,
+		.resv = obj->resv,
+	};
+
+	return drm_gem_dmabuf_export(dev, &exp_info);
+}
+
 static struct drm_driver rockchip_drm_driver = {
-	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
-	.lastclose		= drm_fb_helper_lastclose,
+	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC | DRIVER_RENDER,
+	.postclose		= rockchip_drm_postclose,
+	.lastclose		= rockchip_drm_lastclose,
+	.open			= rockchip_drm_open,
 	.gem_vm_ops		= &drm_gem_cma_vm_ops,
 	.gem_free_object_unlocked = rockchip_gem_free_object,
 	.dumb_create		= rockchip_gem_dumb_create,
 	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
 	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
+	.gem_prime_import	= rockchip_drm_gem_prime_import,
+	.gem_prime_export	= rockchip_drm_gem_prime_export,
 	.gem_prime_get_sg_table	= rockchip_gem_prime_get_sg_table,
 	.gem_prime_import_sg_table	= rockchip_gem_prime_import_sg_table,
 	.gem_prime_vmap		= rockchip_gem_prime_vmap,
 	.gem_prime_vunmap	= rockchip_gem_prime_vunmap,
 	.gem_prime_mmap		= rockchip_gem_mmap_buf,
+#ifdef CONFIG_DEBUG_FS
+	.debugfs_init		= rockchip_drm_debugfs_init,
+#endif
+	.ioctls			= rockchip_ioctls,
+	.num_ioctls		= ARRAY_SIZE(rockchip_ioctls),
 	.fops			= &rockchip_drm_driver_fops,
 	.name	= DRIVER_NAME,
 	.desc	= DRIVER_DESC,
@@ -371,7 +1742,7 @@ static int rockchip_drm_platform_of_probe(struct device *dev)
 		}
 
 		iommu = of_parse_phandle(port->parent, "iommus", 0);
-		if (!iommu || !of_device_is_available(iommu->parent)) {
+		if (!iommu || !of_device_is_available(iommu)) {
 			DRM_DEV_DEBUG(dev,
 				      "no iommu attached for %pOF, using non-iommu buffers\n",
 				      port->parent);
@@ -422,6 +1793,10 @@ static int rockchip_drm_platform_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64));
+	if (ret)
+		return ret;
+
 	return 0;
 }
 
@@ -471,6 +1846,8 @@ static int __init rockchip_drm_init(void)
 
 	num_rockchip_sub_drivers = 0;
 	ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_DRM_ROCKCHIP);
+	ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_DRM_ROCKCHIP);
+	ADD_ROCKCHIP_SUB_DRIVER(vconn_platform_driver, CONFIG_ROCKCHIP_VCONN);
 	ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
 				CONFIG_ROCKCHIP_LVDS);
 	ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
@@ -480,9 +1857,13 @@ static int __init rockchip_drm_init(void)
 				CONFIG_ROCKCHIP_DW_HDMI);
 	ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver,
 				CONFIG_ROCKCHIP_DW_MIPI_DSI);
+	ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi2_rockchip_driver,
+				CONFIG_ROCKCHIP_DW_MIPI_DSI);
 	ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI);
 	ADD_ROCKCHIP_SUB_DRIVER(rk3066_hdmi_driver,
 				CONFIG_ROCKCHIP_RK3066_HDMI);
+	ADD_ROCKCHIP_SUB_DRIVER(rockchip_rgb_driver, CONFIG_ROCKCHIP_RGB);
+	ADD_ROCKCHIP_SUB_DRIVER(dw_dp_driver, CONFIG_ROCKCHIP_DW_DP);
 
 	ret = platform_register_drivers(rockchip_sub_drivers,
 					num_rockchip_sub_drivers);
@@ -493,6 +1874,8 @@ static int __init rockchip_drm_init(void)
 	if (ret)
 		goto err_unreg_drivers;
 
+	rockchip_gem_get_ddr_info();
+
 	return 0;
 
 err_unreg_drivers:
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index e33c2dcd0..591969768 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -9,21 +9,128 @@
 #ifndef _ROCKCHIP_DRM_DRV_H
 #define _ROCKCHIP_DRM_DRV_H
 
-#include <drm/drm_fb_helper.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_dsc.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_fourcc.h>
 #include <drm/drm_gem.h>
-
+#include <uapi/drm/rockchip_drm.h>
 #include <linux/module.h>
 #include <linux/component.h>
 
+#include <soc/rockchip/rockchip_dmc.h>
+
+#include <drm/panel-simple.h>
+
+#include <drm/rockchip_drm_debugfs.h>
+
 #define ROCKCHIP_MAX_FB_BUFFER	3
 #define ROCKCHIP_MAX_CONNECTOR	2
-#define ROCKCHIP_MAX_CRTC	2
+#define ROCKCHIP_MAX_CRTC	4
+#define ROCKCHIP_MAX_LAYER	16
+
 
 struct drm_device;
 struct drm_connector;
 struct iommu_domain;
 
+#define VOP_OUTPUT_IF_RGB	BIT(0)
+#define VOP_OUTPUT_IF_BT1120	BIT(1)
+#define VOP_OUTPUT_IF_BT656	BIT(2)
+#define VOP_OUTPUT_IF_LVDS0	BIT(3)
+#define VOP_OUTPUT_IF_LVDS1	BIT(4)
+#define VOP_OUTPUT_IF_MIPI0	BIT(5)
+#define VOP_OUTPUT_IF_MIPI1	BIT(6)
+#define VOP_OUTPUT_IF_eDP0	BIT(7)
+#define VOP_OUTPUT_IF_eDP1	BIT(8)
+#define VOP_OUTPUT_IF_DP0	BIT(9)
+#define VOP_OUTPUT_IF_DP1	BIT(10)
+#define VOP_OUTPUT_IF_HDMI0	BIT(11)
+#define VOP_OUTPUT_IF_HDMI1	BIT(12)
+
+#ifndef DRM_FORMAT_NV20
+#define DRM_FORMAT_NV20		fourcc_code('N', 'V', '2', '0') /* 2x1 subsampled Cr:Cb plane */
+#endif
+
+#ifndef DRM_FORMAT_NV30
+#define DRM_FORMAT_NV30		fourcc_code('N', 'V', '3', '0') /* non-subsampled Cr:Cb plane */
+#endif
+
+struct rockchip_drm_sub_dev {
+	struct list_head list;
+	struct drm_connector *connector;
+	struct device_node *of_node;
+	void (*loader_protect)(struct drm_encoder *encoder, bool on);
+	void (*oob_hotplug_event)(struct drm_connector *connector);
+};
+
+struct rockchip_sdr2hdr_state {
+	int sdr2hdr_func;
+
+	bool bt1886eotf_pre_conv_en;
+	bool rgb2rgb_pre_conv_en;
+	bool rgb2rgb_pre_conv_mode;
+	bool st2084oetf_pre_conv_en;
+
+	bool bt1886eotf_post_conv_en;
+	bool rgb2rgb_post_conv_en;
+	bool rgb2rgb_post_conv_mode;
+	bool st2084oetf_post_conv_en;
+};
+
+struct rockchip_hdr_state {
+	bool pre_overlay;
+	bool hdr2sdr_en;
+	struct rockchip_sdr2hdr_state sdr2hdr_state;
+};
+
+struct rockchip_bcsh_state {
+	int brightness;
+	int contrast;
+	int saturation;
+	int sin_hue;
+	int cos_hue;
+};
+
+struct rockchip_crtc {
+	struct drm_crtc crtc;
+#if defined(CONFIG_ROCKCHIP_DRM_DEBUG)
+	/**
+	 * @vop_dump_status the status of vop dump control
+	 * @vop_dump_list_head the list head of vop dump list
+	 * @vop_dump_list_init_flag init once
+	 * @vop_dump_times control the dump times
+	 * @frme_count the frame of dump buf
+	 */
+	enum vop_dump_status vop_dump_status;
+	struct list_head vop_dump_list_head;
+	bool vop_dump_list_init_flag;
+	int vop_dump_times;
+	int frame_count;
+#endif
+};
+
+struct rockchip_dsc_sink_cap {
+	/**
+	 * @slice_width: the number of pixel columns that comprise the slice width
+	 * @slice_height: the number of pixel rows that comprise the slice height
+	 * @block_pred: Does block prediction
+	 * @native_420: Does sink support DSC with 4:2:0 compression
+	 * @bpc_supported: compressed bpc supported by sink : 10, 12 or 16 bpc
+	 * @version_major: DSC major version
+	 * @version_minor: DSC minor version
+	 * @target_bits_per_pixel_x16: bits num after compress and multiply 16
+	 */
+	u16 slice_width;
+	u16 slice_height;
+	bool block_pred;
+	bool native_420;
+	u8 bpc_supported;
+	u8 version_major;
+	u8 version_minor;
+	u16 target_bits_per_pixel_x16;
+};
+
 struct rockchip_crtc_state {
 	struct drm_crtc_state base;
 	int output_type;
@@ -31,10 +138,206 @@ struct rockchip_crtc_state {
 	int output_bpc;
 	int output_flags;
 	bool enable_afbc;
+	/**
+	 * @splice_mode: enabled when display a hdisplay > 4096 on rk3588
+	 */
+	bool splice_mode;
+
+	/**
+	 * @hold_mode: enabled when it's:
+	 * (1) mcu hold mode
+	 * (2) mipi dsi cmd mode
+	 * (3) edp psr mode
+	 */
+	bool hold_mode;
+
+	struct drm_tv_connector_state *tv_state;
+	int left_margin;
+	int right_margin;
+	int top_margin;
+	int bottom_margin;
+	int vdisplay;
+	int afbdc_win_format;
+	int afbdc_win_width;
+	int afbdc_win_height;
+	int afbdc_win_ptr;
+	int afbdc_win_id;
+	int afbdc_en;
+	int afbdc_win_vir_width;
+	int afbdc_win_xoffset;
+	int afbdc_win_yoffset;
+	int dsp_layer_sel;
+	u32 output_if;
+	u32 bus_format;
+	u32 bus_flags;
+	int yuv_overlay;
+	int post_r2y_en;
+	int post_y2r_en;
+	int post_csc_mode;
+	int bcsh_en;
+	int color_space;
+	int eotf;
+	u32 background;
+	u32 line_flag;
+	u8 mode_update;
+	u8 dsc_id;
+	u8 dsc_enable;
+
+	u8 dsc_slice_num;
+	u8 dsc_pixel_num;
+
+	u64 dsc_txp_clk_rate;
+	u64 dsc_pxl_clk_rate;
+	u64 dsc_cds_clk_rate;
+
+	struct drm_dsc_picture_parameter_set pps;
+	struct rockchip_dsc_sink_cap dsc_sink_cap;
+	struct rockchip_hdr_state hdr;
 };
+
 #define to_rockchip_crtc_state(s) \
 		container_of(s, struct rockchip_crtc_state, base)
 
+struct rockchip_drm_vcnt {
+	struct drm_pending_vblank_event *event;
+	__u32 sequence;
+	int pipe;
+};
+
+struct rockchip_logo {
+	dma_addr_t dma_addr;
+	void *kvaddr;
+	phys_addr_t start;
+	phys_addr_t size;
+	int count;
+};
+
+struct loader_cubic_lut {
+	bool enable;
+	u32 offset;
+};
+
+struct rockchip_drm_dsc_cap {
+	bool v_1p2;
+	bool native_420;
+	bool all_bpp;
+	u8 bpc_supported;
+	u8 max_slices;
+	u8 max_lanes;
+	u8 max_frl_rate_per_lane;
+	u8 total_chunk_kbytes;
+	int clk_per_slice;
+};
+
+struct ver_26_v0 {
+	u8 yuv422_12bit;
+	u8 support_2160p_60;
+	u8 global_dimming;
+	u8 dm_major_ver;
+	u8 dm_minor_ver;
+	u16 t_min_pq;
+	u16 t_max_pq;
+	u16 rx;
+	u16 ry;
+	u16 gx;
+	u16 gy;
+	u16 bx;
+	u16 by;
+	u16 wx;
+	u16 wy;
+} __packed;
+
+struct ver_15_v1 {
+	u8 yuv422_12bit;
+	u8 support_2160p_60;
+	u8 global_dimming;
+	u8 dm_version;
+	u8 colorimetry;
+	u8 t_max_lum;
+	u8 t_min_lum;
+	u8 rx;
+	u8 ry;
+	u8 gx;
+	u8 gy;
+	u8 bx;
+	u8 by;
+} __packed;
+
+struct ver_12_v1 {
+	u8 yuv422_12bit;
+	u8 support_2160p_60;
+	u8 global_dimming;
+	u8 dm_version;
+	u8 colorimetry;
+	u8 low_latency;
+	u8 t_max_lum;
+	u8 t_min_lum;
+	u8 unique_rx;
+	u8 unique_ry;
+	u8 unique_gx;
+	u8 unique_gy;
+	u8 unique_bx;
+	u8 unique_by;
+} __packed;
+
+struct ver_12_v2 {
+	u8 yuv422_12bit;
+	u8 backlt_ctrl;
+	u8 global_dimming;
+	u8 dm_version;
+	u8 backlt_min_luma;
+	u8 interface;
+	u8 yuv444_10b_12b;
+	u8 t_min_pq_v2;
+	u8 t_max_pq_v2;
+	u8 unique_rx;
+	u8 unique_ry;
+	u8 unique_gx;
+	u8 unique_gy;
+	u8 unique_bx;
+	u8 unique_by;
+} __packed;
+
+struct next_hdr_sink_data {
+	u8 version;
+	struct ver_26_v0 ver_26_v0;
+	struct ver_15_v1 ver_15_v1;
+	struct ver_12_v1 ver_12_v1;
+	struct ver_12_v2 ver_12_v2;
+} __packed;
+
+/*
+ * Rockchip drm private crtc funcs.
+ * @loader_protect: protect loader logo crtc's power
+ * @enable_vblank: enable crtc vblank irq.
+ * @disable_vblank: disable crtc vblank irq.
+ * @bandwidth: report present crtc bandwidth consume.
+ */
+struct rockchip_crtc_funcs {
+	int (*loader_protect)(struct drm_crtc *crtc, bool on);
+	int (*enable_vblank)(struct drm_crtc *crtc);
+	void (*disable_vblank)(struct drm_crtc *crtc);
+	size_t (*bandwidth)(struct drm_crtc *crtc,
+			    struct drm_crtc_state *crtc_state,
+			    struct dmcfreq_vop_info *vop_bw_info);
+	void (*cancel_pending_vblank)(struct drm_crtc *crtc,
+				      struct drm_file *file_priv);
+	int (*debugfs_init)(struct drm_minor *minor, struct drm_crtc *crtc);
+	int (*debugfs_dump)(struct drm_crtc *crtc, struct seq_file *s);
+	void (*regs_dump)(struct drm_crtc *crtc, struct seq_file *s);
+	enum drm_mode_status (*mode_valid)(struct drm_crtc *crtc,
+					   const struct drm_display_mode *mode,
+					   int output_type);
+	void (*crtc_close)(struct drm_crtc *crtc);
+	void (*crtc_send_mcu_cmd)(struct drm_crtc *crtc, u32 type, u32 value);
+	void (*te_handler)(struct drm_crtc *crtc);
+};
+
+struct rockchip_dclk_pll {
+	struct clk *pll;
+	unsigned int use_count;
+};
+
 /*
  * Rockchip drm private structure.
  *
@@ -43,13 +346,54 @@ struct rockchip_crtc_state {
  * @mm_lock: protect drm_mm on multi-threads.
  */
 struct rockchip_drm_private {
-	struct drm_fb_helper fbdev_helper;
+	struct rockchip_logo *logo;
+	struct drm_fb_helper *fbdev_helper;
 	struct drm_gem_object *fbdev_bo;
 	struct iommu_domain *domain;
+	struct gen_pool *secure_buffer_pool;
 	struct mutex mm_lock;
 	struct drm_mm mm;
 	struct list_head psr_list;
 	struct mutex psr_list_lock;
+	struct mutex commit_lock;
+
+	/* private crtc prop */
+	struct drm_property *soc_id_prop;
+	struct drm_property *port_id_prop;
+	struct drm_property *aclk_prop;
+	struct drm_property *bg_prop;
+	struct drm_property *line_flag_prop;
+
+	/* private plane prop */
+	struct drm_property *eotf_prop;
+	struct drm_property *color_space_prop;
+	struct drm_property *async_commit_prop;
+	struct drm_property *share_id_prop;
+
+	/* private connector prop */
+	struct drm_property *connector_id_prop;
+
+	const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC];
+
+	struct rockchip_dclk_pll default_pll;
+	struct rockchip_dclk_pll hdmi_pll;
+
+	/*
+	 * protect some shared overlay resource
+	 * OVL_LAYER_SEL/OVL_PORT_SEL
+	 */
+	struct mutex ovl_lock;
+
+	struct rockchip_drm_vcnt vcnt[ROCKCHIP_MAX_CRTC];
+	/**
+	 * @loader_protect
+	 * ignore restore_fbdev_mode_atomic when in logo on state
+	 */
+	bool loader_protect;
+
+	dma_addr_t cubic_lut_dma_addr;
+	void *cubic_lut_kvaddr;
+	struct loader_cubic_lut cubic_lut[ROCKCHIP_MAX_CRTC];
 };
 
 int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
@@ -57,14 +401,49 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
 void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
 				    struct device *dev);
 int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout);
+int rockchip_register_crtc_funcs(struct drm_crtc *crtc,
+				 const struct rockchip_crtc_funcs *crtc_funcs);
+void rockchip_unregister_crtc_funcs(struct drm_crtc *crtc);
+
+void rockchip_drm_register_sub_dev(struct rockchip_drm_sub_dev *sub_dev);
+void rockchip_drm_unregister_sub_dev(struct rockchip_drm_sub_dev *sub_dev);
+struct rockchip_drm_sub_dev *rockchip_drm_get_sub_dev(struct device_node *node);
+int rockchip_drm_add_modes_noedid(struct drm_connector *connector);
+void rockchip_drm_te_handle(struct drm_crtc *crtc);
+void drm_mode_convert_to_split_mode(struct drm_display_mode *mode);
+void drm_mode_convert_to_origin_mode(struct drm_display_mode *mode);
+#if IS_ENABLED(CONFIG_DRM_ROCKCHIP)
+int rockchip_drm_get_sub_dev_type(void);
+#else
+static inline int rockchip_drm_get_sub_dev_type(void)
+{
+	return DRM_MODE_CONNECTOR_Unknown;
+}
+#endif
 
 int rockchip_drm_endpoint_is_subdriver(struct device_node *ep);
+uint32_t rockchip_drm_of_find_possible_crtcs(struct drm_device *dev,
+					     struct device_node *port);
+uint32_t rockchip_drm_get_bpp(const struct drm_format_info *info);
+int rockchip_drm_get_yuv422_format(struct drm_connector *connector,
+				   struct edid *edid);
+int rockchip_drm_parse_cea_ext(struct rockchip_drm_dsc_cap *dsc_cap,
+			       u8 *max_frl_rate_per_lane, u8 *max_lanes,
+			       const struct edid *edid);
+int rockchip_drm_parse_next_hdr(struct next_hdr_sink_data *sink_data,
+				const struct edid *edid);
+
 extern struct platform_driver cdn_dp_driver;
 extern struct platform_driver dw_hdmi_rockchip_pltfm_driver;
 extern struct platform_driver dw_mipi_dsi_rockchip_driver;
+extern struct platform_driver dw_mipi_dsi2_rockchip_driver;
 extern struct platform_driver inno_hdmi_driver;
 extern struct platform_driver rockchip_dp_driver;
 extern struct platform_driver rockchip_lvds_driver;
 extern struct platform_driver vop_platform_driver;
+extern struct platform_driver vop2_platform_driver;
 extern struct platform_driver rk3066_hdmi_driver;
+extern struct platform_driver rockchip_rgb_driver;
+extern struct platform_driver dw_dp_driver;
+extern struct platform_driver vconn_platform_driver;
 #endif /* _ROCKCHIP_DRM_DRV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index 3aa37e177..5658fe507 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -5,6 +5,7 @@
  */
 
 #include <linux/kernel.h>
+#include <linux/devfreq.h>
 
 #include <drm/drm.h>
 #include <drm/drm_atomic.h>
@@ -13,13 +14,43 @@
 #include <drm/drm_fourcc.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_probe_helper.h>
+#include <soc/rockchip/rockchip_dmc.h>
 
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_fb.h"
 #include "rockchip_drm_gem.h"
+#include <drm/rockchip_drm_logo.h>
+
+static bool is_rockchip_logo_fb(struct drm_framebuffer *fb)
+{
+	return fb->flags & ROCKCHIP_DRM_MODE_LOGO_FB ? true : false;
+}
+
+static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
+{
+	int i = 0;
+
+	drm_framebuffer_cleanup(fb);
+
+	if (is_rockchip_logo_fb(fb)) {
+		struct rockchip_drm_logo_fb *rockchip_logo_fb = to_rockchip_logo_fb(fb);
+
+#ifndef MODULE
+		rockchip_free_loader_memory(fb->dev);
+#endif
+		kfree(rockchip_logo_fb);
+	} else {
+		for (i = 0; i < 4; i++) {
+			if (fb->obj[i])
+				drm_gem_object_put(fb->obj[i]);
+		}
+
+		kfree(fb);
+	}
+}
 
 static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
-	.destroy       = drm_gem_fb_destroy,
+	.destroy       = rockchip_drm_fb_destroy,
 	.create_handle = drm_gem_fb_create_handle,
 	.dirty	       = drm_atomic_helper_dirtyfb,
 };
@@ -53,8 +84,103 @@ rockchip_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cm
 	return fb;
 }
 
+struct drm_framebuffer *
+rockchip_drm_logo_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd,
+			   struct rockchip_logo *logo)
+{
+	int ret = 0;
+	struct rockchip_drm_logo_fb *rockchip_logo_fb;
+	struct drm_framebuffer *fb;
+
+	rockchip_logo_fb = kzalloc(sizeof(*rockchip_logo_fb), GFP_KERNEL);
+	if (!rockchip_logo_fb)
+		return ERR_PTR(-ENOMEM);
+	fb = &rockchip_logo_fb->fb;
+
+	drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
+
+	ret = drm_framebuffer_init(dev, fb, &rockchip_drm_fb_funcs);
+	if (ret) {
+		DRM_DEV_ERROR(dev->dev,
+			      "Failed to initialize rockchip logo fb: %d\n",
+			      ret);
+		kfree(rockchip_logo_fb);
+		return ERR_PTR(ret);
+	}
+
+	fb->flags |= ROCKCHIP_DRM_MODE_LOGO_FB;
+	rockchip_logo_fb->logo = logo;
+	rockchip_logo_fb->fb.obj[0] = &rockchip_logo_fb->rk_obj.base;
+	rockchip_logo_fb->rk_obj.dma_addr = logo->dma_addr;
+	rockchip_logo_fb->rk_obj.kvaddr = logo->kvaddr;
+	logo->count++;
+
+	return &rockchip_logo_fb->fb;
+}
+
+static int rockchip_drm_bandwidth_atomic_check(struct drm_device *dev,
+					       struct drm_atomic_state *state,
+					       struct dmcfreq_vop_info *vop_bw_info)
+{
+	struct rockchip_drm_private *priv = dev->dev_private;
+	struct drm_crtc_state *old_crtc_state;
+	const struct rockchip_crtc_funcs *funcs;
+	struct drm_crtc *crtc;
+	int i;
+
+	vop_bw_info->line_bw_mbyte = 0;
+	vop_bw_info->frame_bw_mbyte = 0;
+	vop_bw_info->plane_num = 0;
+
+	for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) {
+		funcs = priv->crtc_funcs[drm_crtc_index(crtc)];
+
+		if (funcs && funcs->bandwidth)
+			funcs->bandwidth(crtc, old_crtc_state, vop_bw_info);
+	}
+
+	return 0;
+}
+
+/**
+ * rockchip_drm_atomic_helper_commit_tail_rpm - commit atomic update to hardware
+ * @old_state: new modeset state to be committed
+ *
+ * This is an alternative implementation for the
+ * &drm_mode_config_helper_funcs.atomic_commit_tail hook, for drivers
+ * that support runtime_pm or need the CRTC to be enabled to perform a
+ * commit. Otherwise, one should use the default implementation
+ * drm_atomic_helper_commit_tail().
+ */
+static void rockchip_drm_atomic_helper_commit_tail_rpm(struct drm_atomic_state *old_state)
+{
+	struct drm_device *dev = old_state->dev;
+	struct rockchip_drm_private *prv = dev->dev_private;
+	struct dmcfreq_vop_info vop_bw_info;
+
+	drm_atomic_helper_commit_modeset_disables(dev, old_state);
+
+	drm_atomic_helper_commit_modeset_enables(dev, old_state);
+
+	rockchip_drm_bandwidth_atomic_check(dev, old_state, &vop_bw_info);
+
+	rockchip_dmcfreq_vop_bandwidth_update(&vop_bw_info);
+
+	mutex_lock(&prv->ovl_lock);
+	drm_atomic_helper_commit_planes(dev, old_state, DRM_PLANE_COMMIT_ACTIVE_ONLY);
+	mutex_unlock(&prv->ovl_lock);
+
+	drm_atomic_helper_fake_vblank(old_state);
+
+	drm_atomic_helper_commit_hw_done(old_state);
+
+	drm_atomic_helper_wait_for_vblanks(dev, old_state);
+
+	drm_atomic_helper_cleanup_planes(dev, old_state);
+}
+
 static const struct drm_mode_config_helper_funcs rockchip_mode_config_helpers = {
-	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
+	.atomic_commit_tail = rockchip_drm_atomic_helper_commit_tail_rpm,
 };
 
 static struct drm_framebuffer *
@@ -81,7 +207,7 @@ rockchip_fb_create(struct drm_device *dev, struct drm_file *file,
 	}
 
 	if (drm_is_afbc(mode_cmd->modifier[0])) {
-		int ret, i;
+		int i;
 
 		ret = drm_gem_fb_afbc_init(dev, mode_cmd, afbc_fb);
 		if (ret) {
@@ -98,9 +224,18 @@ rockchip_fb_create(struct drm_device *dev, struct drm_file *file,
 	return &afbc_fb->base;
 }
 
+static void rockchip_drm_output_poll_changed(struct drm_device *dev)
+{
+	struct rockchip_drm_private *private = dev->dev_private;
+	struct drm_fb_helper *fb_helper = private->fbdev_helper;
+
+	if (fb_helper && dev->mode_config.poll_enabled && !private->loader_protect)
+		drm_fb_helper_hotplug_event(fb_helper);
+}
+
 static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
 	.fb_create = rockchip_fb_create,
-	.output_poll_changed = drm_fb_helper_output_poll_changed,
+	.output_poll_changed = rockchip_drm_output_poll_changed,
 	.atomic_check = drm_atomic_helper_check,
 	.atomic_commit = drm_atomic_helper_commit,
 };
@@ -125,12 +260,13 @@ void rockchip_drm_mode_config_init(struct drm_device *dev)
 	dev->mode_config.min_height = 0;
 
 	/*
-	 * set max width and height as default value(4096x4096).
+	 * set max width and height as default value(16384x16384).
 	 * this value would be used to check framebuffer size limitation
 	 * at drm_mode_addfb().
 	 */
-	dev->mode_config.max_width = 4096;
-	dev->mode_config.max_height = 4096;
+	dev->mode_config.max_width = 16384;
+	dev->mode_config.max_height = 16384;
+	dev->mode_config.async_page_flip = true;
 
 	dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
 	dev->mode_config.helper_private = &rockchip_mode_config_helpers;
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
index 1a6965210..3b8755e9f 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h
@@ -7,6 +7,10 @@
 #ifndef _ROCKCHIP_DRM_FB_H
 #define _ROCKCHIP_DRM_FB_H
 
+#include "rockchip_drm_gem.h"
+
+#define ROCKCHIP_DRM_MODE_LOGO_FB	(1<<31) /* used for kernel logo, follow the define: DRM_MODE_FB_MODIFIERS at drm_mode.h */
+
 struct drm_framebuffer *
 rockchip_drm_framebuffer_init(struct drm_device *dev,
 			      const struct drm_mode_fb_cmd2 *mode_cmd,
@@ -14,4 +18,16 @@ rockchip_drm_framebuffer_init(struct drm_device *dev,
 void rockchip_drm_framebuffer_fini(struct drm_framebuffer *fb);
 
 void rockchip_drm_mode_config_init(struct drm_device *dev);
+struct drm_framebuffer *
+rockchip_drm_logo_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd,
+			   struct rockchip_logo *logo);
+
+#define to_rockchip_logo_fb(x) container_of(x, struct rockchip_drm_logo_fb, fb)
+
+struct rockchip_drm_logo_fb {
+	struct drm_framebuffer fb;
+	struct rockchip_logo *logo;
+	struct rockchip_gem_object rk_obj;
+};
+
 #endif /* _ROCKCHIP_DRM_FB_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
index 2fdc455c4..065fa7fde 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
@@ -15,14 +15,12 @@
 #include "rockchip_drm_fbdev.h"
 
 #define PREFERRED_BPP		32
-#define to_drm_private(x) \
-		container_of(x, struct rockchip_drm_private, fbdev_helper)
 
 static int rockchip_fbdev_mmap(struct fb_info *info,
 			       struct vm_area_struct *vma)
 {
 	struct drm_fb_helper *helper = info->par;
-	struct rockchip_drm_private *private = to_drm_private(helper);
+	struct rockchip_drm_private *private = helper->dev->dev_private;
 
 	return rockchip_gem_mmap_buf(private->fbdev_bo, vma);
 }
@@ -39,7 +37,7 @@ static const struct fb_ops rockchip_drm_fbdev_ops = {
 static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,
 				     struct drm_fb_helper_surface_size *sizes)
 {
-	struct rockchip_drm_private *private = to_drm_private(helper);
+	struct rockchip_drm_private *private = helper->dev->dev_private;
 	struct drm_mode_fb_cmd2 mode_cmd = { 0 };
 	struct drm_device *dev = helper->dev;
 	struct rockchip_gem_object *rk_obj;
@@ -60,7 +58,7 @@ static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper,
 
 	size = mode_cmd.pitches[0] * mode_cmd.height;
 
-	rk_obj = rockchip_gem_create_object(dev, size, true);
+	rk_obj = rockchip_gem_create_object(dev, size, true, 0);
 	if (IS_ERR(rk_obj))
 		return -ENOMEM;
 
@@ -120,7 +118,10 @@ int rockchip_drm_fbdev_init(struct drm_device *dev)
 	if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
 		return -EINVAL;
 
-	helper = &private->fbdev_helper;
+	helper = devm_kzalloc(dev->dev, sizeof(*helper), GFP_KERNEL);
+	if (!helper)
+		return -ENOMEM;
+	private->fbdev_helper = helper;
 
 	drm_fb_helper_prepare(dev, helper, &rockchip_drm_fb_helper_funcs);
 
@@ -150,9 +151,10 @@ int rockchip_drm_fbdev_init(struct drm_device *dev)
 void rockchip_drm_fbdev_fini(struct drm_device *dev)
 {
 	struct rockchip_drm_private *private = dev->dev_private;
-	struct drm_fb_helper *helper;
+	struct drm_fb_helper *helper = private->fbdev_helper;
 
-	helper = &private->fbdev_helper;
+	if (!helper)
+		return;
 
 	drm_fb_helper_unregister_fbi(helper);
 
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
index 62e5d0970..51dead7f3 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
@@ -4,7 +4,7 @@
  * Author:Mark Yao <mark.yao@rock-chips.com>
  */
 
-#include <linux/dma-buf.h>
+#include <linux/dma-buf-cache.h>
 #include <linux/iommu.h>
 #include <linux/vmalloc.h>
 
@@ -13,9 +13,25 @@
 #include <drm/drm_prime.h>
 #include <drm/drm_vma_manager.h>
 
+#include <linux/genalloc.h>
+#include <linux/iommu.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include <linux/rockchip/rockchip_sip.h>
+
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_gem.h"
 
+static u32 bank_bit_first = 12;
+static u32 bank_bit_mask = 0x7;
+
+struct page_info {
+	struct page *page;
+	struct list_head list;
+};
+
+#define PG_ROUND       8
+
 static int rockchip_gem_iommu_map(struct rockchip_gem_object *rk_obj)
 {
 	struct drm_device *drm = rk_obj->base.dev;
@@ -45,6 +61,8 @@ static int rockchip_gem_iommu_map(struct rockchip_gem_object *rk_obj)
 		goto err_remove_node;
 	}
 
+	iommu_flush_iotlb_all(private->domain);
+
 	rk_obj->size = ret;
 
 	return 0;
@@ -73,25 +91,137 @@ static int rockchip_gem_iommu_unmap(struct rockchip_gem_object *rk_obj)
 	return 0;
 }
 
+static void rockchip_gem_free_list(struct list_head lists[])
+{
+	struct page_info *info, *tmp_info;
+	int i;
+
+	for (i = 0; i < PG_ROUND; i++) {
+		list_for_each_entry_safe(info, tmp_info, &lists[i], list) {
+			list_del(&info->list);
+			kfree(info);
+		}
+	}
+}
+
+void rockchip_gem_get_ddr_info(void)
+{
+	struct dram_addrmap_info *ddr_map_info;
+
+	ddr_map_info = sip_smc_get_dram_map();
+	if (ddr_map_info) {
+		bank_bit_first = ddr_map_info->bank_bit_first;
+		bank_bit_mask = ddr_map_info->bank_bit_mask;
+	}
+}
+
 static int rockchip_gem_get_pages(struct rockchip_gem_object *rk_obj)
 {
 	struct drm_device *drm = rk_obj->base.dev;
 	int ret, i;
 	struct scatterlist *s;
-
-	rk_obj->pages = drm_gem_get_pages(&rk_obj->base);
-	if (IS_ERR(rk_obj->pages))
-		return PTR_ERR(rk_obj->pages);
+	unsigned int cur_page;
+	struct page **pages, **dst_pages;
+	int j;
+	int n_pages;
+	unsigned long chunk_pages;
+	unsigned long remain;
+	struct list_head lists[PG_ROUND];
+	dma_addr_t phys;
+	int end = 0;
+	unsigned int bit_index;
+	unsigned int block_index[PG_ROUND] = {0};
+	struct page_info *info;
+	unsigned int maximum;
+
+	for (i = 0; i < PG_ROUND; i++)
+		INIT_LIST_HEAD(&lists[i]);
+
+	pages = drm_gem_get_pages(&rk_obj->base);
+	if (IS_ERR(pages))
+		return PTR_ERR(pages);
+
+	rk_obj->pages = pages;
 
 	rk_obj->num_pages = rk_obj->base.size >> PAGE_SHIFT;
 
+	n_pages = rk_obj->num_pages;
+
+	dst_pages = __vmalloc(sizeof(struct page *) * n_pages,
+			GFP_KERNEL | __GFP_HIGHMEM);
+	if (!dst_pages) {
+		ret = -ENOMEM;
+		goto err_put_pages;
+	}
+
+	DRM_DEBUG_KMS("bank_bit_first = 0x%x, bank_bit_mask = 0x%x\n",
+		      bank_bit_first, bank_bit_mask);
+
+	cur_page = 0;
+	remain = n_pages;
+	/* look for the end of the current chunk */
+	while (remain) {
+		for (j = cur_page + 1; j < n_pages; ++j) {
+			if (page_to_pfn(pages[j]) !=
+				page_to_pfn(pages[j - 1]) + 1)
+				break;
+		}
+
+		chunk_pages = j - cur_page;
+		if (chunk_pages >= PG_ROUND) {
+			for (i = 0; i < chunk_pages; i++)
+				dst_pages[end + i] = pages[cur_page + i];
+			end += chunk_pages;
+		} else {
+			for (i = 0; i < chunk_pages; i++) {
+				info = kmalloc(sizeof(*info), GFP_KERNEL);
+				if (!info) {
+					ret = -ENOMEM;
+					goto err_put_list;
+				}
+
+				INIT_LIST_HEAD(&info->list);
+				info->page = pages[cur_page + i];
+				phys = page_to_phys(info->page);
+				bit_index = ((phys >> bank_bit_first) & bank_bit_mask) % PG_ROUND;
+				list_add_tail(&info->list, &lists[bit_index]);
+				block_index[bit_index]++;
+			}
+		}
+
+		cur_page = j;
+		remain -= chunk_pages;
+	}
+
+	maximum = block_index[0];
+	for (i = 1; i < PG_ROUND; i++)
+		maximum = max(maximum, block_index[i]);
+
+	for (i = 0; i < maximum; i++) {
+		for (j = 0; j < PG_ROUND; j++) {
+			if (!list_empty(&lists[j])) {
+				struct page_info *info;
+
+				info = list_first_entry(&lists[j],
+							struct page_info, list);
+				dst_pages[end++] = info->page;
+				list_del(&info->list);
+				kfree(info);
+			}
+		}
+	}
+
+	DRM_DEBUG_KMS("%s, %d, end = %d, n_pages = %d\n", __func__, __LINE__,
+			end, n_pages);
 	rk_obj->sgt = drm_prime_pages_to_sg(rk_obj->base.dev,
-					    rk_obj->pages, rk_obj->num_pages);
+					    dst_pages, rk_obj->num_pages);
 	if (IS_ERR(rk_obj->sgt)) {
 		ret = PTR_ERR(rk_obj->sgt);
-		goto err_put_pages;
+		goto err_put_list;
 	}
 
+	rk_obj->pages = dst_pages;
+
 	/*
 	 * Fake up the SG table so that dma_sync_sg_for_device() can be used
 	 * to flush the pages associated with it.
@@ -104,8 +234,13 @@ static int rockchip_gem_get_pages(struct rockchip_gem_object *rk_obj)
 
 	dma_sync_sgtable_for_device(drm->dev, rk_obj->sgt, DMA_TO_DEVICE);
 
+	kvfree(pages);
+
 	return 0;
 
+err_put_list:
+	rockchip_gem_free_list(lists);
+	kvfree(dst_pages);
 err_put_pages:
 	drm_gem_put_pages(&rk_obj->base, rk_obj->pages, false, false);
 	return ret;
@@ -118,59 +253,164 @@ static void rockchip_gem_put_pages(struct rockchip_gem_object *rk_obj)
 	drm_gem_put_pages(&rk_obj->base, rk_obj->pages, true, true);
 }
 
-static int rockchip_gem_alloc_iommu(struct rockchip_gem_object *rk_obj,
-				    bool alloc_kmap)
+static inline void *drm_calloc_large(size_t nmemb, size_t size);
+static inline void drm_free_large(void *ptr);
+static void rockchip_gem_free_dma(struct rockchip_gem_object *rk_obj);
+static int rockchip_gem_alloc_dma(struct rockchip_gem_object *rk_obj,
+				  bool alloc_kmap)
 {
-	int ret;
+	struct drm_gem_object *obj = &rk_obj->base;
+	struct drm_device *drm = obj->dev;
+	struct sg_table *sgt;
+	int ret, i;
+	struct scatterlist *s;
 
-	ret = rockchip_gem_get_pages(rk_obj);
-	if (ret < 0)
-		return ret;
+	rk_obj->dma_attrs = DMA_ATTR_WRITE_COMBINE;
 
-	ret = rockchip_gem_iommu_map(rk_obj);
-	if (ret < 0)
-		goto err_free;
-
-	if (alloc_kmap) {
-		rk_obj->kvaddr = vmap(rk_obj->pages, rk_obj->num_pages, VM_MAP,
-				      pgprot_writecombine(PAGE_KERNEL));
-		if (!rk_obj->kvaddr) {
-			DRM_ERROR("failed to vmap() buffer\n");
-			ret = -ENOMEM;
-			goto err_unmap;
-		}
+	if (!alloc_kmap)
+		rk_obj->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
+
+	rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size,
+					 &rk_obj->dma_handle, GFP_KERNEL,
+					 rk_obj->dma_attrs);
+	if (!rk_obj->kvaddr) {
+		DRM_ERROR("failed to allocate %zu byte dma buffer", obj->size);
+		return -ENOMEM;
+	}
+
+	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
+	if (!sgt) {
+		ret = -ENOMEM;
+		goto err_dma_free;
 	}
 
+	ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr,
+				    rk_obj->dma_handle, obj->size,
+				    rk_obj->dma_attrs);
+	if (ret) {
+		DRM_ERROR("failed to allocate sgt, %d\n", ret);
+		goto err_sgt_free;
+	}
+
+	for_each_sg(sgt->sgl, s, sgt->nents, i)
+		sg_dma_address(s) = sg_phys(s);
+
+	rk_obj->num_pages = rk_obj->base.size >> PAGE_SHIFT;
+
+	rk_obj->pages = drm_calloc_large(rk_obj->num_pages,
+					 sizeof(*rk_obj->pages));
+	if (!rk_obj->pages) {
+		DRM_ERROR("failed to allocate pages.\n");
+		goto err_sg_table_free;
+	}
+
+	if (drm_prime_sg_to_page_addr_arrays(sgt, rk_obj->pages, NULL,
+					     rk_obj->num_pages)) {
+		DRM_ERROR("invalid sgtable.\n");
+		ret = -EINVAL;
+		goto err_page_free;
+	}
+
+	rk_obj->sgt = sgt;
+
 	return 0;
 
-err_unmap:
-	rockchip_gem_iommu_unmap(rk_obj);
-err_free:
-	rockchip_gem_put_pages(rk_obj);
+err_page_free:
+	drm_free_large(rk_obj->pages);
+err_sg_table_free:
+	sg_free_table(sgt);
+err_sgt_free:
+	kfree(sgt);
+err_dma_free:
+	dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr,
+		       rk_obj->dma_handle, rk_obj->dma_attrs);
 
 	return ret;
 }
 
-static int rockchip_gem_alloc_dma(struct rockchip_gem_object *rk_obj,
-				  bool alloc_kmap)
+static inline void *drm_calloc_large(size_t nmemb, size_t size)
+{
+	if (size != 0 && nmemb > SIZE_MAX / size)
+		return NULL;
+
+	if (size * nmemb <= PAGE_SIZE)
+		return kcalloc(nmemb, size, GFP_KERNEL);
+
+	return __vmalloc(size * nmemb,
+			 GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
+}
+
+static inline void drm_free_large(void *ptr)
+{
+	kvfree(ptr);
+}
+
+static int rockchip_gem_alloc_secure(struct rockchip_gem_object *rk_obj)
 {
 	struct drm_gem_object *obj = &rk_obj->base;
 	struct drm_device *drm = obj->dev;
+	struct rockchip_drm_private *private = drm->dev_private;
+	unsigned long paddr;
+	struct sg_table *sgt;
+	int ret = 0, i;
 
-	rk_obj->dma_attrs = DMA_ATTR_WRITE_COMBINE;
-
-	if (!alloc_kmap)
-		rk_obj->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
+	if (!private->secure_buffer_pool) {
+		DRM_ERROR("No secure buffer pool found\n");
+		return -ENOMEM;
+	}
 
-	rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size,
-					 &rk_obj->dma_addr, GFP_KERNEL,
-					 rk_obj->dma_attrs);
-	if (!rk_obj->kvaddr) {
-		DRM_ERROR("failed to allocate %zu byte dma buffer", obj->size);
+	paddr = gen_pool_alloc(private->secure_buffer_pool, rk_obj->base.size);
+	if (!paddr) {
+		DRM_ERROR("failed to allocate secure buffer\n");
 		return -ENOMEM;
 	}
 
+	rk_obj->dma_handle = paddr;
+	rk_obj->num_pages = rk_obj->base.size >> PAGE_SHIFT;
+
+	rk_obj->pages = drm_calloc_large(rk_obj->num_pages,
+					 sizeof(*rk_obj->pages));
+	if (!rk_obj->pages) {
+		DRM_ERROR("failed to allocate pages.\n");
+		ret = -ENOMEM;
+		goto err_buf_free;
+	}
+
+	i = 0;
+	while (i < rk_obj->num_pages) {
+		rk_obj->pages[i] = phys_to_page(paddr);
+		paddr += PAGE_SIZE;
+		i++;
+	}
+	sgt = drm_prime_pages_to_sg(obj->dev, rk_obj->pages, rk_obj->num_pages);
+	if (IS_ERR(sgt)) {
+		ret = PTR_ERR(sgt);
+		goto err_free_pages;
+	}
+
+	rk_obj->sgt = sgt;
+
 	return 0;
+
+err_free_pages:
+	drm_free_large(rk_obj->pages);
+err_buf_free:
+	gen_pool_free(private->secure_buffer_pool, paddr, rk_obj->base.size);
+
+	return ret;
+}
+
+static void rockchip_gem_free_secure(struct rockchip_gem_object *rk_obj)
+{
+	struct drm_gem_object *obj = &rk_obj->base;
+	struct drm_device *drm = obj->dev;
+	struct rockchip_drm_private *private = drm->dev_private;
+
+	drm_free_large(rk_obj->pages);
+	sg_free_table(rk_obj->sgt);
+	kfree(rk_obj->sgt);
+	gen_pool_free(private->secure_buffer_pool, rk_obj->dma_handle,
+		      rk_obj->base.size);
 }
 
 static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj,
@@ -179,18 +419,66 @@ static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj,
 	struct drm_gem_object *obj = &rk_obj->base;
 	struct drm_device *drm = obj->dev;
 	struct rockchip_drm_private *private = drm->dev_private;
+	int ret = 0;
+
+	if (!private->domain)
+		rk_obj->flags |= ROCKCHIP_BO_CONTIG;
+
+	if (rk_obj->flags & ROCKCHIP_BO_SECURE) {
+		rk_obj->buf_type = ROCKCHIP_GEM_BUF_TYPE_SECURE;
+		rk_obj->flags |= ROCKCHIP_BO_CONTIG;
+		if (alloc_kmap) {
+			DRM_ERROR("Not allow alloc secure buffer with kmap\n");
+			return -EINVAL;
+		}
+		ret = rockchip_gem_alloc_secure(rk_obj);
+		if (ret)
+			return ret;
+	} else if (rk_obj->flags & ROCKCHIP_BO_CONTIG) {
+		rk_obj->buf_type = ROCKCHIP_GEM_BUF_TYPE_CMA;
+		ret = rockchip_gem_alloc_dma(rk_obj, alloc_kmap);
+		if (ret)
+			return ret;
+	} else {
+		rk_obj->buf_type = ROCKCHIP_GEM_BUF_TYPE_SHMEM;
+		ret = rockchip_gem_get_pages(rk_obj);
+		if (ret < 0)
+			return ret;
+
+		if (alloc_kmap) {
+			rk_obj->kvaddr = vmap(rk_obj->pages, rk_obj->num_pages,
+					      VM_MAP,
+					      pgprot_writecombine(PAGE_KERNEL));
+			if (!rk_obj->kvaddr) {
+				DRM_ERROR("failed to vmap() buffer\n");
+				ret = -ENOMEM;
+				goto err_iommu_free;
+			}
+		}
+	}
 
+	if (private->domain) {
+		ret = rockchip_gem_iommu_map(rk_obj);
+		if (ret < 0)
+			goto err_free;
+	} else {
+		WARN_ON(!rk_obj->dma_handle);
+		rk_obj->dma_addr = rk_obj->dma_handle;
+	}
+
+	return 0;
+
+err_iommu_free:
 	if (private->domain)
-		return rockchip_gem_alloc_iommu(rk_obj, alloc_kmap);
+		rockchip_gem_iommu_unmap(rk_obj);
+err_free:
+	if (rk_obj->buf_type == ROCKCHIP_GEM_BUF_TYPE_SECURE)
+		rockchip_gem_free_secure(rk_obj);
+	else if (rk_obj->buf_type == ROCKCHIP_GEM_BUF_TYPE_CMA)
+		rockchip_gem_free_dma(rk_obj);
 	else
-		return rockchip_gem_alloc_dma(rk_obj, alloc_kmap);
-}
-
-static void rockchip_gem_free_iommu(struct rockchip_gem_object *rk_obj)
-{
-	vunmap(rk_obj->kvaddr);
-	rockchip_gem_iommu_unmap(rk_obj);
-	rockchip_gem_put_pages(rk_obj);
+		rockchip_gem_put_pages(rk_obj);
+	return ret;
 }
 
 static void rockchip_gem_free_dma(struct rockchip_gem_object *rk_obj)
@@ -198,16 +486,29 @@ static void rockchip_gem_free_dma(struct rockchip_gem_object *rk_obj)
 	struct drm_gem_object *obj = &rk_obj->base;
 	struct drm_device *drm = obj->dev;
 
-	dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_addr,
-		       rk_obj->dma_attrs);
+	drm_free_large(rk_obj->pages);
+	sg_free_table(rk_obj->sgt);
+	kfree(rk_obj->sgt);
+	dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr,
+		       rk_obj->dma_handle, rk_obj->dma_attrs);
 }
 
 static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj)
 {
-	if (rk_obj->pages)
-		rockchip_gem_free_iommu(rk_obj);
-	else
+	struct drm_device *drm = rk_obj->base.dev;
+	struct rockchip_drm_private *private = drm->dev_private;
+
+	if (private->domain)
+		rockchip_gem_iommu_unmap(rk_obj);
+
+	if (rk_obj->buf_type == ROCKCHIP_GEM_BUF_TYPE_SHMEM) {
+		vunmap(rk_obj->kvaddr);
+		rockchip_gem_put_pages(rk_obj);
+	} else if (rk_obj->buf_type == ROCKCHIP_GEM_BUF_TYPE_SECURE) {
+		rockchip_gem_free_secure(rk_obj);
+	} else {
 		rockchip_gem_free_dma(rk_obj);
+	}
 }
 
 static int rockchip_drm_gem_object_mmap_iommu(struct drm_gem_object *obj,
@@ -239,16 +540,24 @@ static int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj,
 	int ret;
 	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
 
+	/* default is wc. */
+	if (rk_obj->flags & ROCKCHIP_BO_CACHABLE)
+		vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
+
 	/*
 	 * We allocated a struct page table for rk_obj, so clear
 	 * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap().
 	 */
 	vma->vm_flags &= ~VM_PFNMAP;
 
-	if (rk_obj->pages)
+	if (rk_obj->buf_type == ROCKCHIP_GEM_BUF_TYPE_SECURE) {
+		DRM_ERROR("Disallow mmap for secure buffer\n");
+		ret = -EINVAL;
+	} else if (rk_obj->pages) {
 		ret = rockchip_drm_gem_object_mmap_iommu(obj, vma);
-	else
+	} else {
 		ret = rockchip_drm_gem_object_mmap_dma(obj, vma);
+	}
 
 	if (ret)
 		drm_gem_vm_close(vma);
@@ -298,9 +607,13 @@ static void rockchip_gem_release_object(struct rockchip_gem_object *rk_obj)
 static struct rockchip_gem_object *
 	rockchip_gem_alloc_object(struct drm_device *drm, unsigned int size)
 {
+	struct address_space *mapping;
 	struct rockchip_gem_object *rk_obj;
 	struct drm_gem_object *obj;
 
+	/* Limit the object to 32bit mappings */
+	gfp_t gfp_mask = GFP_HIGHUSER | __GFP_RECLAIMABLE | __GFP_DMA32;
+
 	size = round_up(size, PAGE_SIZE);
 
 	rk_obj = kzalloc(sizeof(*rk_obj), GFP_KERNEL);
@@ -311,12 +624,15 @@ static struct rockchip_gem_object *
 
 	drm_gem_object_init(drm, obj, size);
 
+	mapping = file_inode(obj->filp)->i_mapping;
+	mapping_set_gfp_mask(mapping, gfp_mask);
+
 	return rk_obj;
 }
 
 struct rockchip_gem_object *
 rockchip_gem_create_object(struct drm_device *drm, unsigned int size,
-			   bool alloc_kmap)
+			   bool alloc_kmap, unsigned int flags)
 {
 	struct rockchip_gem_object *rk_obj;
 	int ret;
@@ -324,6 +640,7 @@ rockchip_gem_create_object(struct drm_device *drm, unsigned int size,
 	rk_obj = rockchip_gem_alloc_object(drm, size);
 	if (IS_ERR(rk_obj))
 		return rk_obj;
+	rk_obj->flags = flags;
 
 	ret = rockchip_gem_alloc_buf(rk_obj, alloc_kmap);
 	if (ret)
@@ -336,6 +653,28 @@ rockchip_gem_create_object(struct drm_device *drm, unsigned int size,
 	return ERR_PTR(ret);
 }
 
+/*
+ * rockchip_gem_destroy - destroy gem object
+ *
+ * The dma_buf_unmap_attachment and dma_buf_detach will be re-defined if
+ * CONFIG_DMABUF_CACHE is enabled.
+ *
+ * Same as drm_prime_gem_destroy
+ */
+static void rockchip_gem_destroy(struct drm_gem_object *obj, struct sg_table *sg)
+{
+	struct dma_buf_attachment *attach;
+	struct dma_buf *dma_buf;
+
+	attach = obj->import_attach;
+	if (sg)
+		dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL);
+	dma_buf = attach->dmabuf;
+	dma_buf_detach(attach->dmabuf, attach);
+	/* remove the reference */
+	dma_buf_put(dma_buf);
+}
+
 /*
  * rockchip_gem_free_object - (struct drm_driver)->gem_free_object_unlocked
  * callback function
@@ -353,7 +692,11 @@ void rockchip_gem_free_object(struct drm_gem_object *obj)
 			dma_unmap_sgtable(drm->dev, rk_obj->sgt,
 					  DMA_BIDIRECTIONAL, 0);
 		}
-		drm_prime_gem_destroy(obj, rk_obj->sgt);
+		drm_free_large(rk_obj->pages);
+		if (IS_ENABLED(CONFIG_DMABUF_CACHE))
+			rockchip_gem_destroy(obj, rk_obj->sgt);
+		else
+			drm_prime_gem_destroy(obj, rk_obj->sgt);
 	} else {
 		rockchip_gem_free_buf(rk_obj);
 	}
@@ -371,13 +714,14 @@ void rockchip_gem_free_object(struct drm_gem_object *obj)
 static struct rockchip_gem_object *
 rockchip_gem_create_with_handle(struct drm_file *file_priv,
 				struct drm_device *drm, unsigned int size,
-				unsigned int *handle)
+				unsigned int *handle, unsigned int flags)
 {
 	struct rockchip_gem_object *rk_obj;
 	struct drm_gem_object *obj;
 	int ret;
+	bool alloc_kmap = flags & ROCKCHIP_BO_ALLOC_KMAP ? true : false;
 
-	rk_obj = rockchip_gem_create_object(drm, size, false);
+	rk_obj = rockchip_gem_create_object(drm, size, alloc_kmap, flags);
 	if (IS_ERR(rk_obj))
 		return ERR_CAST(rk_obj);
 
@@ -414,7 +758,7 @@ int rockchip_gem_dumb_create(struct drm_file *file_priv,
 			     struct drm_mode_create_dumb *args)
 {
 	struct rockchip_gem_object *rk_obj;
-	int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+	u32 min_pitch = args->width * DIV_ROUND_UP(args->bpp, 8);
 
 	/*
 	 * align to 64 bytes since Mali requires it.
@@ -423,7 +767,7 @@ int rockchip_gem_dumb_create(struct drm_file *file_priv,
 	args->size = args->pitch * args->height;
 
 	rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
-						 &args->handle);
+						 &args->handle, args->flags);
 
 	return PTR_ERR_OR_ZERO(rk_obj);
 }
@@ -514,6 +858,21 @@ rockchip_gem_prime_import_sg_table(struct drm_device *drm,
 		goto err_free_rk_obj;
 	}
 
+	rk_obj->num_pages = rk_obj->base.size >> PAGE_SHIFT;
+	rk_obj->pages = drm_calloc_large(rk_obj->num_pages, sizeof(*rk_obj->pages));
+	if (!rk_obj->pages) {
+		DRM_ERROR("failed to allocate pages.\n");
+		ret = -ENOMEM;
+		goto err_free_rk_obj;
+	}
+
+	ret = drm_prime_sg_to_page_addr_arrays(sg, rk_obj->pages, NULL, rk_obj->num_pages);
+	if (ret < 0) {
+		DRM_ERROR("invalid sgtable.\n");
+		drm_free_large(rk_obj->pages);
+		goto err_free_rk_obj;
+	}
+
 	return &rk_obj->base;
 
 err_free_rk_obj:
@@ -546,3 +905,155 @@ void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
 
 	/* Nothing to do if allocated by DMA mapping API. */
 }
+
+int rockchip_gem_create_ioctl(struct drm_device *dev, void *data,
+			      struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_create *args = data;
+	struct rockchip_gem_object *rk_obj;
+
+	rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
+						 &args->handle, args->flags);
+	return PTR_ERR_OR_ZERO(rk_obj);
+}
+
+int rockchip_gem_map_offset_ioctl(struct drm_device *drm, void *data,
+				  struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_map_off *args = data;
+
+	return drm_gem_dumb_map_offset(file_priv, drm, args->handle,
+				       &args->offset);
+}
+
+int rockchip_gem_get_phys_ioctl(struct drm_device *dev, void *data,
+				struct drm_file *file_priv)
+{
+	struct drm_rockchip_gem_phys *args = data;
+	struct rockchip_gem_object *rk_obj;
+	struct drm_gem_object *obj;
+	int ret = 0;
+
+	obj = drm_gem_object_lookup(file_priv, args->handle);
+	if (!obj) {
+		DRM_ERROR("failed to lookup gem object.\n");
+		return -EINVAL;
+	}
+	rk_obj = to_rockchip_obj(obj);
+
+	if (!(rk_obj->flags & ROCKCHIP_BO_CONTIG)) {
+		DRM_ERROR("Can't get phys address from non-continue buf.\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	args->phy_addr = page_to_phys(rk_obj->pages[0]);
+
+out:
+	drm_gem_object_put(obj);
+
+	return ret;
+}
+
+int rockchip_gem_prime_begin_cpu_access(struct drm_gem_object *obj,
+					enum dma_data_direction dir)
+{
+	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
+	struct drm_device *drm = obj->dev;
+
+	if (!rk_obj->sgt)
+		return 0;
+
+	dma_sync_sg_for_cpu(drm->dev, rk_obj->sgt->sgl,
+			    rk_obj->sgt->nents, dir);
+	return 0;
+}
+
+int rockchip_gem_prime_end_cpu_access(struct drm_gem_object *obj,
+				      enum dma_data_direction dir)
+{
+	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
+	struct drm_device *drm = obj->dev;
+
+	if (!rk_obj->sgt)
+		return 0;
+
+	dma_sync_sg_for_device(drm->dev, rk_obj->sgt->sgl,
+			       rk_obj->sgt->nents, dir);
+	return 0;
+}
+
+static int rockchip_gem_prime_sgl_sync_range(struct device *dev,
+					struct scatterlist *sgl, unsigned int nents,
+					unsigned int offset, unsigned int length,
+					enum dma_data_direction dir, bool for_cpu)
+{
+	int i;
+	struct scatterlist *sg;
+	unsigned int len = 0;
+	dma_addr_t sg_dma_addr;
+
+	for_each_sg(sgl, sg, nents, i) {
+		unsigned int sg_offset, sg_left, size = 0;
+
+		len += sg->length;
+		if (len <= offset)
+			continue;
+
+		sg_dma_addr = sg_dma_address(sg);
+		sg_left = len - offset;
+		sg_offset = sg->length - sg_left;
+
+		size = (length < sg_left) ? length : sg_left;
+		if (for_cpu)
+			dma_sync_single_range_for_cpu(dev, sg_dma_addr,
+						      sg_offset, size, dir);
+		else
+			dma_sync_single_range_for_device(dev, sg_dma_addr,
+							 sg_offset, size, dir);
+
+		offset += size;
+		length -= size;
+
+		if (length == 0)
+			break;
+	}
+
+	return 0;
+}
+
+int rockchip_gem_prime_begin_cpu_access_partial(struct drm_gem_object *obj,
+						enum dma_data_direction dir,
+						unsigned int offset,
+						unsigned int len)
+{
+	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
+	struct drm_device *drm = obj->dev;
+
+	if (!rk_obj->sgt)
+		return 0;
+
+	rockchip_gem_prime_sgl_sync_range(drm->dev, rk_obj->sgt->sgl,
+					  rk_obj->sgt->nents,
+					  offset, len, dir, true);
+
+	return 0;
+}
+
+int rockchip_gem_prime_end_cpu_access_partial(struct drm_gem_object *obj,
+					      enum dma_data_direction dir,
+					      unsigned int offset,
+					      unsigned int len)
+{
+	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
+	struct drm_device *drm = obj->dev;
+
+	if (!rk_obj->sgt)
+		return 0;
+
+	rockchip_gem_prime_sgl_sync_range(drm->dev, rk_obj->sgt->sgl,
+					  rk_obj->sgt->nents,
+					  offset, len, dir, false);
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
index 7ffc541be..6a1d8e55b 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
@@ -7,14 +7,24 @@
 #ifndef _ROCKCHIP_DRM_GEM_H
 #define _ROCKCHIP_DRM_GEM_H
 
+#include <linux/dma-direction.h>
+
 #define to_rockchip_obj(x) container_of(x, struct rockchip_gem_object, base)
 
+enum rockchip_gem_buf_type {
+	ROCKCHIP_GEM_BUF_TYPE_CMA,
+	ROCKCHIP_GEM_BUF_TYPE_SHMEM,
+	ROCKCHIP_GEM_BUF_TYPE_SECURE,
+};
+
 struct rockchip_gem_object {
 	struct drm_gem_object base;
 	unsigned int flags;
+	enum rockchip_gem_buf_type buf_type;
 
 	void *kvaddr;
-	dma_addr_t dma_addr;
+	dma_addr_t dma_addr;	/* iova if iommu enable, otherwise physical address */
+	dma_addr_t dma_handle;	/* physical address */
 	/* Used when IOMMU is disabled */
 	unsigned long dma_attrs;
 
@@ -42,12 +52,43 @@ int rockchip_gem_mmap_buf(struct drm_gem_object *obj,
 			  struct vm_area_struct *vma);
 
 struct rockchip_gem_object *
-	rockchip_gem_create_object(struct drm_device *drm, unsigned int size,
-				   bool alloc_kmap);
+rockchip_gem_create_object(struct drm_device *drm, unsigned int size,
+			   bool alloc_kmap, unsigned int flags);
 
 void rockchip_gem_free_object(struct drm_gem_object *obj);
 
 int rockchip_gem_dumb_create(struct drm_file *file_priv,
 			     struct drm_device *dev,
 			     struct drm_mode_create_dumb *args);
+/*
+ * request gem object creation and buffer allocation as the size
+ * that it is calculated with framebuffer information such as width,
+ * height and bpp.
+ */
+int rockchip_gem_create_ioctl(struct drm_device *dev, void *data,
+			      struct drm_file *file_priv);
+
+/* get buffer offset to map to user space. */
+int rockchip_gem_map_offset_ioctl(struct drm_device *dev, void *data,
+				  struct drm_file *file_priv);
+
+int rockchip_gem_get_phys_ioctl(struct drm_device *dev, void *data,
+				struct drm_file *file_priv);
+
+int rockchip_gem_prime_begin_cpu_access(struct drm_gem_object *obj,
+					enum dma_data_direction dir);
+
+int rockchip_gem_prime_end_cpu_access(struct drm_gem_object *obj,
+				      enum dma_data_direction dir);
+
+int rockchip_gem_prime_begin_cpu_access_partial(struct drm_gem_object *obj,
+						enum dma_data_direction dir,
+						unsigned int offset,
+						unsigned int len);
+
+int rockchip_gem_prime_end_cpu_access_partial(struct drm_gem_object *obj,
+					      enum dma_data_direction dir,
+					      unsigned int offset,
+					      unsigned int len);
+void rockchip_gem_get_ddr_info(void);
 #endif /* _ROCKCHIP_DRM_GEM_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index af98bfcde..ccf39dcf6 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -6,21 +6,28 @@
 
 #include <linux/clk.h>
 #include <linux/component.h>
+#include <linux/debugfs.h>
 #include <linux/delay.h>
+#include <linux/fixp-arith.h>
 #include <linux/iopoll.h>
 #include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/overflow.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/regmap.h>
 #include <linux/reset.h>
+#include <linux/sort.h>
 
 #include <drm/drm.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_uapi.h>
 #include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_debugfs.h>
 #include <drm/drm_flip_work.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_gem_framebuffer_helper.h>
@@ -32,6 +39,12 @@
 #ifdef CONFIG_DRM_ANALOGIX_DP
 #include <drm/bridge/analogix_dp.h>
 #endif
+#include <dt-bindings/soc/rockchip-system-status.h>
+
+#include <soc/rockchip/rockchip_dmc.h>
+#include <soc/rockchip/rockchip-system-status.h>
+#include <uapi/linux/videodev2.h>
+#include "../drm_crtc_internal.h"
 
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_gem.h"
@@ -39,28 +52,65 @@
 #include "rockchip_drm_vop.h"
 #include "rockchip_rgb.h"
 
-#define VOP_WIN_SET(vop, win, name, v) \
-		vop_reg_set(vop, &win->phy->name, win->base, ~0, v, #name)
-#define VOP_SCL_SET(vop, win, name, v) \
-		vop_reg_set(vop, &win->phy->scl->name, win->base, ~0, v, #name)
-#define VOP_SCL_SET_EXT(vop, win, name, v) \
-		vop_reg_set(vop, &win->phy->scl->ext->name, \
-			    win->base, ~0, v, #name)
+#define VOP_REG_SUPPORT(vop, reg) \
+		(reg.mask && \
+		 (!reg.major || \
+		  (reg.major == VOP_MAJOR(vop->version) && \
+		   reg.begin_minor <= VOP_MINOR(vop->version) && \
+		   reg.end_minor >= VOP_MINOR(vop->version))))
 
-#define VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, name, v) \
-	do { \
-		if (win_yuv2yuv && win_yuv2yuv->name.mask) \
-			vop_reg_set(vop, &win_yuv2yuv->name, 0, ~0, v, #name); \
-	} while (0)
+#define VOP_WIN_SUPPORT(vop, win, name) \
+		VOP_REG_SUPPORT(vop, win->phy->name)
+
+#define VOP_WIN_SCL_EXT_SUPPORT(vop, win, name) \
+		(win->phy->scl->ext && \
+		VOP_REG_SUPPORT(vop, win->phy->scl->ext->name))
 
-#define VOP_WIN_YUV2YUV_COEFFICIENT_SET(vop, win_yuv2yuv, name, v) \
+#define VOP_CTRL_SUPPORT(vop, name) \
+		VOP_REG_SUPPORT(vop, vop->data->ctrl->name)
+
+#define VOP_INTR_SUPPORT(vop, name) \
+		VOP_REG_SUPPORT(vop, vop->data->intr->name)
+
+#define __REG_SET(x, off, mask, shift, v, write_mask, relaxed) \
+		vop_mask_write(x, off, mask, shift, v, write_mask, relaxed)
+
+#define _REG_SET(vop, name, off, reg, mask, v, relaxed) \
 	do { \
-		if (win_yuv2yuv && win_yuv2yuv->phy->name.mask) \
-			vop_reg_set(vop, &win_yuv2yuv->phy->name, win_yuv2yuv->base, ~0, v, #name); \
+		if (VOP_REG_SUPPORT(vop, reg)) \
+			__REG_SET(vop, off + reg.offset, mask, reg.shift, \
+				  v, reg.write_mask, relaxed); \
+		else \
+			dev_dbg(vop->dev, "Warning: not support "#name"\n"); \
 	} while (0)
 
+#define REG_SET(x, name, off, reg, v, relaxed) \
+		_REG_SET(x, name, off, reg, reg.mask, v, relaxed)
+#define REG_SET_MASK(x, name, off, reg, mask, v, relaxed) \
+		_REG_SET(x, name, off, reg, reg.mask & mask, v, relaxed)
+
+#define VOP_WIN_SET(x, win, name, v) \
+		REG_SET(x, name, win->offset, VOP_WIN_NAME(win, name), v, true)
+#define VOP_WIN_SET_EXT(x, win, ext, name, v) \
+		REG_SET(x, name, 0, win->ext->name, v, true)
+#define VOP_SCL_SET(x, win, name, v) \
+		REG_SET(x, name, win->offset, win->phy->scl->name, v, true)
+#define VOP_SCL_SET_EXT(x, win, name, v) \
+		REG_SET(x, name, win->offset, win->phy->scl->ext->name, v, true)
+
+#define VOP_CTRL_SET(x, name, v) \
+		REG_SET(x, name, 0, (x)->data->ctrl->name, v, false)
+
+#define VOP_INTR_GET(vop, name) \
+		vop_read_reg(vop, 0, &vop->data->ctrl->name)
+
+#define VOP_INTR_SET(vop, name, v) \
+		REG_SET(vop, name, 0, vop->data->intr->name, \
+			v, false)
 #define VOP_INTR_SET_MASK(vop, name, mask, v) \
-		vop_reg_set(vop, &vop->data->intr->name, 0, mask, v, #name)
+		REG_SET_MASK(vop, name, 0, vop->data->intr->name, \
+			     mask, v, false)
+
 
 #define VOP_REG_SET(vop, group, name, v) \
 		    vop_reg_set(vop, &vop->data->group->name, 0, ~0, v, #name)
@@ -79,66 +129,129 @@
 #define VOP_INTR_GET_TYPE(vop, name, type) \
 		vop_get_intr_type(vop, &vop->data->intr->name, type)
 
-#define VOP_WIN_GET(vop, win, name) \
-		vop_read_reg(vop, win->base, &win->phy->name)
+#define VOP_CTRL_GET(x, name) \
+		vop_read_reg(x, 0, &vop->data->ctrl->name)
 
-#define VOP_WIN_HAS_REG(win, name) \
-	(!!(win->phy->name.mask))
+#define VOP_WIN_GET(vop, win, name) \
+		vop_read_reg(vop, win->offset, &VOP_WIN_NAME(win, name))
 
-#define VOP_WIN_GET_YRGBADDR(vop, win) \
-		vop_readl(vop, win->base + win->phy->yrgb_mst.offset)
+#define VOP_WIN_NAME(win, name) \
+		(vop_get_win_phy(win, &win->phy->name)->name)
 
 #define VOP_WIN_TO_INDEX(vop_win) \
 	((vop_win) - (vop_win)->vop->win)
 
-#define VOP_AFBC_SET(vop, name, v) \
+#define VOP_GRF_SET(vop, reg, v) \
 	do { \
-		if ((vop)->data->afbc) \
-			vop_reg_set((vop), &(vop)->data->afbc->name, \
-				    0, ~0, v, #name); \
+		if (vop->data->grf_ctrl) { \
+			vop_grf_writel(vop, vop->data->grf_ctrl->reg, v); \
+		} \
 	} while (0)
 
-#define to_vop(x) container_of(x, struct vop, crtc)
 #define to_vop_win(x) container_of(x, struct vop_win, base)
+#define to_vop_plane_state(x) container_of(x, struct vop_plane_state, base)
 
-#define AFBC_FMT_RGB565		0x0
-#define AFBC_FMT_U8U8U8U8	0x5
-#define AFBC_FMT_U8U8U8		0x4
+enum vop_pending {
+	VOP_PENDING_FB_UNREF,
+};
 
-#define AFBC_TILE_16x16		BIT(4)
+struct vop_zpos {
+	int win_id;
+	int zpos;
+};
 
-/*
- * The coefficients of the following matrix are all fixed points.
- * The format is S2.10 for the 3x3 part of the matrix, and S9.12 for the offsets.
- * They are all represented in two's complement.
- */
-static const uint32_t bt601_yuv2rgb[] = {
-	0x4A8, 0x0,    0x662,
-	0x4A8, 0x1E6F, 0x1CBF,
-	0x4A8, 0x812,  0x0,
-	0x321168, 0x0877CF, 0x2EB127
+struct vop_plane_state {
+	struct drm_plane_state base;
+	int format;
+	int zpos;
+	struct drm_rect src;
+	struct drm_rect dest;
+	dma_addr_t yrgb_mst;
+	dma_addr_t uv_mst;
+	const uint32_t *y2r_table;
+	const uint32_t *r2r_table;
+	const uint32_t *r2y_table;
+	int eotf;
+	bool y2r_en;
+	bool r2r_en;
+	bool r2y_en;
+	int color_space;
+	u32 color_key;
+	unsigned int csc_mode;
+	int global_alpha;
+	int blend_mode;
+	unsigned long offset;
+	int pdaf_data_type;
+	bool async_commit;
+	struct vop_dump_list *planlist;
 };
 
-enum vop_pending {
-	VOP_PENDING_FB_UNREF,
+struct rockchip_mcu_timing {
+	int mcu_pix_total;
+	int mcu_cs_pst;
+	int mcu_cs_pend;
+	int mcu_rw_pst;
+	int mcu_rw_pend;
+	int mcu_hold_mode;
 };
 
 struct vop_win {
+	struct vop_win *parent;
 	struct drm_plane base;
-	const struct vop_win_data *data;
-	const struct vop_win_yuv2yuv_data *yuv2yuv_data;
+
+	int win_id;
+	int area_id;
+	u8 plane_id; /* unique plane id */
+	const char *name;
+
+	int zpos;
+	uint32_t offset;
+	enum drm_plane_type type;
+	const struct vop_win_phy *phy;
+	const struct vop_csc *csc;
+	const uint32_t *data_formats;
+	uint32_t nformats;
+	const uint64_t *format_modifiers;
+	u64 feature;
 	struct vop *vop;
+	struct vop_plane_state state;
+
+	struct drm_property *input_width_prop;
+	struct drm_property *input_height_prop;
+	struct drm_property *output_width_prop;
+	struct drm_property *output_height_prop;
+	struct drm_property *color_key_prop;
+	struct drm_property *scale_prop;
+	struct drm_property *name_prop;
 };
 
-struct rockchip_rgb;
 struct vop {
-	struct drm_crtc crtc;
+	struct rockchip_crtc rockchip_crtc;
 	struct device *dev;
 	struct drm_device *drm_dev;
+	struct dentry *debugfs;
+	struct drm_info_list *debugfs_files;
+	struct drm_property *plane_feature_prop;
+	struct drm_property *plane_mask_prop;
+	struct drm_property *feature_prop;
+
+	bool is_iommu_enabled;
+	bool is_iommu_needed;
 	bool is_enabled;
-
+	bool support_multi_area;
+
+	u32 version;
+	u32 background;
+	u32 line_flag;
+	u8 id;
+	u8 plane_mask;
+	u64 soc_id;
+	struct drm_prop_enum_list *plane_name_list;
+
+	struct drm_tv_connector_state active_tv_state;
+	bool pre_overlay;
+	bool loader_protect;
 	struct completion dsp_hold_completion;
-	unsigned int win_enabled;
 
 	/* protected by dev->event_lock */
 	struct drm_pending_vblank_event *event;
@@ -149,14 +262,22 @@ struct vop {
 	struct completion line_flag_completion;
 
 	const struct vop_data *data;
+	int num_wins;
 
 	uint32_t *regsbak;
 	void __iomem *regs;
-	void __iomem *lut_regs;
+	struct regmap *grf;
 
 	/* physical map length of vop register */
 	uint32_t len;
 
+	void __iomem *lut_regs;
+	u32 *lut;
+	u32 lut_len;
+	bool lut_active;
+	/* gamma look up table */
+	struct drm_color_lut *gamma_lut;
+	bool dual_channel_swap;
 	/* one time only one process allowed to config the register */
 	spinlock_t reg_lock;
 	/* lock vop irq reg */
@@ -172,16 +293,83 @@ struct vop {
 	struct clk *dclk;
 	/* vop share memory frequency */
 	struct clk *aclk;
+	/* vop source handling, optional */
+	struct clk *dclk_source;
 
 	/* vop dclk reset */
 	struct reset_control *dclk_rst;
 
-	/* optional internal rgb encoder */
-	struct rockchip_rgb *rgb;
+	struct rockchip_dclk_pll *pll;
+
+	struct rockchip_mcu_timing mcu_timing;
 
 	struct vop_win win[];
 };
 
+/*
+ * bus-format types.
+ */
+struct drm_bus_format_enum_list {
+	int type;
+	const char *name;
+};
+
+static const struct drm_bus_format_enum_list drm_bus_format_enum_list[] = {
+	{ DRM_MODE_CONNECTOR_Unknown, "Unknown" },
+	{ MEDIA_BUS_FMT_RGB565_1X16, "RGB565_1X16" },
+	{ MEDIA_BUS_FMT_RGB666_1X18, "RGB666_1X18" },
+	{ MEDIA_BUS_FMT_RGB666_1X24_CPADHI, "RGB666_1X24_CPADHI" },
+	{ MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, "RGB666_1X7X3_SPWG" },
+	{ MEDIA_BUS_FMT_YUV8_1X24, "YUV8_1X24" },
+	{ MEDIA_BUS_FMT_UYYVYY8_0_5X24, "UYYVYY8_0_5X24" },
+	{ MEDIA_BUS_FMT_YUV10_1X30, "YUV10_1X30" },
+	{ MEDIA_BUS_FMT_UYYVYY10_0_5X30, "UYYVYY10_0_5X30" },
+	{ MEDIA_BUS_FMT_RGB888_3X8, "RGB888_3X8" },
+	{ MEDIA_BUS_FMT_RGB888_DUMMY_4X8, "RGB888_DUMMY_4X8" },
+	{ MEDIA_BUS_FMT_RGB888_1X24, "RGB888_1X24" },
+	{ MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, "RGB888_1X7X4_SPWG" },
+	{ MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, "RGB888_1X7X4_JEIDA" },
+	{ MEDIA_BUS_FMT_UYVY8_2X8, "UYVY8_2X8" },
+	{ MEDIA_BUS_FMT_YUYV8_1X16, "YUYV8_1X16" },
+	{ MEDIA_BUS_FMT_UYVY8_1X16, "UYVY8_1X16" },
+};
+
+static DRM_ENUM_NAME_FN(drm_get_bus_format_name, drm_bus_format_enum_list)
+
+static inline struct vop *to_vop(struct drm_crtc *crtc)
+{
+	struct rockchip_crtc *rockchip_crtc;
+
+	rockchip_crtc = container_of(crtc, struct rockchip_crtc, crtc);
+
+	return container_of(rockchip_crtc, struct vop, rockchip_crtc);
+}
+
+static void vop_lock(struct vop *vop)
+{
+	mutex_lock(&vop->vop_lock);
+	rockchip_dmcfreq_lock();
+}
+
+static void vop_unlock(struct vop *vop)
+{
+	rockchip_dmcfreq_unlock();
+	mutex_unlock(&vop->vop_lock);
+}
+
+static inline void vop_grf_writel(struct vop *vop, struct vop_reg reg, u32 v)
+{
+	u32 val = 0;
+
+	if (IS_ERR_OR_NULL(vop->grf))
+		return;
+
+	if (VOP_REG_SUPPORT(vop, reg)) {
+		val = (v << reg.shift) | (reg.mask << (reg.shift + 16));
+		regmap_write(vop->grf, reg.offset, val);
+	}
+}
+
 static inline void vop_writel(struct vop *vop, uint32_t offset, uint32_t v)
 {
 	writel(v, vop->regs + offset);
@@ -199,23 +387,15 @@ static inline uint32_t vop_read_reg(struct vop *vop, uint32_t base,
 	return (vop_readl(vop, base + reg->offset) >> reg->shift) & reg->mask;
 }
 
-static void vop_reg_set(struct vop *vop, const struct vop_reg *reg,
-			uint32_t _offset, uint32_t _mask, uint32_t v,
-			const char *reg_name)
+static inline void vop_mask_write(struct vop *vop, uint32_t offset,
+				  uint32_t mask, uint32_t shift, uint32_t v,
+				  bool write_mask, bool relaxed)
 {
-	int offset, mask, shift;
-
-	if (!reg || !reg->mask) {
-		DRM_DEV_DEBUG(vop->dev, "Warning: not support %s\n", reg_name);
+	if (!mask)
 		return;
-	}
-
-	offset = reg->offset + _offset;
-	mask = reg->mask & _mask;
-	shift = reg->shift;
 
-	if (reg->write_mask) {
-		v = ((v << shift) & 0xffff) | (mask << (shift + 16));
+	if (write_mask) {
+		v = ((v & mask) << shift) | (mask << (shift + 16));
 	} else {
 		uint32_t cached_val = vop->regsbak[offset >> 2];
 
@@ -223,12 +403,21 @@ static void vop_reg_set(struct vop *vop, const struct vop_reg *reg,
 		vop->regsbak[offset >> 2] = v;
 	}
 
-	if (reg->relaxed)
+	if (relaxed)
 		writel_relaxed(v, vop->regs + offset);
 	else
 		writel(v, vop->regs + offset);
 }
 
+static inline const struct vop_win_phy *
+vop_get_win_phy(struct vop_win *win, const struct vop_reg *reg)
+{
+	if (!reg->mask && win->parent)
+		return win->parent->phy;
+
+	return win->phy;
+}
+
 static inline uint32_t vop_get_intr_type(struct vop *vop,
 					 const struct vop_reg *reg, int type)
 {
@@ -243,9 +432,147 @@ static inline uint32_t vop_get_intr_type(struct vop *vop,
 	return ret;
 }
 
+static void vop_load_hdr2sdr_table(struct vop *vop)
+{
+	int i;
+	const struct vop_hdr_table *table = vop->data->hdr_table;
+	uint32_t hdr2sdr_eetf_oetf_yn[33];
+
+	for (i = 0; i < 33; i++)
+		hdr2sdr_eetf_oetf_yn[i] = table->hdr2sdr_eetf_yn[i] +
+				(table->hdr2sdr_bt1886oetf_yn[i] << 16);
+
+	vop_writel(vop, table->hdr2sdr_eetf_oetf_y0_offset,
+		   hdr2sdr_eetf_oetf_yn[0]);
+	for (i = 1; i < 33; i++)
+		vop_writel(vop,
+			   table->hdr2sdr_eetf_oetf_y1_offset + (i - 1) * 4,
+			   hdr2sdr_eetf_oetf_yn[i]);
+
+	vop_writel(vop, table->hdr2sdr_sat_y0_offset,
+		   table->hdr2sdr_sat_yn[0]);
+	for (i = 1; i < 9; i++)
+		vop_writel(vop, table->hdr2sdr_sat_y1_offset + (i - 1) * 4,
+			   table->hdr2sdr_sat_yn[i]);
+
+	VOP_CTRL_SET(vop, hdr2sdr_src_min, table->hdr2sdr_src_range_min);
+	VOP_CTRL_SET(vop, hdr2sdr_src_max, table->hdr2sdr_src_range_max);
+	VOP_CTRL_SET(vop, hdr2sdr_normfaceetf, table->hdr2sdr_normfaceetf);
+	VOP_CTRL_SET(vop, hdr2sdr_dst_min, table->hdr2sdr_dst_range_min);
+	VOP_CTRL_SET(vop, hdr2sdr_dst_max, table->hdr2sdr_dst_range_max);
+	VOP_CTRL_SET(vop, hdr2sdr_normfacgamma, table->hdr2sdr_normfacgamma);
+}
+
+static void vop_load_sdr2hdr_table(struct vop *vop, uint32_t cmd)
+{
+	int i;
+	const struct vop_hdr_table *table = vop->data->hdr_table;
+	uint32_t sdr2hdr_eotf_oetf_yn[65];
+	uint32_t sdr2hdr_oetf_dx_dxpow[64];
+
+	for (i = 0; i < 65; i++) {
+		if (cmd == SDR2HDR_FOR_BT2020)
+			sdr2hdr_eotf_oetf_yn[i] =
+				table->sdr2hdr_bt1886eotf_yn_for_bt2020[i] +
+				(table->sdr2hdr_st2084oetf_yn_for_bt2020[i] << 18);
+		else if (cmd == SDR2HDR_FOR_HDR)
+			sdr2hdr_eotf_oetf_yn[i] =
+				table->sdr2hdr_bt1886eotf_yn_for_hdr[i] +
+				(table->sdr2hdr_st2084oetf_yn_for_hdr[i] << 18);
+		else if (cmd == SDR2HDR_FOR_HLG_HDR)
+			sdr2hdr_eotf_oetf_yn[i] =
+				table->sdr2hdr_bt1886eotf_yn_for_hlg_hdr[i] +
+				(table->sdr2hdr_st2084oetf_yn_for_hlg_hdr[i] << 18);
+	}
+	vop_writel(vop, table->sdr2hdr_eotf_oetf_y0_offset,
+		   sdr2hdr_eotf_oetf_yn[0]);
+	for (i = 1; i < 65; i++)
+		vop_writel(vop, table->sdr2hdr_eotf_oetf_y1_offset +
+			   (i - 1) * 4, sdr2hdr_eotf_oetf_yn[i]);
+
+	for (i = 0; i < 64; i++) {
+		sdr2hdr_oetf_dx_dxpow[i] = table->sdr2hdr_st2084oetf_dxn[i] +
+				(table->sdr2hdr_st2084oetf_dxn_pow2[i] << 16);
+		vop_writel(vop, table->sdr2hdr_oetf_dx_dxpow1_offset + i * 4,
+			   sdr2hdr_oetf_dx_dxpow[i]);
+	}
+
+	for (i = 0; i < 63; i++)
+		vop_writel(vop, table->sdr2hdr_oetf_xn1_offset + i * 4,
+			   table->sdr2hdr_st2084oetf_xn[i]);
+}
+
+static void vop_load_csc_table(struct vop *vop, u32 offset, const u32 *table)
+{
+	int i;
+
+	/*
+	 * so far the csc offset is not 0 and in the feature the csc offset
+	 * impossible be 0, so when the offset is 0, should return here.
+	 */
+	if (!table || offset == 0)
+		return;
+
+	for (i = 0; i < 8; i++)
+		vop_writel(vop, offset + i * 4, table[i]);
+}
+
 static inline void vop_cfg_done(struct vop *vop)
 {
-	VOP_REG_SET(vop, common, cfg_done, 1);
+	VOP_CTRL_SET(vop, cfg_done, 1);
+}
+
+static bool vop_is_allwin_disabled(struct vop *vop)
+{
+	int i;
+
+	for (i = 0; i < vop->num_wins; i++) {
+		struct vop_win *win = &vop->win[i];
+
+		if (VOP_WIN_GET(vop, win, enable) != 0)
+			return false;
+	}
+
+	return true;
+}
+
+static void vop_win_disable(struct vop *vop, struct vop_win *win)
+{
+	/*
+	 * FIXUP: some of the vop scale would be abnormal after windows power
+	 * on/off so deinit scale to scale_none mode.
+	 */
+	if (win->phy->scl && win->phy->scl->ext) {
+		VOP_SCL_SET_EXT(vop, win, yrgb_hor_scl_mode, SCALE_NONE);
+		VOP_SCL_SET_EXT(vop, win, yrgb_ver_scl_mode, SCALE_NONE);
+		VOP_SCL_SET_EXT(vop, win, cbcr_hor_scl_mode, SCALE_NONE);
+		VOP_SCL_SET_EXT(vop, win, cbcr_ver_scl_mode, SCALE_NONE);
+	}
+
+	VOP_WIN_SET(vop, win, enable, 0);
+	if (win->area_id == 0)
+		VOP_WIN_SET(vop, win, gate, 0);
+}
+
+static void vop_disable_allwin(struct vop *vop)
+{
+	int i;
+
+	for (i = 0; i < vop->num_wins; i++) {
+		struct vop_win *win = &vop->win[i];
+
+		vop_win_disable(vop, win);
+	}
+}
+
+static inline void vop_write_lut(struct vop *vop, uint32_t offset, uint32_t v)
+{
+	writel(v, vop->lut_regs + offset);
+}
+
+static inline uint32_t vop_read_lut(struct vop *vop, uint32_t offset)
+{
+	return readl(vop->lut_regs + offset);
 }
 
 static bool has_rb_swapped(uint32_t format)
@@ -276,38 +603,150 @@ static enum vop_data_format vop_convert_format(uint32_t format)
 	case DRM_FORMAT_BGR565:
 		return VOP_FMT_RGB565;
 	case DRM_FORMAT_NV12:
+	case DRM_FORMAT_NV15:
 		return VOP_FMT_YUV420SP;
 	case DRM_FORMAT_NV16:
+	case DRM_FORMAT_NV20:
 		return VOP_FMT_YUV422SP;
 	case DRM_FORMAT_NV24:
+	case DRM_FORMAT_NV30:
 		return VOP_FMT_YUV444SP;
+	case DRM_FORMAT_YVYU:
+	case DRM_FORMAT_VYUY:
+	case DRM_FORMAT_YUYV:
+	case DRM_FORMAT_UYVY:
+		return VOP_FMT_YUYV;
 	default:
 		DRM_ERROR("unsupported format[%08x]\n", format);
 		return -EINVAL;
 	}
 }
 
-static int vop_convert_afbc_format(uint32_t format)
+static bool is_uv_swap(uint32_t bus_format, uint32_t output_mode)
+{
+	/*
+	 * FIXME:
+	 *
+	 * There is no media type for YUV444 output,
+	 * so when out_mode is AAAA or P888, assume output is YUV444 on
+	 * yuv format.
+	 *
+	 * From H/W testing, YUV444 mode need a rb swap.
+	 */
+	if (bus_format == MEDIA_BUS_FMT_YVYU8_1X16 ||
+	    bus_format == MEDIA_BUS_FMT_VYUY8_1X16 ||
+	    bus_format == MEDIA_BUS_FMT_YVYU8_2X8 ||
+	    bus_format == MEDIA_BUS_FMT_VYUY8_2X8 ||
+	    ((bus_format == MEDIA_BUS_FMT_YUV8_1X24 ||
+	      bus_format == MEDIA_BUS_FMT_YUV10_1X30) &&
+	     (output_mode == ROCKCHIP_OUT_MODE_AAAA ||
+	      output_mode == ROCKCHIP_OUT_MODE_P888)))
+		return true;
+	else
+		return false;
+}
+
+static bool is_yc_swap(uint32_t bus_format)
+{
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_YUYV8_1X16:
+	case MEDIA_BUS_FMT_YVYU8_1X16:
+	case MEDIA_BUS_FMT_YUYV8_2X8:
+	case MEDIA_BUS_FMT_YVYU8_2X8:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool is_yuv_output(uint32_t bus_format)
+{
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_YUV8_1X24:
+	case MEDIA_BUS_FMT_YUV10_1X30:
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+	case MEDIA_BUS_FMT_YUYV8_2X8:
+	case MEDIA_BUS_FMT_YVYU8_2X8:
+	case MEDIA_BUS_FMT_UYVY8_2X8:
+	case MEDIA_BUS_FMT_VYUY8_2X8:
+	case MEDIA_BUS_FMT_YUYV8_1X16:
+	case MEDIA_BUS_FMT_YVYU8_1X16:
+	case MEDIA_BUS_FMT_UYVY8_1X16:
+	case MEDIA_BUS_FMT_VYUY8_1X16:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool is_yuv_support(uint32_t format)
+{
+	switch (format) {
+	case DRM_FORMAT_NV12:
+	case DRM_FORMAT_NV15:
+	case DRM_FORMAT_NV16:
+	case DRM_FORMAT_NV20:
+	case DRM_FORMAT_NV24:
+	case DRM_FORMAT_NV30:
+	case DRM_FORMAT_YVYU:
+	case DRM_FORMAT_VYUY:
+	case DRM_FORMAT_YUYV:
+	case DRM_FORMAT_UYVY:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool is_yuyv_format(uint32_t format)
+{
+	switch (format) {
+	case DRM_FORMAT_YVYU:
+	case DRM_FORMAT_VYUY:
+	case DRM_FORMAT_YUYV:
+	case DRM_FORMAT_UYVY:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool is_yuv_10bit(uint32_t format)
+{
+	switch (format) {
+	case DRM_FORMAT_NV15:
+	case DRM_FORMAT_NV20:
+	case DRM_FORMAT_NV30:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool is_alpha_support(uint32_t format)
 {
 	switch (format) {
-	case DRM_FORMAT_XRGB8888:
 	case DRM_FORMAT_ARGB8888:
-	case DRM_FORMAT_XBGR8888:
 	case DRM_FORMAT_ABGR8888:
-		return AFBC_FMT_U8U8U8U8;
-	case DRM_FORMAT_RGB888:
-	case DRM_FORMAT_BGR888:
-		return AFBC_FMT_U8U8U8;
-	case DRM_FORMAT_RGB565:
-	case DRM_FORMAT_BGR565:
-		return AFBC_FMT_RGB565;
-	/* either of the below should not be reachable */
+		return true;
 	default:
-		DRM_WARN_ONCE("unsupported AFBC format[%08x]\n", format);
-		return -EINVAL;
+		return false;
 	}
+}
 
-	return -EINVAL;
+static inline bool rockchip_afbc(struct drm_plane *plane, u64 modifier)
+{
+	int i;
+
+	if (modifier == DRM_FORMAT_MOD_LINEAR)
+		return false;
+
+	for (i = 0 ; i < plane->modifier_count; i++)
+		if (plane->modifiers[i] == modifier)
+			break;
+
+	return (i < plane->modifier_count) ? true : false;
 }
 
 static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src,
@@ -344,29 +783,37 @@ static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src,
 	return val;
 }
 
-static void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win_data *win,
-			     uint32_t src_w, uint32_t src_h, uint32_t dst_w,
-			     uint32_t dst_h, const struct drm_format_info *info)
+static void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win *win,
+				uint32_t src_w, uint32_t src_h, uint32_t dst_w,
+				uint32_t dst_h, uint32_t pixel_format)
 {
 	uint16_t yrgb_hor_scl_mode, yrgb_ver_scl_mode;
 	uint16_t cbcr_hor_scl_mode = SCALE_NONE;
 	uint16_t cbcr_ver_scl_mode = SCALE_NONE;
+	const struct drm_format_info *info = drm_format_info(pixel_format);
+	uint8_t hsub = info->hsub;
+	uint8_t vsub = info->vsub;
 	bool is_yuv = false;
-	uint16_t cbcr_src_w = src_w / info->hsub;
-	uint16_t cbcr_src_h = src_h / info->vsub;
+	uint16_t cbcr_src_w = src_w / hsub;
+	uint16_t cbcr_src_h = src_h / vsub;
 	uint16_t vsu_mode;
 	uint16_t lb_mode;
 	uint32_t val;
+	const struct vop_data *vop_data = vop->data;
 	int vskiplines;
 
-	if (info->is_yuv)
-		is_yuv = true;
-
-	if (dst_w > 3840) {
-		DRM_DEV_ERROR(vop->dev, "Maximum dst width (3840) exceeded\n");
+	if (!win->phy->scl)
 		return;
+
+	if (!(vop_data->feature & VOP_FEATURE_ALPHA_SCALE)) {
+		if (is_alpha_support(pixel_format) &&
+		    (src_w != dst_w || src_h != dst_h))
+			DRM_ERROR("ERROR: unsupported ppixel alpha&scale\n");
 	}
 
+	if (info->is_yuv)
+		is_yuv = true;
+
 	if (!win->phy->scl->ext) {
 		VOP_SCL_SET(vop, win, scale_yrgb_x,
 			    scl_cal_scale2(src_w, dst_w));
@@ -448,46 +895,412 @@ static void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win_data *win,
 	}
 }
 
-static void vop_dsp_hold_valid_irq_enable(struct vop *vop)
+/*
+ * rk3328 HDR/CSC path
+ *
+ * HDR/SDR --> win0  --> HDR2SDR ----\
+ *		  \		      MUX --\
+ *                 \ --> SDR2HDR/CSC--/      \
+ *                                            \
+ * SDR --> win1 -->pre_overlay ->SDR2HDR/CSC --> post_ovrlay-->post CSC-->output
+ * SDR --> win2 -/
+ *
+ */
+
+static int vop_hdr_atomic_check(struct drm_crtc *crtc,
+				struct drm_crtc_state *crtc_state)
 {
-	unsigned long flags;
+	struct drm_atomic_state *state = crtc_state->state;
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+	struct drm_plane_state *pstate;
+	struct drm_plane *plane;
+	struct vop *vop = to_vop(crtc);
+	int pre_sdr2hdr_state = 0, post_sdr2hdr_state = 0;
+	int pre_sdr2hdr_mode = 0, post_sdr2hdr_mode = 0, sdr2hdr_func = 0;
+	bool pre_overlay = false;
+	int hdr2sdr_en = 0, plane_id = 0;
 
-	if (WARN_ON(!vop->is_enabled))
-		return;
+	if (!vop->data->hdr_table)
+		return 0;
+	/* hdr cover */
+	drm_atomic_crtc_state_for_each_plane(plane, crtc_state) {
+		struct vop_plane_state *vop_plane_state;
+		struct vop_win *win = to_vop_win(plane);
+
+		pstate = drm_atomic_get_plane_state(state, plane);
+		if (IS_ERR(pstate))
+			return PTR_ERR(pstate);
+		vop_plane_state = to_vop_plane_state(pstate);
+		if (!pstate->fb)
+			continue;
 
-	spin_lock_irqsave(&vop->irq_lock, flags);
+		if (vop_plane_state->eotf > s->eotf)
+			if (win->feature & WIN_FEATURE_HDR2SDR)
+				hdr2sdr_en = 1;
+		if (vop_plane_state->eotf < s->eotf) {
+			if (win->feature & WIN_FEATURE_PRE_OVERLAY)
+				pre_sdr2hdr_state |= BIT(plane_id);
+			else
+				post_sdr2hdr_state |= BIT(plane_id);
+		}
+		plane_id++;
+	}
 
-	VOP_INTR_SET_TYPE(vop, clear, DSP_HOLD_VALID_INTR, 1);
-	VOP_INTR_SET_TYPE(vop, enable, DSP_HOLD_VALID_INTR, 1);
+	if (pre_sdr2hdr_state || post_sdr2hdr_state || hdr2sdr_en) {
+		pre_overlay = true;
+		pre_sdr2hdr_mode = BT709_TO_BT2020;
+		post_sdr2hdr_mode = BT709_TO_BT2020;
+		sdr2hdr_func = SDR2HDR_FOR_HDR;
+		goto exit_hdr_convert;
+	}
 
-	spin_unlock_irqrestore(&vop->irq_lock, flags);
-}
+	/* overlay mode */
+	plane_id = 0;
+	pre_overlay = false;
+	pre_sdr2hdr_mode = 0;
+	post_sdr2hdr_mode = 0;
+	pre_sdr2hdr_state = 0;
+	post_sdr2hdr_state = 0;
+	drm_atomic_crtc_state_for_each_plane(plane, crtc_state) {
+		struct vop_plane_state *vop_plane_state;
+		struct vop_win *win = to_vop_win(plane);
+
+		pstate = drm_atomic_get_plane_state(state, plane);
+		if (IS_ERR(pstate))
+			return PTR_ERR(pstate);
+		vop_plane_state = to_vop_plane_state(pstate);
+		if (!pstate->fb)
+			continue;
 
-static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
-{
-	unsigned long flags;
+		if (vop_plane_state->color_space == V4L2_COLORSPACE_BT2020 &&
+		    vop_plane_state->color_space > s->color_space) {
+			if (win->feature & WIN_FEATURE_PRE_OVERLAY) {
+				pre_sdr2hdr_mode = BT2020_TO_BT709;
+				pre_sdr2hdr_state |= BIT(plane_id);
+			} else {
+				post_sdr2hdr_mode = BT2020_TO_BT709;
+				post_sdr2hdr_state |= BIT(plane_id);
+			}
+		}
+		if (s->color_space == V4L2_COLORSPACE_BT2020 &&
+		    vop_plane_state->color_space < s->color_space) {
+			if (win->feature & WIN_FEATURE_PRE_OVERLAY) {
+				pre_sdr2hdr_mode = BT709_TO_BT2020;
+				pre_sdr2hdr_state |= BIT(plane_id);
+			} else {
+				post_sdr2hdr_mode = BT709_TO_BT2020;
+				post_sdr2hdr_state |= BIT(plane_id);
+			}
+		}
+		plane_id++;
+	}
 
-	if (WARN_ON(!vop->is_enabled))
-		return;
+	if (pre_sdr2hdr_state || post_sdr2hdr_state) {
+		pre_overlay = true;
+		sdr2hdr_func = SDR2HDR_FOR_BT2020;
+	}
 
-	spin_lock_irqsave(&vop->irq_lock, flags);
+exit_hdr_convert:
+	s->hdr.pre_overlay = pre_overlay;
+	s->hdr.hdr2sdr_en = hdr2sdr_en;
+	if (s->hdr.pre_overlay)
+		s->yuv_overlay = 0;
 
-	VOP_INTR_SET_TYPE(vop, enable, DSP_HOLD_VALID_INTR, 0);
+	s->hdr.sdr2hdr_state.bt1886eotf_pre_conv_en = !!pre_sdr2hdr_state;
+	s->hdr.sdr2hdr_state.rgb2rgb_pre_conv_en = !!pre_sdr2hdr_state;
+	s->hdr.sdr2hdr_state.rgb2rgb_pre_conv_mode = pre_sdr2hdr_mode;
+	s->hdr.sdr2hdr_state.st2084oetf_pre_conv_en = !!pre_sdr2hdr_state;
 
-	spin_unlock_irqrestore(&vop->irq_lock, flags);
+	s->hdr.sdr2hdr_state.bt1886eotf_post_conv_en = !!post_sdr2hdr_state;
+	s->hdr.sdr2hdr_state.rgb2rgb_post_conv_en = !!post_sdr2hdr_state;
+	s->hdr.sdr2hdr_state.rgb2rgb_post_conv_mode = post_sdr2hdr_mode;
+	s->hdr.sdr2hdr_state.st2084oetf_post_conv_en = !!post_sdr2hdr_state;
+	s->hdr.sdr2hdr_state.sdr2hdr_func = sdr2hdr_func;
+
+	return 0;
+}
+
+static int to_vop_csc_mode(int csc_mode)
+{
+	switch (csc_mode) {
+	case V4L2_COLORSPACE_SMPTE170M:
+	case V4L2_COLORSPACE_470_SYSTEM_M:
+	case V4L2_COLORSPACE_470_SYSTEM_BG:
+		return CSC_BT601L;
+	case V4L2_COLORSPACE_REC709:
+	case V4L2_COLORSPACE_SMPTE240M:
+	case V4L2_COLORSPACE_DEFAULT:
+		return CSC_BT709L;
+	case V4L2_COLORSPACE_JPEG:
+		return CSC_BT601F;
+	case V4L2_COLORSPACE_BT2020:
+		return CSC_BT2020;
+	default:
+		return CSC_BT709L;
+	}
+}
+
+static void vop_disable_all_planes(struct vop *vop)
+{
+	bool active;
+	int ret;
+
+	vop_disable_allwin(vop);
+	vop_cfg_done(vop);
+	ret = readx_poll_timeout_atomic(vop_is_allwin_disabled,
+					vop, active, active,
+					0, 500 * 1000);
+	if (ret)
+		dev_err(vop->dev, "wait win close timeout\n");
 }
 
 /*
- * (1) each frame starts at the start of the Vsync pulse which is signaled by
- *     the "FRAME_SYNC" interrupt.
- * (2) the active data region of each frame ends at dsp_vact_end
- * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
- *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
+ * rk3399 colorspace path:
+ *      Input        Win csc                     Output
+ * 1. YUV(2020)  --> Y2R->2020To709->R2Y   --> YUV_OUTPUT(601/709)
+ *    RGB        --> R2Y                  __/
  *
- * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
- * Interrupts
- * LINE_FLAG -------------------------------+
- * FRAME_SYNC ----+                         |
+ * 2. YUV(2020)  --> bypasss               --> YUV_OUTPUT(2020)
+ *    RGB        --> 709To2020->R2Y       __/
+ *
+ * 3. YUV(2020)  --> Y2R->2020To709        --> RGB_OUTPUT(709)
+ *    RGB        --> R2Y                  __/
+ *
+ * 4. YUV(601/709)-> Y2R->709To2020->R2Y   --> YUV_OUTPUT(2020)
+ *    RGB        --> 709To2020->R2Y       __/
+ *
+ * 5. YUV(601/709)-> bypass                --> YUV_OUTPUT(709)
+ *    RGB        --> R2Y                  __/
+ *
+ * 6. YUV(601/709)-> bypass                --> YUV_OUTPUT(601)
+ *    RGB        --> R2Y(601)             __/
+ *
+ * 7. YUV        --> Y2R(709)              --> RGB_OUTPUT(709)
+ *    RGB        --> bypass               __/
+ *
+ * 8. RGB        --> 709To2020->R2Y        --> YUV_OUTPUT(2020)
+ *
+ * 9. RGB        --> R2Y(709)              --> YUV_OUTPUT(709)
+ *
+ * 10. RGB       --> R2Y(601)              --> YUV_OUTPUT(601)
+ *
+ * 11. RGB       --> bypass                --> RGB_OUTPUT(709)
+ */
+static int vop_setup_csc_table(const struct vop_csc_table *csc_table,
+			       bool is_input_yuv, bool is_output_yuv,
+			       int input_csc, int output_csc,
+			       const uint32_t **y2r_table,
+			       const uint32_t **r2r_table,
+			       const uint32_t **r2y_table)
+{
+	*y2r_table = NULL;
+	*r2r_table = NULL;
+	*r2y_table = NULL;
+
+	if (!csc_table)
+		return 0;
+
+	if (is_output_yuv) {
+		if (output_csc == V4L2_COLORSPACE_BT2020) {
+			if (is_input_yuv) {
+				if (input_csc == V4L2_COLORSPACE_BT2020)
+					return 0;
+				*y2r_table = csc_table->y2r_bt709;
+			}
+			if (input_csc != V4L2_COLORSPACE_BT2020)
+				*r2r_table = csc_table->r2r_bt709_to_bt2020;
+			*r2y_table = csc_table->r2y_bt2020;
+		} else {
+			if (is_input_yuv && input_csc == V4L2_COLORSPACE_BT2020)
+				*y2r_table = csc_table->y2r_bt2020;
+			if (input_csc == V4L2_COLORSPACE_BT2020)
+				*r2r_table = csc_table->r2r_bt2020_to_bt709;
+			if (!is_input_yuv || *y2r_table) {
+				if (output_csc == V4L2_COLORSPACE_REC709 ||
+				    output_csc == V4L2_COLORSPACE_SMPTE240M ||
+				    output_csc == V4L2_COLORSPACE_DEFAULT)
+					*r2y_table = csc_table->r2y_bt709;
+				else if (output_csc == V4L2_COLORSPACE_SMPTE170M ||
+					 output_csc == V4L2_COLORSPACE_470_SYSTEM_M ||
+					 output_csc == V4L2_COLORSPACE_470_SYSTEM_BG)
+					*r2y_table = csc_table->r2y_bt601_12_235; /* bt601 limit */
+				else
+					*r2y_table = csc_table->r2y_bt601; /* bt601 full */
+			}
+		}
+	} else {
+		if (!is_input_yuv)
+			return 0;
+
+		/*
+		 * is possible use bt2020 on rgb mode?
+		 */
+		if (WARN_ON(output_csc == V4L2_COLORSPACE_BT2020))
+			return -EINVAL;
+
+		if (input_csc == V4L2_COLORSPACE_BT2020)
+			*y2r_table = csc_table->y2r_bt2020;
+		else if (input_csc == V4L2_COLORSPACE_REC709 ||
+			 input_csc == V4L2_COLORSPACE_SMPTE240M ||
+			 input_csc == V4L2_COLORSPACE_DEFAULT)
+			*y2r_table = csc_table->y2r_bt709;
+		else if (input_csc == V4L2_COLORSPACE_SMPTE170M ||
+			 input_csc == V4L2_COLORSPACE_470_SYSTEM_M ||
+			 input_csc == V4L2_COLORSPACE_470_SYSTEM_BG)
+			*y2r_table = csc_table->y2r_bt601_12_235; /* bt601 limit */
+		else
+			*y2r_table = csc_table->y2r_bt601;  /* bt601 full */
+
+		if (input_csc == V4L2_COLORSPACE_BT2020)
+			/*
+			 * We don't have bt601 to bt709 table, force use bt709.
+			 */
+			*r2r_table = csc_table->r2r_bt2020_to_bt709;
+	}
+
+	return 0;
+}
+
+static void vop_setup_csc_mode(bool is_input_yuv, bool is_output_yuv,
+			       int input_csc, int output_csc,
+			       bool *y2r_en, bool *r2y_en, int *csc_mode)
+{
+	if (is_input_yuv && !is_output_yuv) {
+		*y2r_en = true;
+		*csc_mode =  to_vop_csc_mode(input_csc);
+	} else if (!is_input_yuv && is_output_yuv) {
+		*r2y_en = true;
+		*csc_mode = to_vop_csc_mode(output_csc);
+	}
+}
+
+static int vop_csc_atomic_check(struct drm_crtc *crtc,
+				struct drm_crtc_state *crtc_state)
+{
+	struct vop *vop = to_vop(crtc);
+	struct drm_atomic_state *state = crtc_state->state;
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+	const struct vop_csc_table *csc_table = vop->data->csc_table;
+	struct drm_plane_state *pstate;
+	struct drm_plane *plane;
+	bool is_input_yuv, is_output_yuv;
+	int ret;
+
+	is_output_yuv = is_yuv_output(s->bus_format);
+
+	drm_atomic_crtc_state_for_each_plane(plane, crtc_state) {
+		struct vop_plane_state *vop_plane_state;
+		struct vop_win *win = to_vop_win(plane);
+
+		pstate = drm_atomic_get_plane_state(state, plane);
+		if (IS_ERR(pstate))
+			return PTR_ERR(pstate);
+		vop_plane_state = to_vop_plane_state(pstate);
+
+		if (!pstate->fb)
+			continue;
+		is_input_yuv = is_yuv_support(pstate->fb->format->format);
+		vop_plane_state->y2r_en = false;
+		vop_plane_state->r2r_en = false;
+		vop_plane_state->r2y_en = false;
+
+		ret = vop_setup_csc_table(csc_table, is_input_yuv,
+					  is_output_yuv,
+					  vop_plane_state->color_space,
+					  s->color_space,
+					  &vop_plane_state->y2r_table,
+					  &vop_plane_state->r2r_table,
+					  &vop_plane_state->r2y_table);
+		if (ret)
+			return ret;
+
+		vop_setup_csc_mode(is_input_yuv, s->yuv_overlay,
+				   vop_plane_state->color_space, s->color_space,
+				   &vop_plane_state->y2r_en,
+				   &vop_plane_state->r2y_en,
+				   &vop_plane_state->csc_mode);
+
+		if (csc_table) {
+			vop_plane_state->y2r_en = !!vop_plane_state->y2r_table;
+			vop_plane_state->r2r_en = !!vop_plane_state->r2r_table;
+			vop_plane_state->r2y_en = !!vop_plane_state->r2y_table;
+			continue;
+		}
+
+		/*
+		 * This is update for IC design not reasonable, when enable
+		 * hdr2sdr on rk3328, vop can't support per-pixel alpha * global
+		 * alpha,so we must back to gpu, but gpu can't support hdr2sdr,
+		 * gpu output hdr UI, vop will do:
+		 * UI(rgbx) -> yuv -> rgb ->hdr2sdr -> overlay -> output.
+		 */
+		if (s->hdr.hdr2sdr_en &&
+		    vop_plane_state->eotf == HDMI_EOTF_SMPTE_ST2084 &&
+		    !is_yuv_support(pstate->fb->format->format))
+			vop_plane_state->r2y_en = true;
+		if (win->feature & WIN_FEATURE_PRE_OVERLAY)
+			vop_plane_state->r2r_en =
+				s->hdr.sdr2hdr_state.rgb2rgb_pre_conv_en;
+		else if (win->feature & WIN_FEATURE_HDR2SDR)
+			vop_plane_state->r2r_en =
+				s->hdr.sdr2hdr_state.rgb2rgb_post_conv_en;
+	}
+
+	return 0;
+}
+
+static void vop_enable_debug_irq(struct drm_crtc *crtc)
+{
+	struct vop *vop = to_vop(crtc);
+	uint32_t irqs;
+
+	irqs = BUS_ERROR_INTR | WIN0_EMPTY_INTR | WIN1_EMPTY_INTR |
+		WIN2_EMPTY_INTR | WIN3_EMPTY_INTR | HWC_EMPTY_INTR |
+		POST_BUF_EMPTY_INTR;
+	VOP_INTR_SET_TYPE(vop, clear, irqs, 1);
+	VOP_INTR_SET_TYPE(vop, enable, irqs, 1);
+}
+
+static void vop_dsp_hold_valid_irq_enable(struct vop *vop)
+{
+	unsigned long flags;
+
+	if (WARN_ON(!vop->is_enabled))
+		return;
+
+	spin_lock_irqsave(&vop->irq_lock, flags);
+
+	VOP_INTR_SET_TYPE(vop, clear, DSP_HOLD_VALID_INTR, 1);
+	VOP_INTR_SET_TYPE(vop, enable, DSP_HOLD_VALID_INTR, 1);
+
+	spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
+static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
+{
+	unsigned long flags;
+
+	if (WARN_ON(!vop->is_enabled))
+		return;
+
+	spin_lock_irqsave(&vop->irq_lock, flags);
+
+	VOP_INTR_SET_TYPE(vop, enable, DSP_HOLD_VALID_INTR, 0);
+
+	spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
+/*
+ * (1) each frame starts at the start of the Vsync pulse which is signaled by
+ *     the "FRAME_SYNC" interrupt.
+ * (2) the active data region of each frame ends at dsp_vact_end
+ * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
+ *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
+ *
+ * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
+ * Interrupts
+ * LINE_FLAG -------------------------------+
+ * FRAME_SYNC ----+                         |
  *                |                         |
  *                v                         v
  *                | Vsync | Vbp |  Vactive  | Vfp |
@@ -567,147 +1380,217 @@ static void vop_core_clks_disable(struct vop *vop)
 	clk_disable(vop->hclk);
 }
 
-static void vop_win_disable(struct vop *vop, const struct vop_win *vop_win)
+static void vop_crtc_load_lut(struct drm_crtc *crtc)
 {
-	const struct vop_win_data *win = vop_win->data;
+	struct vop *vop = to_vop(crtc);
+	int i, dle, lut_idx = 0;
 
-	if (win->phy->scl && win->phy->scl->ext) {
-		VOP_SCL_SET_EXT(vop, win, yrgb_hor_scl_mode, SCALE_NONE);
-		VOP_SCL_SET_EXT(vop, win, yrgb_ver_scl_mode, SCALE_NONE);
-		VOP_SCL_SET_EXT(vop, win, cbcr_hor_scl_mode, SCALE_NONE);
-		VOP_SCL_SET_EXT(vop, win, cbcr_ver_scl_mode, SCALE_NONE);
+	if (!vop->is_enabled || !vop->lut || !vop->lut_regs)
+		return;
+
+	if (WARN_ON(!drm_modeset_is_locked(&crtc->mutex)))
+		return;
+
+	if (!VOP_CTRL_SUPPORT(vop, update_gamma_lut)) {
+		spin_lock(&vop->reg_lock);
+		VOP_CTRL_SET(vop, dsp_lut_en, 0);
+		vop_cfg_done(vop);
+		spin_unlock(&vop->reg_lock);
+
+#define CTRL_GET(name) VOP_CTRL_GET(vop, name)
+		readx_poll_timeout(CTRL_GET, dsp_lut_en,
+				dle, !dle, 5, 33333);
+	} else {
+		lut_idx = CTRL_GET(lut_buffer_index);
 	}
 
-	VOP_WIN_SET(vop, win, enable, 0);
-	vop->win_enabled &= ~BIT(VOP_WIN_TO_INDEX(vop_win));
+	for (i = 0; i < vop->lut_len; i++)
+		vop_write_lut(vop, i << 2, vop->lut[i]);
+
+	spin_lock(&vop->reg_lock);
+
+	VOP_CTRL_SET(vop, dsp_lut_en, 1);
+	VOP_CTRL_SET(vop, update_gamma_lut, 1);
+	vop_cfg_done(vop);
+	vop->lut_active = true;
+
+	spin_unlock(&vop->reg_lock);
+
+	if (VOP_CTRL_SUPPORT(vop, update_gamma_lut)) {
+		readx_poll_timeout(CTRL_GET, lut_buffer_index,
+				   dle, dle != lut_idx, 5, 33333);
+		/* FIXME:
+		 * update_gamma value auto clean to 0 by HW, should not
+		 * bakeup it.
+		 */
+		VOP_CTRL_SET(vop, update_gamma_lut, 0);
+	}
+#undef CTRL_GET
 }
 
-static int vop_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
+static void rockchip_vop_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red,
+					   u16 green, u16 blue, int regno)
 {
 	struct vop *vop = to_vop(crtc);
-	int ret, i;
+	u32 lut_len = vop->lut_len;
+	u32 r, g, b;
 
-	ret = pm_runtime_get_sync(vop->dev);
-	if (ret < 0) {
-		DRM_DEV_ERROR(vop->dev, "failed to get pm runtime: %d\n", ret);
-		return ret;
-	}
+	if (regno >= lut_len || !vop->lut)
+		return;
 
-	ret = vop_core_clks_enable(vop);
-	if (WARN_ON(ret < 0))
-		goto err_put_pm_runtime;
+	r = red * (lut_len - 1) / 0xffff;
+	g = green * (lut_len - 1) / 0xffff;
+	b = blue * (lut_len - 1) / 0xffff;
+	vop->lut[regno] = r * lut_len * lut_len + g * lut_len + b;
+}
 
-	ret = clk_enable(vop->dclk);
-	if (WARN_ON(ret < 0))
-		goto err_disable_core;
+static void rockchip_vop_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red,
+					   u16 *green, u16 *blue, int regno)
+{
+	struct vop *vop = to_vop(crtc);
+	u32 lut_len = vop->lut_len;
+	u32 r, g, b;
 
-	/*
-	 * Slave iommu shares power, irq and clock with vop.  It was associated
-	 * automatically with this master device via common driver code.
-	 * Now that we have enabled the clock we attach it to the shared drm
-	 * mapping.
-	 */
-	ret = rockchip_drm_dma_attach_device(vop->drm_dev, vop->dev);
-	if (ret) {
-		DRM_DEV_ERROR(vop->dev,
-			      "failed to attach dma mapping, %d\n", ret);
-		goto err_disable_dclk;
-	}
+	if (regno >= lut_len || !vop->lut)
+		return;
 
-	spin_lock(&vop->reg_lock);
-	for (i = 0; i < vop->len; i += 4)
-		writel_relaxed(vop->regsbak[i / 4], vop->regs + i);
+	r = (vop->lut[regno] / lut_len / lut_len) & (lut_len - 1);
+	g = (vop->lut[regno] / lut_len) & (lut_len - 1);
+	b = vop->lut[regno] & (lut_len - 1);
+	*red = r * 0xffff / (lut_len - 1);
+	*green = g * 0xffff / (lut_len - 1);
+	*blue = b * 0xffff / (lut_len - 1);
+}
 
-	/*
-	 * We need to make sure that all windows are disabled before we
-	 * enable the crtc. Otherwise we might try to scan from a destroyed
-	 * buffer later.
-	 *
-	 * In the case of enable-after-PSR, we don't need to worry about this
-	 * case since the buffer is guaranteed to be valid and disabling the
-	 * window will result in screen glitches on PSR exit.
-	 */
-	if (!old_state || !old_state->self_refresh_active) {
-		for (i = 0; i < vop->data->win_size; i++) {
-			struct vop_win *vop_win = &vop->win[i];
+static int vop_crtc_legacy_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
+				     u16 *blue, uint32_t size,
+				     struct drm_modeset_acquire_ctx *ctx)
+{
+	struct vop *vop = to_vop(crtc);
+	int len = min(size, vop->lut_len);
+	int i;
 
-			vop_win_disable(vop, vop_win);
-		}
-	}
+	if (!vop->lut)
+		return -EINVAL;
 
-	if (vop->data->afbc) {
-		struct rockchip_crtc_state *s;
-		/*
-		 * Disable AFBC and forget there was a vop window with AFBC
-		 */
-		VOP_AFBC_SET(vop, enable, 0);
-		s = to_rockchip_crtc_state(crtc->state);
-		s->enable_afbc = false;
+	for (i = 0; i < len; i++)
+		rockchip_vop_crtc_fb_gamma_set(crtc, red[i], green[i], blue[i], i);
+
+	vop_crtc_load_lut(crtc);
+
+	return 0;
+}
+
+static int vop_crtc_atomic_gamma_set(struct drm_crtc *crtc,
+				     struct drm_crtc_state *old_state)
+{
+	struct vop *vop = to_vop(crtc);
+	struct drm_color_lut *lut = vop->gamma_lut;
+	unsigned int i;
+
+	for (i = 0; i < vop->lut_len; i++)
+		rockchip_vop_crtc_fb_gamma_set(crtc, lut[i].red, lut[i].green,
+					       lut[i].blue, i);
+	vop_crtc_load_lut(crtc);
+
+	return 0;
+}
+
+static void vop_power_enable(struct drm_crtc *crtc)
+{
+	struct vop *vop = to_vop(crtc);
+	int ret;
+
+	ret = clk_prepare_enable(vop->hclk);
+	if (ret < 0) {
+		dev_err(vop->dev, "failed to enable hclk - %d\n", ret);
+		return;
 	}
 
-	vop_cfg_done(vop);
+	ret = clk_prepare_enable(vop->dclk);
+	if (ret < 0) {
+		dev_err(vop->dev, "failed to enable dclk - %d\n", ret);
+		goto err_disable_hclk;
+	}
 
-	spin_unlock(&vop->reg_lock);
+	ret = clk_prepare_enable(vop->aclk);
+	if (ret < 0) {
+		dev_err(vop->dev, "failed to enable aclk - %d\n", ret);
+		goto err_disable_dclk;
+	}
 
-	/*
-	 * At here, vop clock & iommu is enable, R/W vop regs would be safe.
-	 */
-	vop->is_enabled = true;
+	ret = pm_runtime_get_sync(vop->dev);
+	if (ret < 0) {
+		dev_err(vop->dev, "failed to get pm runtime: %d\n", ret);
+		return;
+	}
 
-	spin_lock(&vop->reg_lock);
+	memcpy(vop->regsbak, vop->regs, vop->len);
 
-	VOP_REG_SET(vop, common, standby, 1);
+	if (VOP_CTRL_SUPPORT(vop, version)) {
+		uint32_t version = VOP_CTRL_GET(vop, version);
 
-	spin_unlock(&vop->reg_lock);
+		/*
+		 * Fixup rk3288w version.
+		 */
+		if (version && version == 0x0a05)
+			vop->version = VOP_VERSION(3, 1);
+	}
 
-	drm_crtc_vblank_on(crtc);
+	vop->is_enabled = true;
 
-	return 0;
+	return;
 
 err_disable_dclk:
-	clk_disable(vop->dclk);
-err_disable_core:
-	vop_core_clks_disable(vop);
-err_put_pm_runtime:
-	pm_runtime_put_sync(vop->dev);
-	return ret;
+	clk_disable_unprepare(vop->dclk);
+err_disable_hclk:
+	clk_disable_unprepare(vop->hclk);
 }
 
-static void rockchip_drm_set_win_enabled(struct drm_crtc *crtc, bool enabled)
+static void vop_initial(struct drm_crtc *crtc)
 {
-        struct vop *vop = to_vop(crtc);
-        int i;
+	struct vop *vop = to_vop(crtc);
+	int i;
 
-        spin_lock(&vop->reg_lock);
+	vop_power_enable(crtc);
 
-        for (i = 0; i < vop->data->win_size; i++) {
-                struct vop_win *vop_win = &vop->win[i];
-                const struct vop_win_data *win = vop_win->data;
+	VOP_CTRL_SET(vop, global_regdone_en, 1);
+	VOP_CTRL_SET(vop, dsp_blank, 0);
+	VOP_CTRL_SET(vop, axi_outstanding_max_num, 30);
+	VOP_CTRL_SET(vop, axi_max_outstanding_en, 1);
+	VOP_CTRL_SET(vop, dither_up_en, 1);
 
-                VOP_WIN_SET(vop, win, enable,
-                            enabled && (vop->win_enabled & BIT(i)));
-        }
-        vop_cfg_done(vop);
+	/*
+	 * We need to make sure that all windows are disabled before resume
+	 * the crtc. Otherwise we might try to scan from a destroyed
+	 * buffer later.
+	 */
+	for (i = 0; i < vop->num_wins; i++) {
+		struct vop_win *win = &vop->win[i];
+		int channel = i * 2 + 1;
 
-        spin_unlock(&vop->reg_lock);
+		VOP_WIN_SET(vop, win, channel, (channel + 1) << 4 | channel);
+	}
+	VOP_CTRL_SET(vop, afbdc_en, 0);
+	vop_enable_debug_irq(crtc);
 }
 
 static void vop_crtc_atomic_disable(struct drm_crtc *crtc,
 				    struct drm_crtc_state *old_state)
 {
 	struct vop *vop = to_vop(crtc);
+	int sys_status = drm_crtc_index(crtc) ?
+				SYS_STATUS_LCDC1 : SYS_STATUS_LCDC0;
 
 	WARN_ON(vop->event);
 
-	if (crtc->state->self_refresh_active)
-		rockchip_drm_set_win_enabled(crtc, false);
-
-	mutex_lock(&vop->vop_lock);
-
+	vop_lock(vop);
+	VOP_CTRL_SET(vop, reg_done_frm, 1);
+	VOP_CTRL_SET(vop, dsp_interlace, 0);
 	drm_crtc_vblank_off(crtc);
-
-	if (crtc->state->self_refresh_active)
-		goto out;
+	VOP_CTRL_SET(vop, out_mode, ROCKCHIP_OUT_MODE_P888);
+	VOP_CTRL_SET(vop, afbdc_en, 0);
+	vop_disable_all_planes(vop);
 
 	/*
 	 * Vop standby will take effect at end of current frame,
@@ -721,27 +1604,32 @@ static void vop_crtc_atomic_disable(struct drm_crtc *crtc,
 
 	spin_lock(&vop->reg_lock);
 
-	VOP_REG_SET(vop, common, standby, 1);
+	VOP_CTRL_SET(vop, standby, 1);
 
 	spin_unlock(&vop->reg_lock);
 
-	wait_for_completion(&vop->dsp_hold_completion);
+	WARN_ON(!wait_for_completion_timeout(&vop->dsp_hold_completion,
+					     msecs_to_jiffies(50)));
 
 	vop_dsp_hold_valid_irq_disable(vop);
 
 	vop->is_enabled = false;
+	if (vop->is_iommu_enabled) {
+		/*
+		 * vop standby complete, so iommu detach is safe.
+		 */
+		VOP_CTRL_SET(vop, dma_stop, 1);
+		rockchip_drm_dma_detach_device(vop->drm_dev, vop->dev);
+		vop->is_iommu_enabled = false;
+	}
 
-	/*
-	 * vop standby complete, so iommu detach is safe.
-	 */
-	rockchip_drm_dma_detach_device(vop->drm_dev, vop->dev);
-
-	clk_disable(vop->dclk);
-	vop_core_clks_disable(vop);
-	pm_runtime_put(vop->dev);
+	pm_runtime_put_sync(vop->dev);
+	clk_disable_unprepare(vop->dclk);
+	clk_disable_unprepare(vop->aclk);
+	clk_disable_unprepare(vop->hclk);
+	vop_unlock(vop);
 
-out:
-	mutex_unlock(&vop->vop_lock);
+	rockchip_clear_system_status(sys_status);
 
 	if (crtc->state->event && !crtc->state->active) {
 		spin_lock_irq(&crtc->dev->event_lock);
@@ -752,29 +1640,20 @@ static void vop_crtc_atomic_disable(struct drm_crtc *crtc,
 	}
 }
 
-static void vop_plane_destroy(struct drm_plane *plane)
+static int vop_plane_prepare_fb(struct drm_plane *plane,
+				struct drm_plane_state *new_state)
 {
-	drm_plane_cleanup(plane);
-}
+	if (plane->state->fb)
+		drm_framebuffer_get(plane->state->fb);
 
-static inline bool rockchip_afbc(u64 modifier)
-{
-	return modifier == ROCKCHIP_AFBC_MOD;
+	return 0;
 }
 
-static bool rockchip_mod_supported(struct drm_plane *plane,
-				   u32 format, u64 modifier)
+static void vop_plane_cleanup_fb(struct drm_plane *plane,
+				 struct drm_plane_state *old_state)
 {
-	if (modifier == DRM_FORMAT_MOD_LINEAR)
-		return true;
-
-	if (!rockchip_afbc(modifier)) {
-		DRM_DEBUG_KMS("Unsupported format modifier 0x%llx\n", modifier);
-
-		return false;
-	}
-
-	return vop_convert_afbc_format(format) >= 0;
+	if (old_state->fb)
+		drm_framebuffer_put(old_state->fb);
 }
 
 static int vop_plane_atomic_check(struct drm_plane *plane,
@@ -783,21 +1662,43 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
 	struct drm_crtc *crtc = state->crtc;
 	struct drm_crtc_state *crtc_state;
 	struct drm_framebuffer *fb = state->fb;
-	struct vop_win *vop_win = to_vop_win(plane);
-	const struct vop_win_data *win = vop_win->data;
+	struct vop_win *win = to_vop_win(plane);
+	struct vop_plane_state *vop_plane_state = to_vop_plane_state(state);
+	const struct vop_data *vop_data;
+	struct vop *vop;
 	int ret;
+	struct drm_rect *dest = &vop_plane_state->dest;
+	struct drm_rect *src = &vop_plane_state->src;
+	struct drm_gem_object *obj, *uv_obj;
+	struct rockchip_gem_object *rk_obj, *rk_uv_obj;
 	int min_scale = win->phy->scl ? FRAC_16_16(1, 8) :
 					DRM_PLANE_HELPER_NO_SCALING;
 	int max_scale = win->phy->scl ? FRAC_16_16(8, 1) :
 					DRM_PLANE_HELPER_NO_SCALING;
+	unsigned long offset;
+	dma_addr_t dma_addr;
 
-	if (!crtc || WARN_ON(!fb))
+	crtc = crtc ? crtc : plane->state->crtc;
+	if (!crtc || !fb) {
+		plane->state->visible = false;
 		return 0;
+	}
 
 	crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
 	if (WARN_ON(!crtc_state))
 		return -EINVAL;
 
+	src->x1 = state->src_x;
+	src->y1 = state->src_y;
+	src->x2 = state->src_x + state->src_w;
+	src->y2 = state->src_y + state->src_h;
+	dest->x1 = state->crtc_x;
+	dest->y1 = state->crtc_y;
+	dest->x2 = state->crtc_x + state->crtc_w;
+	dest->y2 = state->crtc_y + state->crtc_h;
+	vop_plane_state->zpos = state->zpos;
+	vop_plane_state->blend_mode = state->pixel_blend_mode;
+
 	ret = drm_atomic_helper_check_plane_state(state, crtc_state,
 						  min_scale, max_scale,
 						  true, true);
@@ -807,13 +1708,34 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
 	if (!state->visible)
 		return 0;
 
-	ret = vop_convert_format(fb->format->format);
-	if (ret < 0)
-		return ret;
+	vop_plane_state->format = vop_convert_format(fb->format->format);
+	if (vop_plane_state->format < 0)
+		return vop_plane_state->format;
 
-	/*
-	 * Src.x1 can be odd when do clip, but yuv plane start point
-	 * need align with 2 pixel.
+	vop = to_vop(crtc);
+	vop_data = vop->data;
+
+	if (state->src_w >> 16 < 4 || state->src_h >> 16 < 4 ||
+	    state->crtc_w < 4 || state->crtc_h < 4) {
+		DRM_ERROR("Invalid size: %dx%d->%dx%d, min size is 4x4\n",
+			  state->src_w >> 16, state->src_h >> 16,
+			  state->crtc_w, state->crtc_h);
+		return -EINVAL;
+	}
+
+	if (drm_rect_width(src) >> 16 > vop_data->max_input.width ||
+	    drm_rect_height(src) >> 16 > vop_data->max_input.height) {
+		DRM_ERROR("Invalid source: %dx%d. max input: %dx%d\n",
+			  drm_rect_width(src) >> 16,
+			  drm_rect_height(src) >> 16,
+			  vop_data->max_input.width,
+			  vop_data->max_input.height);
+		return -EINVAL;
+	}
+
+	/*
+	 * Src.x1 can be odd when do clip, but yuv plane start point
+	 * need align with 2 pixel.
 	 */
 	if (fb->format->is_yuv && ((state->src.x1 >> 16) % 2)) {
 		DRM_ERROR("Invalid Source: Yuv format not support odd xpos\n");
@@ -825,28 +1747,28 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
 		return -EINVAL;
 	}
 
-	if (rockchip_afbc(fb->modifier)) {
-		struct vop *vop = to_vop(crtc);
+	offset = (src->x1 >> 16) * fb->format->cpp[0];
+	vop_plane_state->offset = offset + fb->offsets[0];
+	if (state->rotation & DRM_MODE_REFLECT_Y)
+		offset += ((src->y2 >> 16) - 1) * fb->pitches[0];
+	else
+		offset += (src->y1 >> 16) * fb->pitches[0];
 
-		if (!vop->data->afbc) {
-			DRM_ERROR("vop does not support AFBC\n");
-			return -EINVAL;
-		}
+	obj = fb->obj[0];
+	rk_obj = to_rockchip_obj(obj);
+	vop_plane_state->yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
+	if (fb->format->is_yuv) {
+		int hsub = fb->format->hsub;
+		int vsub = fb->format->vsub;
 
-		ret = vop_convert_afbc_format(fb->format->format);
-		if (ret < 0)
-			return ret;
+		offset = (src->x1 >> 16) * fb->format->cpp[1] / hsub;
+		offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
 
-		if (state->src.x1 || state->src.y1) {
-			DRM_ERROR("AFBC does not support offset display, xpos=%d, ypos=%d, offset=%d\n", state->src.x1, state->src.y1, fb->offsets[0]);
-			return -EINVAL;
-		}
+		uv_obj = fb->obj[1];
+		rk_uv_obj = to_rockchip_obj(uv_obj);
 
-		if (state->rotation && state->rotation != DRM_MODE_ROTATE_0) {
-			DRM_ERROR("No rotation support in AFBC, rotation=%d\n",
-				  state->rotation);
-			return -EINVAL;
-		}
+		dma_addr = rk_uv_obj->dma_addr + offset + fb->offsets[1];
+		vop_plane_state->uv_mst = dma_addr;
 	}
 
 	return 0;
@@ -855,15 +1777,33 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
 static void vop_plane_atomic_disable(struct drm_plane *plane,
 				     struct drm_plane_state *old_state)
 {
-	struct vop_win *vop_win = to_vop_win(plane);
+	struct vop_win *win = to_vop_win(plane);
 	struct vop *vop = to_vop(old_state->crtc);
+#if defined(CONFIG_ROCKCHIP_DRM_DEBUG)
+	struct vop_plane_state *vop_plane_state =
+					to_vop_plane_state(plane->state);
+#endif
 
 	if (!old_state->crtc)
 		return;
 
 	spin_lock(&vop->reg_lock);
 
-	vop_win_disable(vop, vop_win);
+	vop_win_disable(vop, win);
+
+	/*
+	 * IC design bug: in the bandwidth tension environment when close win2,
+	 * vop will access the freed memory lead to iommu pagefault.
+	 * so we add this reset to workaround.
+	 */
+	if (VOP_MAJOR(vop->version) == 2 && VOP_MINOR(vop->version) == 5 &&
+	    win->win_id == 2)
+		VOP_WIN_SET(vop, win, yrgb_mst, 0);
+
+#if defined(CONFIG_ROCKCHIP_DRM_DEBUG)
+	kfree(vop_plane_state->planlist);
+	vop_plane_state->planlist = NULL;
+#endif
 
 	spin_unlock(&vop->reg_lock);
 }
@@ -873,26 +1813,46 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
 {
 	struct drm_plane_state *state = plane->state;
 	struct drm_crtc *crtc = state->crtc;
-	struct vop_win *vop_win = to_vop_win(plane);
-	const struct vop_win_data *win = vop_win->data;
-	const struct vop_win_yuv2yuv_data *win_yuv2yuv = vop_win->yuv2yuv_data;
+	struct drm_display_mode *mode = NULL;
+	struct vop_win *win = to_vop_win(plane);
+	struct vop_plane_state *vop_plane_state = to_vop_plane_state(state);
+	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
+	struct rockchip_crtc_state *s;
 	struct vop *vop = to_vop(state->crtc);
 	struct drm_framebuffer *fb = state->fb;
-	unsigned int actual_w, actual_h;
+	unsigned int actual_w, actual_h, dsp_w, dsp_h;
 	unsigned int dsp_stx, dsp_sty;
 	uint32_t act_info, dsp_info, dsp_st;
-	struct drm_rect *src = &state->src;
-	struct drm_rect *dest = &state->dst;
-	struct drm_gem_object *obj, *uv_obj;
-	struct rockchip_gem_object *rk_obj, *rk_uv_obj;
-	unsigned long offset;
-	dma_addr_t dma_addr;
+	struct drm_rect *src = &vop_plane_state->src;
+	struct drm_rect *dest = &vop_plane_state->dest;
+	const uint32_t *y2r_table = vop_plane_state->y2r_table;
+	const uint32_t *r2r_table = vop_plane_state->r2r_table;
+	const uint32_t *r2y_table = vop_plane_state->r2y_table;
 	uint32_t val;
-	bool rb_swap;
-	int win_index = VOP_WIN_TO_INDEX(vop_win);
-	int format;
+	bool rb_swap, global_alpha_en;
 	int is_yuv = fb->format->is_yuv;
-	int i;
+
+#if defined(CONFIG_ROCKCHIP_DRM_DEBUG)
+	bool AFBC_flag = false;
+	struct vop_dump_list *planlist;
+	unsigned long num_pages;
+	struct page **pages;
+	struct drm_gem_object *obj;
+	struct rockchip_gem_object *rk_obj;
+
+	num_pages = 0;
+	pages = NULL;
+	obj = fb->obj[0];
+	rk_obj = to_rockchip_obj(obj);
+	if (rk_obj) {
+		num_pages = rk_obj->num_pages;
+		pages = rk_obj->pages;
+	}
+	if (fb->modifier == DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16))
+		AFBC_flag = true;
+	else
+		AFBC_flag = false;
+#endif
 
 	/*
 	 * can't update plane when vop is disabled.
@@ -908,206 +1868,426 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
 		return;
 	}
 
-	obj = fb->obj[0];
-	rk_obj = to_rockchip_obj(obj);
-
+	mode = &crtc->state->adjusted_mode;
 	actual_w = drm_rect_width(src) >> 16;
 	actual_h = drm_rect_height(src) >> 16;
-	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
-
-	dsp_info = (drm_rect_height(dest) - 1) << 16;
-	dsp_info |= (drm_rect_width(dest) - 1) & 0xffff;
 
-	dsp_stx = dest->x1 + crtc->mode.htotal - crtc->mode.hsync_start;
-	dsp_sty = dest->y1 + crtc->mode.vtotal - crtc->mode.vsync_start;
-	dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff);
+	dsp_w = drm_rect_width(dest);
+	if (dest->x1 + dsp_w > adjusted_mode->hdisplay) {
+		DRM_ERROR("%s win%d dest->x1[%d] + dsp_w[%d] exceed mode hdisplay[%d]\n",
+			  crtc->name, win->win_id, dest->x1, dsp_w, adjusted_mode->hdisplay);
+		dsp_w = adjusted_mode->hdisplay - dest->x1;
+		if (dsp_w < 4)
+			dsp_w = 4;
+		actual_w = dsp_w * actual_w / drm_rect_width(dest);
+	}
+	dsp_h = drm_rect_height(dest);
+	if (dest->y1 + dsp_h > adjusted_mode->vdisplay) {
+		DRM_ERROR("%s win%d dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n",
+			  crtc->name, win->win_id, dest->y1, dsp_h, adjusted_mode->vdisplay);
+		dsp_h = adjusted_mode->vdisplay - dest->y1;
+		if (dsp_h < 4)
+			dsp_h = 4;
+		actual_h = dsp_h * actual_h / drm_rect_height(dest);
+	}
 
-	offset = (src->x1 >> 16) * fb->format->cpp[0];
-	offset += (src->y1 >> 16) * fb->pitches[0];
-	dma_addr = rk_obj->dma_addr + offset + fb->offsets[0];
+	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
 
-	/*
-	 * For y-mirroring we need to move address
-	 * to the beginning of the last line.
-	 */
-	if (state->rotation & DRM_MODE_REFLECT_Y)
-		dma_addr += (actual_h - 1) * fb->pitches[0];
+	dsp_info = (dsp_h - 1) << 16;
+	dsp_info |= (dsp_w - 1) & 0xffff;
 
-	format = vop_convert_format(fb->format->format);
+	dsp_stx = dest->x1 + mode->crtc_htotal - mode->crtc_hsync_start;
+	dsp_sty = dest->y1 + mode->crtc_vtotal - mode->crtc_vsync_start;
+	dsp_st = dsp_sty << 16 | (dsp_stx & 0xffff);
 
+	s = to_rockchip_crtc_state(crtc->state);
 	spin_lock(&vop->reg_lock);
 
-	if (rockchip_afbc(fb->modifier)) {
-		int afbc_format = vop_convert_afbc_format(fb->format->format);
-
-		VOP_AFBC_SET(vop, format, afbc_format | AFBC_TILE_16x16);
-		VOP_AFBC_SET(vop, hreg_block_split, 0);
-		VOP_AFBC_SET(vop, win_sel, VOP_WIN_TO_INDEX(vop_win));
-		VOP_AFBC_SET(vop, hdr_ptr, dma_addr);
-		VOP_AFBC_SET(vop, pic_size, act_info);
-	}
-
-	VOP_WIN_SET(vop, win, format, format);
+	VOP_WIN_SET(vop, win, format, vop_plane_state->format);
 	VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4));
-	VOP_WIN_SET(vop, win, yrgb_mst, dma_addr);
-	VOP_WIN_YUV2YUV_SET(vop, win_yuv2yuv, y2r_en, is_yuv);
-	VOP_WIN_SET(vop, win, y_mir_en,
+	VOP_WIN_SET(vop, win, yrgb_mst, vop_plane_state->yrgb_mst);
+
+	VOP_WIN_SET(vop, win, ymirror,
 		    (state->rotation & DRM_MODE_REFLECT_Y) ? 1 : 0);
-	VOP_WIN_SET(vop, win, x_mir_en,
+	VOP_WIN_SET(vop, win, xmirror,
 		    (state->rotation & DRM_MODE_REFLECT_X) ? 1 : 0);
 
 	if (is_yuv) {
-		int hsub = fb->format->hsub;
-		int vsub = fb->format->vsub;
-		int bpp = fb->format->cpp[1];
-
-		uv_obj = fb->obj[1];
-		rk_uv_obj = to_rockchip_obj(uv_obj);
-
-		offset = (src->x1 >> 16) * bpp / hsub;
-		offset += (src->y1 >> 16) * fb->pitches[1] / vsub;
-
-		dma_addr = rk_uv_obj->dma_addr + offset + fb->offsets[1];
 		VOP_WIN_SET(vop, win, uv_vir, DIV_ROUND_UP(fb->pitches[1], 4));
-		VOP_WIN_SET(vop, win, uv_mst, dma_addr);
-
-		for (i = 0; i < NUM_YUV2YUV_COEFFICIENTS; i++) {
-			VOP_WIN_YUV2YUV_COEFFICIENT_SET(vop,
-							win_yuv2yuv,
-							y2r_coefficients[i],
-							bt601_yuv2rgb[i]);
-		}
+		VOP_WIN_SET(vop, win, uv_mst, vop_plane_state->uv_mst);
 	}
+	VOP_WIN_SET(vop, win, fmt_10, is_yuv_10bit(fb->format->format));
+	VOP_WIN_SET(vop, win, fmt_yuyv, is_yuyv_format(fb->format->format));
 
 	if (win->phy->scl)
 		scl_vop_cal_scl_fac(vop, win, actual_w, actual_h,
 				    drm_rect_width(dest), drm_rect_height(dest),
-				    fb->format);
+				    fb->format->format);
 
 	VOP_WIN_SET(vop, win, act_info, act_info);
 	VOP_WIN_SET(vop, win, dsp_info, dsp_info);
 	VOP_WIN_SET(vop, win, dsp_st, dsp_st);
 
 	rb_swap = has_rb_swapped(fb->format->format);
-	VOP_WIN_SET(vop, win, rb_swap, rb_swap);
-
 	/*
-	 * Blending win0 with the background color doesn't seem to work
-	 * correctly. We only get the background color, no matter the contents
-	 * of the win0 framebuffer.  However, blending pre-multiplied color
-	 * with the default opaque black default background color is a no-op,
-	 * so we can just disable blending to get the correct result.
+	 * VOP full need to do rb swap to show rgb888/bgr888 format color correctly
 	 */
-	if (fb->format->has_alpha && win_index > 0) {
+	if ((fb->format->format == DRM_FORMAT_RGB888 || fb->format->format == DRM_FORMAT_BGR888) &&
+	    VOP_MAJOR(vop->version) == 3)
+		rb_swap = !rb_swap;
+	VOP_WIN_SET(vop, win, rb_swap, rb_swap);
+
+	global_alpha_en = (vop_plane_state->global_alpha == 0xff) ? 0 : 1;
+	if ((is_alpha_support(fb->format->format) || global_alpha_en) &&
+	    (s->dsp_layer_sel & 0x3) != win->win_id) {
+		int src_blend_m0;
+
+		if (is_alpha_support(fb->format->format) && global_alpha_en)
+			src_blend_m0 = ALPHA_PER_PIX_GLOBAL;
+		else if (is_alpha_support(fb->format->format))
+			src_blend_m0 = ALPHA_PER_PIX;
+		else
+			src_blend_m0 = ALPHA_GLOBAL;
+
 		VOP_WIN_SET(vop, win, dst_alpha_ctl,
 			    DST_FACTOR_M0(ALPHA_SRC_INVERSE));
 		val = SRC_ALPHA_EN(1) | SRC_COLOR_M0(ALPHA_SRC_PRE_MUL) |
 			SRC_ALPHA_M0(ALPHA_STRAIGHT) |
-			SRC_BLEND_M0(ALPHA_PER_PIX) |
-			SRC_ALPHA_CAL_M0(ALPHA_NO_SATURATION) |
-			SRC_FACTOR_M0(ALPHA_ONE);
+			SRC_BLEND_M0(src_blend_m0) |
+			SRC_ALPHA_CAL_M0(ALPHA_SATURATION) |
+			SRC_FACTOR_M0(global_alpha_en ?
+				      ALPHA_SRC_GLOBAL : ALPHA_ONE);
 		VOP_WIN_SET(vop, win, src_alpha_ctl, val);
-
-		VOP_WIN_SET(vop, win, alpha_pre_mul, ALPHA_SRC_PRE_MUL);
-		VOP_WIN_SET(vop, win, alpha_mode, ALPHA_PER_PIX);
+		VOP_WIN_SET(vop, win, alpha_pre_mul,
+			    vop_plane_state->blend_mode == DRM_MODE_BLEND_PREMULTI ? 1 : 0);
+		VOP_WIN_SET(vop, win, alpha_mode, 1);
 		VOP_WIN_SET(vop, win, alpha_en, 1);
 	} else {
 		VOP_WIN_SET(vop, win, src_alpha_ctl, SRC_ALPHA_EN(0));
 		VOP_WIN_SET(vop, win, alpha_en, 0);
 	}
-
+	VOP_WIN_SET(vop, win, global_alpha_val, vop_plane_state->global_alpha);
+
+	VOP_WIN_SET(vop, win, csc_mode, vop_plane_state->csc_mode);
+	if (win->csc) {
+		vop_load_csc_table(vop, win->csc->y2r_offset, y2r_table);
+		vop_load_csc_table(vop, win->csc->r2r_offset, r2r_table);
+		vop_load_csc_table(vop, win->csc->r2y_offset, r2y_table);
+		VOP_WIN_SET_EXT(vop, win, csc, y2r_en, vop_plane_state->y2r_en);
+		VOP_WIN_SET_EXT(vop, win, csc, r2r_en, vop_plane_state->r2r_en);
+		VOP_WIN_SET_EXT(vop, win, csc, r2y_en, vop_plane_state->r2y_en);
+		VOP_WIN_SET_EXT(vop, win, csc, csc_mode, vop_plane_state->csc_mode);
+	}
 	VOP_WIN_SET(vop, win, enable, 1);
-	vop->win_enabled |= BIT(win_index);
+	VOP_WIN_SET(vop, win, gate, 1);
 	spin_unlock(&vop->reg_lock);
+	/*
+	 * spi interface(vop_plane_state->yrgb_kvaddr, fb->pixel_format,
+	 * actual_w, actual_h)
+	 */
+	vop->is_iommu_needed = true;
+#if defined(CONFIG_ROCKCHIP_DRM_DEBUG)
+	kfree(vop_plane_state->planlist);
+	vop_plane_state->planlist = NULL;
+
+	planlist = kmalloc(sizeof(*planlist), GFP_KERNEL);
+	if (planlist) {
+		planlist->dump_info.AFBC_flag = AFBC_flag;
+		planlist->dump_info.area_id = win->area_id;
+		planlist->dump_info.win_id = win->win_id;
+		planlist->dump_info.yuv_format =
+			is_yuv_support(fb->format->format);
+		planlist->dump_info.num_pages = num_pages;
+		planlist->dump_info.pages = pages;
+		planlist->dump_info.offset = vop_plane_state->offset;
+		planlist->dump_info.pitches = fb->pitches[0];
+		planlist->dump_info.height = actual_h;
+		planlist->dump_info.format = fb->format;
+		list_add_tail(&planlist->entry, &vop->rockchip_crtc.vop_dump_list_head);
+		vop_plane_state->planlist = planlist;
+	} else {
+		DRM_ERROR("can't alloc a node of planlist %p\n", planlist);
+		return;
+	}
+	if (vop->rockchip_crtc.vop_dump_status == DUMP_KEEP ||
+	    vop->rockchip_crtc.vop_dump_times > 0) {
+		rockchip_drm_dump_plane_buffer(&planlist->dump_info, vop->rockchip_crtc.frame_count);
+		vop->rockchip_crtc.vop_dump_times--;
+	}
+#endif
 }
 
-static int vop_plane_atomic_async_check(struct drm_plane *plane,
-					struct drm_plane_state *state)
+static const struct drm_plane_helper_funcs plane_helper_funcs = {
+	.prepare_fb = vop_plane_prepare_fb,
+	.cleanup_fb = vop_plane_cleanup_fb,
+	.atomic_check = vop_plane_atomic_check,
+	.atomic_update = vop_plane_atomic_update,
+	.atomic_disable = vop_plane_atomic_disable,
+};
+
+/**
+ * rockchip_atomic_helper_update_plane copy from drm_atomic_helper_update_plane
+ * be designed to support async commit at ioctl DRM_IOCTL_MODE_SETPLANE.
+ * @plane: plane object to update
+ * @crtc: owning CRTC of owning plane
+ * @fb: framebuffer to flip onto plane
+ * @crtc_x: x offset of primary plane on crtc
+ * @crtc_y: y offset of primary plane on crtc
+ * @crtc_w: width of primary plane rectangle on crtc
+ * @crtc_h: height of primary plane rectangle on crtc
+ * @src_x: x offset of @fb for panning
+ * @src_y: y offset of @fb for panning
+ * @src_w: width of source rectangle in @fb
+ * @src_h: height of source rectangle in @fb
+ * @ctx: lock acquire context
+ *
+ * Provides a default plane update handler using the atomic driver interface.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure
+ */
+static int __maybe_unused
+rockchip_atomic_helper_update_plane(struct drm_plane *plane,
+				    struct drm_crtc *crtc,
+				    struct drm_framebuffer *fb,
+				    int crtc_x, int crtc_y,
+				    unsigned int crtc_w, unsigned int crtc_h,
+				    uint32_t src_x, uint32_t src_y,
+				    uint32_t src_w, uint32_t src_h,
+				    struct drm_modeset_acquire_ctx *ctx)
 {
-	struct vop_win *vop_win = to_vop_win(plane);
-	const struct vop_win_data *win = vop_win->data;
-	int min_scale = win->phy->scl ? FRAC_16_16(1, 8) :
-					DRM_PLANE_HELPER_NO_SCALING;
-	int max_scale = win->phy->scl ? FRAC_16_16(8, 1) :
-					DRM_PLANE_HELPER_NO_SCALING;
-	struct drm_crtc_state *crtc_state;
+	struct drm_atomic_state *state;
+	struct drm_plane_state *plane_state;
+	struct vop_plane_state *vop_plane_state;
+	int ret = 0;
 
-	if (plane != state->crtc->cursor)
-		return -EINVAL;
+	state = drm_atomic_state_alloc(plane->dev);
+	if (!state)
+		return -ENOMEM;
 
-	if (!plane->state)
-		return -EINVAL;
+	state->acquire_ctx = ctx;
+	plane_state = drm_atomic_get_plane_state(state, plane);
+	if (IS_ERR(plane_state)) {
+		ret = PTR_ERR(plane_state);
+		goto fail;
+	}
 
-	if (!plane->state->fb)
-		return -EINVAL;
+	vop_plane_state = to_vop_plane_state(plane_state);
+
+	ret = drm_atomic_set_crtc_for_plane(plane_state, crtc);
+	if (ret != 0)
+		goto fail;
+	drm_atomic_set_fb_for_plane(plane_state, fb);
+	plane_state->crtc_x = crtc_x;
+	plane_state->crtc_y = crtc_y;
+	plane_state->crtc_w = crtc_w;
+	plane_state->crtc_h = crtc_h;
+	plane_state->src_x = src_x;
+	plane_state->src_y = src_y;
+	plane_state->src_w = src_w;
+	plane_state->src_h = src_h;
+
+	if (plane == crtc->cursor || vop_plane_state->async_commit)
+		state->legacy_cursor_update = true;
+
+	ret = drm_atomic_commit(state);
+fail:
+	drm_atomic_state_put(state);
+	return ret;
+}
+
+/**
+ * drm_atomic_helper_disable_plane copy from drm_atomic_helper_disable_plane
+ * be designed to support async commit at ioctl DRM_IOCTL_MODE_SETPLANE.
+ *
+ * @plane: plane to disable
+ * @ctx: lock acquire context
+ *
+ * Provides a default plane disable handler using the atomic driver interface.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure
+ */
+static int __maybe_unused
+rockchip_atomic_helper_disable_plane(struct drm_plane *plane,
+				     struct drm_modeset_acquire_ctx *ctx)
+{
+	struct drm_atomic_state *state;
+	struct drm_plane_state *plane_state;
+	struct vop_plane_state *vop_plane_state;
+	int ret = 0;
+
+	state = drm_atomic_state_alloc(plane->dev);
+	if (!state)
+		return -ENOMEM;
+
+	state->acquire_ctx = ctx;
+	plane_state = drm_atomic_get_plane_state(state, plane);
+	if (IS_ERR(plane_state)) {
+		ret = PTR_ERR(plane_state);
+		goto fail;
+	}
+	vop_plane_state = to_vop_plane_state(plane_state);
+
+	if ((plane_state->crtc && plane_state->crtc->cursor == plane) ||
+	    vop_plane_state->async_commit)
+		plane_state->state->legacy_cursor_update = true;
 
-	if (state->state)
-		crtc_state = drm_atomic_get_existing_crtc_state(state->state,
-								state->crtc);
-	else /* Special case for asynchronous cursor updates. */
-		crtc_state = plane->crtc->state;
+	ret = __drm_atomic_helper_disable_plane(plane, plane_state);
+	if (ret != 0)
+		goto fail;
 
-	return drm_atomic_helper_check_plane_state(plane->state, crtc_state,
-						   min_scale, max_scale,
-						   true, true);
+	ret = drm_atomic_commit(state);
+fail:
+	drm_atomic_state_put(state);
+	return ret;
 }
 
-static void vop_plane_atomic_async_update(struct drm_plane *plane,
-					  struct drm_plane_state *new_state)
+static void vop_plane_destroy(struct drm_plane *plane)
 {
-	struct vop *vop = to_vop(plane->state->crtc);
-	struct drm_framebuffer *old_fb = plane->state->fb;
+	drm_plane_cleanup(plane);
+}
 
-	plane->state->crtc_x = new_state->crtc_x;
-	plane->state->crtc_y = new_state->crtc_y;
-	plane->state->crtc_h = new_state->crtc_h;
-	plane->state->crtc_w = new_state->crtc_w;
-	plane->state->src_x = new_state->src_x;
-	plane->state->src_y = new_state->src_y;
-	plane->state->src_h = new_state->src_h;
-	plane->state->src_w = new_state->src_w;
-	swap(plane->state->fb, new_state->fb);
+static void vop_atomic_plane_reset(struct drm_plane *plane)
+{
+	struct vop_plane_state *vop_plane_state =
+					to_vop_plane_state(plane->state);
+	struct vop_win *win = to_vop_win(plane);
+
+	if (plane->state && plane->state->fb)
+		__drm_atomic_helper_plane_destroy_state(plane->state);
+	kfree(vop_plane_state);
+	vop_plane_state = kzalloc(sizeof(*vop_plane_state), GFP_KERNEL);
+	if (!vop_plane_state)
+		return;
 
-	if (vop->is_enabled) {
-		vop_plane_atomic_update(plane, plane->state);
-		spin_lock(&vop->reg_lock);
-		vop_cfg_done(vop);
-		spin_unlock(&vop->reg_lock);
+	__drm_atomic_helper_plane_reset(plane, &vop_plane_state->base);
+	win->state.zpos = win->zpos;
+	vop_plane_state->global_alpha = 0xff;
+}
 
-		/*
-		 * A scanout can still be occurring, so we can't drop the
-		 * reference to the old framebuffer. To solve this we get a
-		 * reference to old_fb and set a worker to release it later.
-		 * FIXME: if we perform 500 async_update calls before the
-		 * vblank, then we can have 500 different framebuffers waiting
-		 * to be released.
-		 */
-		if (old_fb && plane->state->fb != old_fb) {
-			drm_framebuffer_get(old_fb);
-			WARN_ON(drm_crtc_vblank_get(plane->state->crtc) != 0);
-			drm_flip_work_queue(&vop->fb_unref_work, old_fb);
-			set_bit(VOP_PENDING_FB_UNREF, &vop->pending);
-		}
+static struct drm_plane_state *
+vop_atomic_plane_duplicate_state(struct drm_plane *plane)
+{
+	struct vop_plane_state *old_vop_plane_state;
+	struct vop_plane_state *vop_plane_state;
+
+	if (WARN_ON(!plane->state))
+		return NULL;
+
+	old_vop_plane_state = to_vop_plane_state(plane->state);
+	vop_plane_state = kmemdup(old_vop_plane_state,
+				  sizeof(*vop_plane_state), GFP_KERNEL);
+	if (!vop_plane_state)
+		return NULL;
+
+	__drm_atomic_helper_plane_duplicate_state(plane,
+						  &vop_plane_state->base);
+
+	return &vop_plane_state->base;
+}
+
+static void vop_atomic_plane_destroy_state(struct drm_plane *plane,
+					   struct drm_plane_state *state)
+{
+	struct vop_plane_state *vop_state = to_vop_plane_state(state);
+
+	__drm_atomic_helper_plane_destroy_state(state);
+
+	kfree(vop_state);
+}
+
+static int vop_atomic_plane_set_property(struct drm_plane *plane,
+					 struct drm_plane_state *state,
+					 struct drm_property *property,
+					 uint64_t val)
+{
+	struct rockchip_drm_private *private = plane->dev->dev_private;
+	struct vop_win *win = to_vop_win(plane);
+	struct vop_plane_state *plane_state = to_vop_plane_state(state);
+
+	if (property == private->eotf_prop) {
+		plane_state->eotf = val;
+		return 0;
+	}
+
+	if (property == private->color_space_prop) {
+		plane_state->color_space = val;
+		return 0;
+	}
+
+	if (property == private->async_commit_prop) {
+		plane_state->async_commit = val;
+		return 0;
+	}
+
+	if (property == win->color_key_prop) {
+		plane_state->color_key = val;
+		return 0;
 	}
+
+	DRM_ERROR("failed to set vop plane property id:%d, name:%s\n",
+		   property->base.id, property->name);
+
+	return -EINVAL;
 }
 
-static const struct drm_plane_helper_funcs plane_helper_funcs = {
-	.atomic_check = vop_plane_atomic_check,
-	.atomic_update = vop_plane_atomic_update,
-	.atomic_disable = vop_plane_atomic_disable,
-	.atomic_async_check = vop_plane_atomic_async_check,
-	.atomic_async_update = vop_plane_atomic_async_update,
-	.prepare_fb = drm_gem_fb_prepare_fb,
-};
+static int vop_atomic_plane_get_property(struct drm_plane *plane,
+					 const struct drm_plane_state *state,
+					 struct drm_property *property,
+					 uint64_t *val)
+{
+	struct vop_plane_state *plane_state = to_vop_plane_state(state);
+	struct vop_win *win = to_vop_win(plane);
+	struct rockchip_drm_private *private = plane->dev->dev_private;
+
+	if (property == private->eotf_prop) {
+		*val = plane_state->eotf;
+		return 0;
+	}
+
+	if (property == private->color_space_prop) {
+		*val = plane_state->color_space;
+		return 0;
+	}
+
+	if (property == private->async_commit_prop) {
+		*val = plane_state->async_commit;
+		return 0;
+	}
+
+	if (property == private->share_id_prop) {
+		int i;
+		struct drm_mode_object *obj = &plane->base;
+
+		for (i = 0; i < obj->properties->count; i++) {
+			if (obj->properties->properties[i] == property) {
+				*val = obj->properties->values[i];
+				return 0;
+			}
+		}
+	}
+
+	if (property == win->color_key_prop) {
+		*val = plane_state->color_key;
+		return 0;
+	}
+
+	DRM_ERROR("failed to get vop plane property id:%d, name:%s\n",
+		   property->base.id, property->name);
+
+	return -EINVAL;
+}
 
 static const struct drm_plane_funcs vop_plane_funcs = {
-	.update_plane	= drm_atomic_helper_update_plane,
-	.disable_plane	= drm_atomic_helper_disable_plane,
+	.update_plane	= rockchip_atomic_helper_update_plane,
+	.disable_plane	= rockchip_atomic_helper_disable_plane,
 	.destroy = vop_plane_destroy,
-	.reset = drm_atomic_helper_plane_reset,
-	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
-	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
-	.format_mod_supported = rockchip_mod_supported,
+	.reset = vop_atomic_plane_reset,
+	.atomic_duplicate_state = vop_atomic_plane_duplicate_state,
+	.atomic_destroy_state = vop_atomic_plane_destroy_state,
+	.atomic_set_property = vop_atomic_plane_set_property,
+	.atomic_get_property = vop_atomic_plane_get_property,
 };
 
 static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
@@ -1118,278 +2298,1434 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
 	if (WARN_ON(!vop->is_enabled))
 		return -EPERM;
 
-	spin_lock_irqsave(&vop->irq_lock, flags);
+	spin_lock_irqsave(&vop->irq_lock, flags);
+
+	if (VOP_MAJOR(vop->version) == 3 && VOP_MINOR(vop->version) >= 7) {
+		VOP_INTR_SET_TYPE(vop, clear, FS_FIELD_INTR, 1);
+		VOP_INTR_SET_TYPE(vop, enable, FS_FIELD_INTR, 1);
+	} else {
+		VOP_INTR_SET_TYPE(vop, clear, FS_INTR, 1);
+		VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 1);
+	}
+
+	spin_unlock_irqrestore(&vop->irq_lock, flags);
+
+	return 0;
+}
+
+static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+	struct vop *vop = to_vop(crtc);
+	unsigned long flags;
+
+	if (WARN_ON(!vop->is_enabled))
+		return;
+
+	spin_lock_irqsave(&vop->irq_lock, flags);
+
+	if (VOP_MAJOR(vop->version) == 3 && VOP_MINOR(vop->version) >= 7)
+		VOP_INTR_SET_TYPE(vop, enable, FS_FIELD_INTR, 0);
+	else
+		VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
+
+	spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
+static void vop_crtc_cancel_pending_vblank(struct drm_crtc *crtc,
+					   struct drm_file *file_priv)
+{
+	struct drm_device *drm = crtc->dev;
+	struct vop *vop = to_vop(crtc);
+	struct drm_pending_vblank_event *e;
+	unsigned long flags;
+
+	spin_lock_irqsave(&drm->event_lock, flags);
+	e = vop->event;
+	if (e && e->base.file_priv == file_priv) {
+		vop->event = NULL;
+
+		/* e->base.destroy(&e->base);//todo */
+		file_priv->event_space += sizeof(e->event);
+	}
+	spin_unlock_irqrestore(&drm->event_lock, flags);
+}
+
+static int vop_crtc_loader_protect(struct drm_crtc *crtc, bool on)
+{
+	struct rockchip_drm_private *private = crtc->dev->dev_private;
+	struct vop *vop = to_vop(crtc);
+	int sys_status = drm_crtc_index(crtc) ?
+				SYS_STATUS_LCDC1 : SYS_STATUS_LCDC0;
+
+	if (on == vop->loader_protect)
+		return 0;
+
+	if (on) {
+		if (vop->dclk_source) {
+			struct clk *parent;
+
+			parent = clk_get_parent(vop->dclk_source);
+			if (parent) {
+				if (clk_is_match(private->default_pll.pll, parent))
+					vop->pll = &private->default_pll;
+				else if (clk_is_match(private->hdmi_pll.pll, parent))
+					vop->pll = &private->hdmi_pll;
+				if (vop->pll)
+					vop->pll->use_count++;
+			}
+		}
+
+		rockchip_set_system_status(sys_status);
+		vop_initial(crtc);
+		drm_crtc_vblank_on(crtc);
+		vop->loader_protect = true;
+	} else {
+		vop_crtc_atomic_disable(crtc, NULL);
+
+		if (vop->dclk_source && vop->pll) {
+			vop->pll->use_count--;
+			vop->pll = NULL;
+		}
+		vop->loader_protect = false;
+	}
+
+	return 0;
+}
+
+#define DEBUG_PRINT(args...) \
+		do { \
+			if (s) \
+				seq_printf(s, args); \
+			else \
+				pr_err(args); \
+		} while (0)
+
+static int vop_plane_info_dump(struct seq_file *s, struct drm_plane *plane)
+{
+	struct vop_win *win = to_vop_win(plane);
+	struct drm_plane_state *state = plane->state;
+	struct vop_plane_state *pstate = to_vop_plane_state(state);
+	struct drm_rect *src, *dest;
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_format_name_buf format_name;
+	int i;
+	struct drm_gem_object *obj;
+	struct rockchip_gem_object *rk_obj;
+	dma_addr_t fb_addr;
+	u64 afbdc_format =
+		DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16);
+
+	DEBUG_PRINT("    win%d-%d: %s\n", win->win_id, win->area_id,
+		    state->crtc ? "ACTIVE" : "DISABLED");
+	if (!fb)
+		return 0;
+
+	src = &pstate->src;
+	dest = &pstate->dest;
+
+	drm_get_format_name(fb->format->format, &format_name);
+	DEBUG_PRINT("\tformat: %s%s%s[%d] color_space[%d]\n",
+		    format_name.str,
+		    fb->modifier == afbdc_format ? "[AFBC]" : "",
+		    pstate->eotf ? " HDR" : " SDR", pstate->eotf,
+		    pstate->color_space);
+	DEBUG_PRINT("\tcsc: y2r[%d] r2r[%d] r2y[%d] csc mode[%d]\n",
+		    pstate->y2r_en, pstate->r2r_en, pstate->r2y_en,
+		    pstate->csc_mode);
+	DEBUG_PRINT("\tzpos: %d\n", pstate->zpos);
+	DEBUG_PRINT("\tsrc: pos[%dx%d] rect[%dx%d]\n", src->x1 >> 16,
+		    src->y1 >> 16, drm_rect_width(src) >> 16,
+		    drm_rect_height(src) >> 16);
+	DEBUG_PRINT("\tdst: pos[%dx%d] rect[%dx%d]\n", dest->x1, dest->y1,
+		    drm_rect_width(dest), drm_rect_height(dest));
+
+	for (i = 0; i < fb->format->num_planes; i++) {
+		obj = fb->obj[0];
+		rk_obj = to_rockchip_obj(obj);
+		fb_addr = rk_obj->dma_addr + fb->offsets[0];
+
+		DEBUG_PRINT("\tbuf[%d]: addr: %pad pitch: %d offset: %d\n",
+			    i, &fb_addr, fb->pitches[i], fb->offsets[i]);
+	}
+
+	return 0;
+}
+
+static void vop_dump_connector_on_crtc(struct drm_crtc *crtc, struct seq_file *s)
+{
+	struct drm_connector_list_iter conn_iter;
+	struct drm_connector *connector;
+
+	drm_connector_list_iter_begin(crtc->dev, &conn_iter);
+	drm_for_each_connector_iter(connector, &conn_iter) {
+		if (crtc->state->connector_mask & drm_connector_mask(connector))
+			DEBUG_PRINT("    Connector: %s\n", connector->name);
+
+	}
+	drm_connector_list_iter_end(&conn_iter);
+}
+
+static int vop_crtc_debugfs_dump(struct drm_crtc *crtc, struct seq_file *s)
+{
+	struct vop *vop = to_vop(crtc);
+	struct drm_crtc_state *crtc_state = crtc->state;
+	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+	struct rockchip_crtc_state *state = to_rockchip_crtc_state(crtc->state);
+	bool interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
+	struct drm_plane *plane;
+	int i;
+
+	DEBUG_PRINT("VOP [%s]: %s\n", dev_name(vop->dev),
+		    crtc_state->active ? "ACTIVE" : "DISABLED");
+
+	if (!crtc_state->active)
+		return 0;
+
+	vop_dump_connector_on_crtc(crtc, s);
+	DEBUG_PRINT("\tbus_format[%x]: %s\n", state->bus_format,
+		    drm_get_bus_format_name(state->bus_format));
+	DEBUG_PRINT("\toverlay_mode[%d] output_mode[%x]",
+		    state->yuv_overlay, state->output_mode);
+	DEBUG_PRINT(" color_space[%d]\n",
+		    state->color_space);
+	DEBUG_PRINT("    Display mode: %dx%d%s%d\n",
+		    mode->hdisplay, mode->vdisplay, interlaced ? "i" : "p",
+		    drm_mode_vrefresh(mode));
+	DEBUG_PRINT("\tclk[%d] real_clk[%d] type[%x] flag[%x]\n",
+		    mode->clock, mode->crtc_clock, mode->type, mode->flags);
+	DEBUG_PRINT("\tH: %d %d %d %d\n", mode->hdisplay, mode->hsync_start,
+		    mode->hsync_end, mode->htotal);
+	DEBUG_PRINT("\tV: %d %d %d %d\n", mode->vdisplay, mode->vsync_start,
+		    mode->vsync_end, mode->vtotal);
+
+	for (i = 0; i < vop->num_wins; i++) {
+		plane = &vop->win[i].base;
+		vop_plane_info_dump(s, plane);
+	}
+	DEBUG_PRINT("    post: sdr2hdr[%d] hdr2sdr[%d]\n",
+		    state->hdr.sdr2hdr_state.bt1886eotf_post_conv_en,
+		    state->hdr.hdr2sdr_en);
+	DEBUG_PRINT("    pre : sdr2hdr[%d]\n",
+		    state->hdr.sdr2hdr_state.bt1886eotf_pre_conv_en);
+	DEBUG_PRINT("    post CSC: r2y[%d] y2r[%d] CSC mode[%d]\n",
+		    state->post_r2y_en, state->post_y2r_en,
+		    state->post_csc_mode);
+
+	return 0;
+}
+
+static void vop_crtc_regs_dump(struct drm_crtc *crtc, struct seq_file *s)
+{
+	struct vop *vop = to_vop(crtc);
+	struct drm_crtc_state *crtc_state = crtc->state;
+	int dump_len = vop->len > 0x400 ? 0x400 : vop->len;
+	int i;
+
+	if (!crtc_state->active)
+		return;
+
+	for (i = 0; i < dump_len; i += 16) {
+		DEBUG_PRINT("0x%08x: %08x %08x %08x %08x\n", i,
+			    vop_readl(vop, i), vop_readl(vop, i + 4),
+			    vop_readl(vop, i + 8), vop_readl(vop, i + 12));
+	}
+}
+
+static int vop_gamma_show(struct seq_file *s, void *data)
+{
+	struct drm_info_node *node = s->private;
+	struct vop *vop = node->info_ent->data;
+	int i;
+
+	if (!vop->lut || !vop->lut_active || !vop->lut_regs)
+		return 0;
+
+	for (i = 0; i < vop->lut_len; i++) {
+		if (i % 8 == 0)
+			DEBUG_PRINT("\n");
+		DEBUG_PRINT("0x%08x ", vop->lut[i]);
+	}
+	DEBUG_PRINT("\n");
+
+	return 0;
+}
+
+#undef DEBUG_PRINT
+
+static struct drm_info_list vop_debugfs_files[] = {
+	{ "gamma_lut", vop_gamma_show, 0, NULL },
+};
+
+static int vop_crtc_debugfs_init(struct drm_minor *minor, struct drm_crtc *crtc)
+{
+	struct vop *vop = to_vop(crtc);
+	int ret, i;
+
+	vop->debugfs = debugfs_create_dir(dev_name(vop->dev),
+					  minor->debugfs_root);
+
+	if (!vop->debugfs)
+		return -ENOMEM;
+
+	vop->debugfs_files = kmemdup(vop_debugfs_files,
+				     sizeof(vop_debugfs_files),
+				     GFP_KERNEL);
+	if (!vop->debugfs_files) {
+		ret = -ENOMEM;
+		goto remove;
+	}
+#if defined(CONFIG_ROCKCHIP_DRM_DEBUG)
+	rockchip_drm_add_dump_buffer(crtc, vop->debugfs);
+#endif
+	for (i = 0; i < ARRAY_SIZE(vop_debugfs_files); i++)
+		vop->debugfs_files[i].data = vop;
+
+	drm_debugfs_create_files(vop->debugfs_files, ARRAY_SIZE(vop_debugfs_files),
+				 vop->debugfs, minor);
+
+	return 0;
+remove:
+	debugfs_remove(vop->debugfs);
+	vop->debugfs = NULL;
+	return ret;
+}
+
+static enum drm_mode_status
+vop_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode,
+		    int output_type)
+{
+	struct vop *vop = to_vop(crtc);
+	const struct vop_data *vop_data = vop->data;
+	int request_clock = mode->clock;
+	int clock;
+
+	if (mode->hdisplay > vop_data->max_output.width)
+		return MODE_BAD_HVALUE;
+
+	if ((mode->flags & DRM_MODE_FLAG_INTERLACE) &&
+	    VOP_MAJOR(vop->version) == 3 &&
+	    VOP_MINOR(vop->version) <= 2)
+		return MODE_BAD;
+
+	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+		request_clock *= 2;
+	clock = clk_round_rate(vop->dclk, request_clock * 1000) / 1000;
+
+	/*
+	 * Hdmi or DisplayPort request a Accurate clock.
+	 */
+	if (output_type == DRM_MODE_CONNECTOR_HDMIA ||
+	    output_type == DRM_MODE_CONNECTOR_DisplayPort)
+		if (clock != request_clock)
+			return MODE_CLOCK_RANGE;
+
+	return MODE_OK;
+}
+
+struct vop_bandwidth {
+	size_t bandwidth;
+	int y1;
+	int y2;
+};
+
+static int vop_bandwidth_cmp(const void *a, const void *b)
+{
+	struct vop_bandwidth *pa = (struct vop_bandwidth *)a;
+	struct vop_bandwidth *pb = (struct vop_bandwidth *)b;
+
+	return pa->y1 - pb->y2;
+}
+
+static size_t vop_plane_line_bandwidth(struct drm_plane_state *pstate)
+{
+	struct vop_plane_state *vop_plane_state = to_vop_plane_state(pstate);
+	struct vop_win *win = to_vop_win(pstate->plane);
+	struct drm_crtc *crtc = pstate->crtc;
+	struct vop *vop = to_vop(crtc);
+	struct drm_framebuffer *fb = pstate->fb;
+	struct drm_rect *dest = &vop_plane_state->dest;
+	struct drm_rect *src = &vop_plane_state->src;
+	int bpp = fb->format->cpp[0] << 3;
+	int src_width = drm_rect_width(src) >> 16;
+	int src_height = drm_rect_height(src) >> 16;
+	int dest_width = drm_rect_width(dest);
+	int dest_height = drm_rect_height(dest);
+	int vskiplines = scl_get_vskiplines(src_height, dest_height);
+	size_t bandwidth;
+
+	if (src_width <= 0 || src_height <= 0 || dest_width <= 0 ||
+	    dest_height <= 0)
+		return 0;
+
+	bandwidth = src_width * bpp / 8;
+
+	bandwidth = bandwidth * src_width / dest_width;
+	bandwidth = bandwidth * src_height / dest_height;
+	if (vskiplines == 2 && VOP_WIN_SCL_EXT_SUPPORT(vop, win, vsd_yrgb_gt2))
+		bandwidth /= 2;
+	else if (vskiplines == 4 &&
+		 VOP_WIN_SCL_EXT_SUPPORT(vop, win, vsd_yrgb_gt4))
+		bandwidth /= 4;
+
+	return bandwidth;
+}
+
+static u64 vop_calc_max_bandwidth(struct vop_bandwidth *bw, int start,
+				  int count, int y2)
+{
+	u64 max_bandwidth = 0;
+	int i;
+
+	for (i = start; i < count; i++) {
+		u64 bandwidth = 0;
+
+		if (bw[i].y1 > y2)
+			continue;
+		bandwidth = bw[i].bandwidth;
+		bandwidth += vop_calc_max_bandwidth(bw, i + 1, count,
+						    min(bw[i].y2, y2));
+
+		if (bandwidth > max_bandwidth)
+			max_bandwidth = bandwidth;
+	}
+
+	return max_bandwidth;
+}
+
+static size_t vop_crtc_bandwidth(struct drm_crtc *crtc,
+				 struct drm_crtc_state *crtc_state,
+				 struct dmcfreq_vop_info *vop_bw_info)
+{
+	struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
+	u16 htotal = adjusted_mode->crtc_htotal;
+	u16 vdisplay = adjusted_mode->crtc_vdisplay;
+	int clock = adjusted_mode->crtc_clock;
+	struct vop_plane_state *vop_plane_state;
+	struct drm_plane_state *pstate;
+	struct vop_bandwidth *pbandwidth;
+	struct drm_plane *plane;
+	u64 line_bw_mbyte = 0;
+	int cnt = 0, plane_num = 0;
+	struct drm_atomic_state *state = crtc_state->state;
+#if defined(CONFIG_ROCKCHIP_DRM_DEBUG)
+	struct vop_dump_list *pos, *n;
+	struct vop *vop = to_vop(crtc);
+#endif
+
+	if (!htotal || !vdisplay)
+		return 0;
+
+#if defined(CONFIG_ROCKCHIP_DRM_DEBUG)
+	if (!vop->rockchip_crtc.vop_dump_list_init_flag) {
+		INIT_LIST_HEAD(&vop->rockchip_crtc.vop_dump_list_head);
+		vop->rockchip_crtc.vop_dump_list_init_flag = true;
+	}
+	list_for_each_entry_safe(pos, n, &vop->rockchip_crtc.vop_dump_list_head, entry) {
+		list_del(&pos->entry);
+	}
+	if (vop->rockchip_crtc.vop_dump_status == DUMP_KEEP ||
+	    vop->rockchip_crtc.vop_dump_times > 0) {
+		vop->rockchip_crtc.frame_count++;
+	}
+#endif
+
+	drm_atomic_crtc_state_for_each_plane(plane, crtc_state)
+		plane_num++;
+
+	vop_bw_info->plane_num += plane_num;
+	pbandwidth = kmalloc_array(plane_num, sizeof(*pbandwidth),
+				   GFP_KERNEL);
+	if (!pbandwidth)
+		return -ENOMEM;
+
+	drm_atomic_crtc_state_for_each_plane(plane, crtc_state) {
+		int act_w, act_h, cpp, afbc_fac;
+
+		pstate = drm_atomic_get_existing_plane_state(state, plane);
+		if (pstate->crtc != crtc || !pstate->fb)
+			continue;
+
+		/* This is an empirical value, if it's afbc format, the frame buffer size div 2 */
+		afbc_fac = rockchip_afbc(plane, pstate->fb->modifier) ? 2 : 1;
+
+		vop_plane_state = to_vop_plane_state(pstate);
+		pbandwidth[cnt].y1 = vop_plane_state->dest.y1;
+		pbandwidth[cnt].y2 = vop_plane_state->dest.y2;
+		pbandwidth[cnt++].bandwidth = vop_plane_line_bandwidth(pstate) / afbc_fac;
+
+		act_w = drm_rect_width(&pstate->src) >> 16;
+		act_h = drm_rect_height(&pstate->src) >> 16;
+		cpp = pstate->fb->format->cpp[0];
+
+		vop_bw_info->frame_bw_mbyte += act_w * act_h / 1000 * cpp * drm_mode_vrefresh(adjusted_mode) / 1000;
+
+	}
+
+	sort(pbandwidth, cnt, sizeof(pbandwidth[0]), vop_bandwidth_cmp, NULL);
+
+	vop_bw_info->line_bw_mbyte = vop_calc_max_bandwidth(pbandwidth, 0, cnt, vdisplay);
+	kfree(pbandwidth);
+	/*
+	 * line_bandwidth(MB/s)
+	 *    = line_bandwidth / line_time
+	 *    = line_bandwidth(Byte) * clock(KHZ) / 1000 / htotal
+	 */
+	line_bw_mbyte *= clock;
+	do_div(line_bw_mbyte, htotal * 1000);
+	vop_bw_info->line_bw_mbyte = line_bw_mbyte;
+
+	return vop_bw_info->line_bw_mbyte;
+}
+
+static void vop_crtc_close(struct drm_crtc *crtc)
+{
+	struct vop *vop = NULL;
+
+	if (!crtc)
+		return;
+	vop = to_vop(crtc);
+	mutex_lock(&vop->vop_lock);
+	if (!vop->is_enabled) {
+		mutex_unlock(&vop->vop_lock);
+		return;
+	}
+
+	vop_disable_all_planes(vop);
+	mutex_unlock(&vop->vop_lock);
+}
+
+static u32 vop_mode_done(struct vop *vop)
+{
+	return VOP_CTRL_GET(vop, out_mode);
+}
+
+static void vop_set_out_mode(struct vop *vop, u32 mode)
+{
+	int ret;
+	u32 val;
+
+	VOP_CTRL_SET(vop, out_mode, mode);
+	vop_cfg_done(vop);
+	ret = readx_poll_timeout(vop_mode_done, vop, val, val == mode,
+				 1000, 500 * 1000);
+	if (ret)
+		dev_err(vop->dev, "wait mode 0x%x timeout\n", mode);
+
+}
+
+static void vop_crtc_send_mcu_cmd(struct drm_crtc *crtc,  u32 type, u32 value)
+{
+	struct rockchip_crtc_state *state;
+	struct vop *vop = NULL;
+
+	if (!crtc)
+		return;
+
+	vop = to_vop(crtc);
+	state = to_rockchip_crtc_state(crtc->state);
+
+	/*
+	 * set output mode to P888 when start send cmd.
+	 */
+	if ((type == MCU_SETBYPASS) && value)
+		vop_set_out_mode(vop, ROCKCHIP_OUT_MODE_P888);
+	mutex_lock(&vop->vop_lock);
+	if (vop && vop->is_enabled) {
+		switch (type) {
+		case MCU_WRCMD:
+			VOP_CTRL_SET(vop, mcu_rs, 0);
+			VOP_CTRL_SET(vop, mcu_rw_bypass_port, value);
+			VOP_CTRL_SET(vop, mcu_rs, 1);
+			break;
+		case MCU_WRDATA:
+			VOP_CTRL_SET(vop, mcu_rs, 1);
+			VOP_CTRL_SET(vop, mcu_rw_bypass_port, value);
+			break;
+		case MCU_SETBYPASS:
+			VOP_CTRL_SET(vop, mcu_bypass, value ? 1 : 0);
+			break;
+		default:
+			break;
+		}
+	}
+	mutex_unlock(&vop->vop_lock);
+
+	/*
+	 * restore output mode at the end
+	 */
+	if ((type == MCU_SETBYPASS) && !value)
+		vop_set_out_mode(vop, state->output_mode);
+}
+
+static const struct rockchip_crtc_funcs private_crtc_funcs = {
+	.loader_protect = vop_crtc_loader_protect,
+	.cancel_pending_vblank = vop_crtc_cancel_pending_vblank,
+	.debugfs_init = vop_crtc_debugfs_init,
+	.debugfs_dump = vop_crtc_debugfs_dump,
+	.regs_dump = vop_crtc_regs_dump,
+	.mode_valid = vop_crtc_mode_valid,
+	.bandwidth = vop_crtc_bandwidth,
+	.crtc_close = vop_crtc_close,
+	.crtc_send_mcu_cmd = vop_crtc_send_mcu_cmd,
+};
+
+static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
+				const struct drm_display_mode *mode,
+				struct drm_display_mode *adj_mode)
+{
+	struct vop *vop = to_vop(crtc);
+	const struct vop_data *vop_data = vop->data;
+
+	if (mode->hdisplay > vop_data->max_output.width)
+		return false;
+
+	drm_mode_set_crtcinfo(adj_mode,
+			      CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE);
+
+	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+		adj_mode->crtc_clock *= 2;
+
+	adj_mode->crtc_clock =
+		DIV_ROUND_UP(clk_round_rate(vop->dclk, adj_mode->crtc_clock * 1000),
+			     1000);
+
+	return true;
+}
+
+static void vop_dither_setup(struct drm_crtc *crtc)
+{
+	struct rockchip_crtc_state *s =
+			to_rockchip_crtc_state(crtc->state);
+	struct vop *vop = to_vop(crtc);
+
+	/*
+	 * VOP MCU interface can't work right when dither enabled.
+	 * (1) the MCU CMD will be treated as data then changed by dither algorithm
+	 * (2) the dither algorithm works wrong in mcu mode
+	 */
+	if (vop->mcu_timing.mcu_pix_total)
+		return;
+
+	switch (s->bus_format) {
+	case MEDIA_BUS_FMT_RGB565_1X16:
+		VOP_CTRL_SET(vop, dither_down_en, 1);
+		VOP_CTRL_SET(vop, dither_down_mode, RGB888_TO_RGB565);
+		break;
+	case MEDIA_BUS_FMT_RGB666_1X18:
+	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
+		VOP_CTRL_SET(vop, dither_down_en, 1);
+		VOP_CTRL_SET(vop, dither_down_mode, RGB888_TO_RGB666);
+		break;
+	case MEDIA_BUS_FMT_YUV8_1X24:
+	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+		VOP_CTRL_SET(vop, dither_down_en, 0);
+		VOP_CTRL_SET(vop, pre_dither_down_en, 1);
+		break;
+	case MEDIA_BUS_FMT_YUV10_1X30:
+	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+		VOP_CTRL_SET(vop, dither_down_en, 0);
+		VOP_CTRL_SET(vop, pre_dither_down_en, 0);
+		break;
+	case MEDIA_BUS_FMT_RGB888_3X8:
+	case MEDIA_BUS_FMT_RGB888_DUMMY_4X8:
+	case MEDIA_BUS_FMT_RGB888_1X24:
+	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
+	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
+	default:
+		VOP_CTRL_SET(vop, dither_down_en, 0);
+		VOP_CTRL_SET(vop, pre_dither_down_en, 0);
+		break;
+	}
+
+	VOP_CTRL_SET(vop, pre_dither_down_en,
+		     s->output_mode == ROCKCHIP_OUT_MODE_AAAA ? 0 : 1);
+	VOP_CTRL_SET(vop, dither_down_sel, DITHER_DOWN_ALLEGRO);
+}
+
+static void vop_update_csc(struct drm_crtc *crtc)
+{
+	struct rockchip_crtc_state *s =
+			to_rockchip_crtc_state(crtc->state);
+	struct vop *vop = to_vop(crtc);
+	u32 val;
+
+	if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
+	    !(vop->data->feature & VOP_FEATURE_OUTPUT_10BIT))
+		s->output_mode = ROCKCHIP_OUT_MODE_P888;
+
+	if (is_uv_swap(s->bus_format, s->output_mode))
+		VOP_CTRL_SET(vop, dsp_data_swap, DSP_RB_SWAP);
+	else
+		VOP_CTRL_SET(vop, dsp_data_swap, 0);
+
+	VOP_CTRL_SET(vop, out_mode, s->output_mode);
+
+	vop_dither_setup(crtc);
+	VOP_CTRL_SET(vop, dclk_ddr,
+		     s->output_mode == ROCKCHIP_OUT_MODE_YUV420 ? 1 : 0);
+	VOP_CTRL_SET(vop, hdmi_dclk_out_en,
+		     s->output_mode == ROCKCHIP_OUT_MODE_YUV420 ? 1 : 0);
+
+	VOP_CTRL_SET(vop, overlay_mode, s->yuv_overlay);
+	VOP_CTRL_SET(vop, dsp_out_yuv, is_yuv_output(s->bus_format));
+
+	/*
+	 * Background color is 10bit depth if vop version >= 3.5
+	 */
+	if (!is_yuv_output(s->bus_format))
+		val = 0;
+	else if (VOP_MAJOR(vop->version) == 3 && VOP_MINOR(vop->version) == 8 &&
+		 s->hdr.pre_overlay)
+		val = 0;
+	else if (VOP_MAJOR(vop->version) == 3 && VOP_MINOR(vop->version) >= 5)
+		val = 0x20010200;
+	else
+		val = 0x801080;
+	VOP_CTRL_SET(vop, dsp_background, val);
+}
+
+/*
+ * if adjusted mode update, return true, else return false
+ */
+static bool vop_crtc_mode_update(struct drm_crtc *crtc)
+{
+	struct vop *vop = to_vop(crtc);
+	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
+	u16 hsync_len = adjusted_mode->crtc_hsync_end -
+				adjusted_mode->crtc_hsync_start;
+	u16 hdisplay = adjusted_mode->crtc_hdisplay;
+	u16 htotal = adjusted_mode->crtc_htotal;
+	u16 hact_st = adjusted_mode->crtc_htotal -
+				adjusted_mode->crtc_hsync_start;
+	u16 hact_end = hact_st + hdisplay;
+	u16 vdisplay = adjusted_mode->crtc_vdisplay;
+	u16 vtotal = adjusted_mode->crtc_vtotal;
+	u16 vsync_len = adjusted_mode->crtc_vsync_end -
+				adjusted_mode->crtc_vsync_start;
+	u16 vact_st = adjusted_mode->crtc_vtotal -
+				adjusted_mode->crtc_vsync_start;
+	u16 vact_end = vact_st + vdisplay;
+	u32 htotal_sync = htotal << 16 | hsync_len;
+	u32 hactive_st_end = hact_st << 16 | hact_end;
+	u32 vtotal_sync = vtotal << 16 | vsync_len;
+	u32 vactive_st_end = vact_st << 16 | vact_end;
+	u32 crtc_clock = adjusted_mode->crtc_clock * 100;
+
+	if (htotal_sync != VOP_CTRL_GET(vop, htotal_pw) ||
+	    hactive_st_end != VOP_CTRL_GET(vop, hact_st_end) ||
+	    vtotal_sync != VOP_CTRL_GET(vop, vtotal_pw) ||
+	    vactive_st_end != VOP_CTRL_GET(vop, vact_st_end) ||
+	    crtc_clock != clk_get_rate(vop->dclk))
+		return true;
+
+	return false;
+}
+
+static void vop_mcu_mode(struct drm_crtc *crtc)
+{
+	struct vop *vop = to_vop(crtc);
+
+	VOP_CTRL_SET(vop, mcu_clk_sel, 1);
+	VOP_CTRL_SET(vop, mcu_type, 1);
+
+	VOP_CTRL_SET(vop, mcu_hold_mode, 1);
+	VOP_CTRL_SET(vop, mcu_pix_total, vop->mcu_timing.mcu_pix_total);
+	VOP_CTRL_SET(vop, mcu_cs_pst, vop->mcu_timing.mcu_cs_pst);
+	VOP_CTRL_SET(vop, mcu_cs_pend, vop->mcu_timing.mcu_cs_pend);
+	VOP_CTRL_SET(vop, mcu_rw_pst, vop->mcu_timing.mcu_rw_pst);
+	VOP_CTRL_SET(vop, mcu_rw_pend, vop->mcu_timing.mcu_rw_pend);
+}
+
+static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
+				   struct drm_crtc_state *old_state)
+{
+	struct vop *vop = to_vop(crtc);
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state);
+	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
+	u16 hsync_len = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
+	u16 hdisplay = adjusted_mode->crtc_hdisplay;
+	u16 htotal = adjusted_mode->crtc_htotal;
+	u16 hact_st = adjusted_mode->crtc_htotal - adjusted_mode->crtc_hsync_start;
+	u16 hact_end = hact_st + hdisplay;
+	u16 vdisplay = adjusted_mode->crtc_vdisplay;
+	u16 vtotal = adjusted_mode->crtc_vtotal;
+	u16 vsync_len = adjusted_mode->crtc_vsync_end - adjusted_mode->crtc_vsync_start;
+	u16 vact_st = adjusted_mode->crtc_vtotal - adjusted_mode->crtc_vsync_start;
+	u16 vact_end = vact_st + vdisplay;
+	int sys_status = drm_crtc_index(crtc) ?
+				SYS_STATUS_LCDC1 : SYS_STATUS_LCDC0;
+	uint32_t val;
+	int act_end;
+	bool interlaced = !!(adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE);
+	int for_ddr_freq = 0;
+	bool dclk_inv, yc_swap = false;
+
+	rockchip_set_system_status(sys_status);
+	vop_lock(vop);
+	DRM_DEV_INFO(vop->dev, "Update mode to %dx%d%s%d, type: %d\n",
+		     hdisplay, vdisplay, interlaced ? "i" : "p",
+		     drm_mode_vrefresh(adjusted_mode), s->output_type);
+	vop_initial(crtc);
+	vop_disable_allwin(vop);
+	VOP_CTRL_SET(vop, standby, 0);
+	s->mode_update = vop_crtc_mode_update(crtc);
+	if (s->mode_update)
+		vop_disable_all_planes(vop);
+	/*
+	 * restore the lut table.
+	 */
+	if (vop->lut_active)
+		vop_crtc_load_lut(crtc);
+
+	if (vop->mcu_timing.mcu_pix_total)
+		vop_mcu_mode(crtc);
+
+	dclk_inv = (s->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) ? 1 : 0;
+
+	VOP_CTRL_SET(vop, dclk_pol, dclk_inv);
+	val = (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ?
+		   0 : BIT(HSYNC_POSITIVE);
+	val |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ?
+		   0 : BIT(VSYNC_POSITIVE);
+	VOP_CTRL_SET(vop, pin_pol, val);
+
+	if (vop->dclk_source && vop->pll && vop->pll->pll) {
+		if (clk_set_parent(vop->dclk_source, vop->pll->pll))
+			DRM_DEV_ERROR(vop->dev,
+				      "failed to set dclk's parents\n");
+	}
+
+	switch (s->output_type) {
+	case DRM_MODE_CONNECTOR_DPI:
+	case DRM_MODE_CONNECTOR_LVDS:
+		VOP_CTRL_SET(vop, rgb_en, 1);
+		VOP_CTRL_SET(vop, rgb_pin_pol, val);
+		VOP_CTRL_SET(vop, rgb_dclk_pol, dclk_inv);
+		VOP_CTRL_SET(vop, lvds_en, 1);
+		VOP_CTRL_SET(vop, lvds_pin_pol, val);
+		VOP_CTRL_SET(vop, lvds_dclk_pol, dclk_inv);
+		VOP_GRF_SET(vop, grf_dclk_inv, dclk_inv);
+		if (s->output_if & VOP_OUTPUT_IF_BT1120) {
+			VOP_CTRL_SET(vop, bt1120_en, 1);
+			yc_swap = is_yc_swap(s->bus_format);
+			VOP_CTRL_SET(vop, bt1120_yc_swap, yc_swap);
+			VOP_CTRL_SET(vop, yuv_clip, 1);
+		}
+		break;
+	case DRM_MODE_CONNECTOR_eDP:
+		VOP_CTRL_SET(vop, edp_en, 1);
+		VOP_CTRL_SET(vop, edp_pin_pol, val);
+		VOP_CTRL_SET(vop, edp_dclk_pol, dclk_inv);
+		break;
+	case DRM_MODE_CONNECTOR_HDMIA:
+		VOP_CTRL_SET(vop, hdmi_en, 1);
+		VOP_CTRL_SET(vop, hdmi_pin_pol, val);
+		VOP_CTRL_SET(vop, hdmi_dclk_pol, 1);
+		break;
+	case DRM_MODE_CONNECTOR_DSI:
+		VOP_CTRL_SET(vop, mipi_en, 1);
+		VOP_CTRL_SET(vop, mipi_pin_pol, val);
+		VOP_CTRL_SET(vop, mipi_dclk_pol, dclk_inv);
+		VOP_CTRL_SET(vop, mipi_dual_channel_en,
+			!!(s->output_flags & ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE));
+		VOP_CTRL_SET(vop, data01_swap,
+			!!(s->output_flags & ROCKCHIP_OUTPUT_DATA_SWAP) ||
+			vop->dual_channel_swap);
+		break;
+	case DRM_MODE_CONNECTOR_DisplayPort:
+		VOP_CTRL_SET(vop, dp_dclk_pol, 0);
+		VOP_CTRL_SET(vop, dp_pin_pol, val);
+		VOP_CTRL_SET(vop, dp_en, 1);
+		break;
+	case DRM_MODE_CONNECTOR_TV:
+		if (vdisplay == CVBS_PAL_VDISPLAY)
+			VOP_CTRL_SET(vop, tve_sw_mode, 1);
+		else
+			VOP_CTRL_SET(vop, tve_sw_mode, 0);
+
+		VOP_CTRL_SET(vop, tve_dclk_pol, 1);
+		VOP_CTRL_SET(vop, tve_dclk_en, 1);
+		/* use the same pol reg with hdmi */
+		VOP_CTRL_SET(vop, hdmi_pin_pol, val);
+		VOP_CTRL_SET(vop, sw_genlock, 1);
+		VOP_CTRL_SET(vop, sw_uv_offset_en, 1);
+		VOP_CTRL_SET(vop, dither_up_en, 1);
+		break;
+	default:
+		DRM_ERROR("unsupported connector_type[%d]\n", s->output_type);
+	}
+	vop_update_csc(crtc);
+	VOP_CTRL_SET(vop, htotal_pw, (htotal << 16) | hsync_len);
+	val = hact_st << 16;
+	val |= hact_end;
+	VOP_CTRL_SET(vop, hact_st_end, val);
+	VOP_CTRL_SET(vop, hpost_st_end, val);
+
+	val = vact_st << 16;
+	val |= vact_end;
+	VOP_CTRL_SET(vop, vact_st_end, val);
+	VOP_CTRL_SET(vop, vpost_st_end, val);
+
+	if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) {
+		u16 vact_st_f1 = vtotal + vact_st + 1;
+		u16 vact_end_f1 = vact_st_f1 + vdisplay;
+
+		val = vact_st_f1 << 16 | vact_end_f1;
+		VOP_CTRL_SET(vop, vact_st_end_f1, val);
+		VOP_CTRL_SET(vop, vpost_st_end_f1, val);
+
+		val = vtotal << 16 | (vtotal + vsync_len);
+		VOP_CTRL_SET(vop, vs_st_end_f1, val);
+		VOP_CTRL_SET(vop, dsp_interlace, 1);
+		VOP_CTRL_SET(vop, p2i_en, 1);
+		vtotal += vtotal + 1;
+		act_end = vact_end_f1;
+	} else {
+		VOP_CTRL_SET(vop, dsp_interlace, 0);
+		VOP_CTRL_SET(vop, p2i_en, 0);
+		act_end = vact_end;
+	}
+
+	if (VOP_MAJOR(vop->version) == 3 &&
+	    (VOP_MINOR(vop->version) == 2 || VOP_MINOR(vop->version) == 8))
+		for_ddr_freq = 1000;
+	VOP_INTR_SET(vop, line_flag_num[0], act_end);
+	VOP_INTR_SET(vop, line_flag_num[1],
+		     act_end - us_to_vertical_line(adjusted_mode, for_ddr_freq));
+
+	VOP_CTRL_SET(vop, vtotal_pw, vtotal << 16 | vsync_len);
+
+	VOP_CTRL_SET(vop, core_dclk_div,
+		     !!(adjusted_mode->flags & DRM_MODE_FLAG_DBLCLK));
+
+	VOP_CTRL_SET(vop, win_csc_mode_sel, 1);
+
+	clk_set_rate(vop->dclk, adjusted_mode->crtc_clock * 1000);
+
+
+	vop_cfg_done(vop);
+
+	drm_crtc_vblank_on(crtc);
+	vop_unlock(vop);
+}
+
+static int vop_zpos_cmp(const void *a, const void *b)
+{
+	struct vop_zpos *pa = (struct vop_zpos *)a;
+	struct vop_zpos *pb = (struct vop_zpos *)b;
+
+	return pa->zpos - pb->zpos;
+}
+
+static int vop_afbdc_atomic_check(struct drm_crtc *crtc,
+				  struct drm_crtc_state *crtc_state)
+{
+	struct vop *vop = to_vop(crtc);
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+	struct drm_atomic_state *state = crtc_state->state;
+	struct drm_plane *plane;
+	struct drm_plane_state *pstate;
+	struct vop_plane_state *plane_state;
+	struct drm_framebuffer *fb;
+	struct drm_rect *src;
+	struct vop_win *win;
+	int afbdc_format;
+
+	s->afbdc_en = 0;
+
+	drm_atomic_crtc_state_for_each_plane(plane, crtc_state) {
+		pstate = drm_atomic_get_existing_plane_state(state, plane);
+		/*
+		 * plane might not have changed, in which case take
+		 * current state:
+		 */
+		if (!pstate)
+			pstate = plane->state;
+
+		fb = pstate->fb;
+
+		if (pstate->crtc != crtc || !fb)
+			continue;
+		if (fb->modifier !=
+			DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16))
+			continue;
+
+		if (!VOP_CTRL_SUPPORT(vop, afbdc_en)) {
+			DRM_INFO("not support afbdc\n");
+			return -EINVAL;
+		}
+
+		plane_state = to_vop_plane_state(pstate);
+
+		switch (plane_state->format) {
+		case VOP_FMT_ARGB8888:
+			afbdc_format = AFBDC_FMT_U8U8U8U8;
+			break;
+		case VOP_FMT_RGB888:
+			afbdc_format = AFBDC_FMT_U8U8U8;
+			break;
+		case VOP_FMT_RGB565:
+			afbdc_format = AFBDC_FMT_RGB565;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		if (s->afbdc_en) {
+			DRM_ERROR("vop only support one afbc layer\n");
+			return -EINVAL;
+		}
+
+		win = to_vop_win(plane);
+		src = &plane_state->src;
+		if (!(win->feature & WIN_FEATURE_AFBDC)) {
+			DRM_ERROR("win[%d] feature:0x%llx, not support afbdc\n",
+				  win->win_id, win->feature);
+			return -EINVAL;
+		}
+		if (!IS_ALIGNED(fb->width, 16)) {
+			DRM_ERROR("win[%d] afbdc must 16 align, width: %d\n",
+				  win->win_id, fb->width);
+			return -EINVAL;
+		}
+
+		if (VOP_CTRL_SUPPORT(vop, afbdc_pic_vir_width)) {
+			u32 align_x1, align_x2, align_y1, align_y2, align_val;
+			struct drm_gem_object *obj;
+			struct rockchip_gem_object *rk_obj;
+			dma_addr_t fb_addr;
+
+			obj = fb->obj[0];
+			rk_obj = to_rockchip_obj(obj);
+			fb_addr = rk_obj->dma_addr + fb->offsets[0];
+
+			s->afbdc_win_format = afbdc_format;
+			s->afbdc_win_id = win->win_id;
+			s->afbdc_win_ptr = fb_addr;
+			s->afbdc_win_vir_width = fb->width;
+			s->afbdc_win_xoffset = (src->x1 >> 16);
+			s->afbdc_win_yoffset = (src->y1 >> 16);
+
+			align_x1 = (src->x1 >> 16) - ((src->x1 >> 16) % 16);
+			align_y1 = (src->y1 >> 16) - ((src->y1 >> 16) % 16);
+
+			align_val = (src->x2 >> 16) % 16;
+			if (align_val)
+				align_x2 = (src->x2 >> 16) + (16 - align_val);
+			else
+				align_x2 = src->x2 >> 16;
+
+			align_val = (src->y2 >> 16) % 16;
+			if (align_val)
+				align_y2 = (src->y2 >> 16) + (16 - align_val);
+			else
+				align_y2 = src->y2 >> 16;
+
+			s->afbdc_win_width = align_x2 - align_x1 - 1;
+			s->afbdc_win_height = align_y2 - align_y1 - 1;
+
+			s->afbdc_en = 1;
+
+			break;
+		}
+		if (src->x1 || src->y1 || fb->offsets[0]) {
+			DRM_ERROR("win[%d] afbdc not support offset display\n",
+				  win->win_id);
+			DRM_ERROR("xpos=%d, ypos=%d, offset=%d\n",
+				  src->x1, src->y1, fb->offsets[0]);
+			return -EINVAL;
+		}
+		s->afbdc_win_format = afbdc_format;
+		s->afbdc_win_width = fb->width - 1;
+		s->afbdc_win_height = (drm_rect_height(src) >> 16) - 1;
+		s->afbdc_win_id = win->win_id;
+		s->afbdc_win_ptr = plane_state->yrgb_mst;
+		s->afbdc_en = 1;
+	}
+
+	return 0;
+}
+
+static void vop_dclk_source_generate(struct drm_crtc *crtc,
+				     struct drm_crtc_state *crtc_state)
+{
+	struct rockchip_drm_private *private = crtc->dev->dev_private;
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+	struct rockchip_crtc_state *old_s = to_rockchip_crtc_state(crtc->state);
+	struct vop *vop = to_vop(crtc);
+	struct rockchip_dclk_pll *old_pll = vop->pll;
+
+	if (!vop->dclk_source)
+		return;
+
+	if (crtc_state->active) {
+		WARN_ON(vop->pll && !vop->pll->use_count);
+		if (!vop->pll || vop->pll->use_count > 1 ||
+		    s->output_type != old_s->output_type) {
+			if (vop->pll)
+				vop->pll->use_count--;
+
+			if (s->output_type != DRM_MODE_CONNECTOR_HDMIA &&
+			    !private->default_pll.use_count)
+				vop->pll = &private->default_pll;
+			else
+				vop->pll = &private->hdmi_pll;
+
+			vop->pll->use_count++;
+		}
+	} else if (vop->pll) {
+		vop->pll->use_count--;
+		vop->pll = NULL;
+	}
+	if (vop->pll != old_pll)
+		crtc_state->mode_changed = true;
+}
+
+static int vop_crtc_atomic_check(struct drm_crtc *crtc,
+				 struct drm_crtc_state *crtc_state)
+{
+	struct drm_atomic_state *state = crtc_state->state;
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+	struct vop *vop = to_vop(crtc);
+	const struct vop_data *vop_data = vop->data;
+	struct drm_plane *plane;
+	struct drm_plane_state *pstate;
+	struct vop_plane_state *plane_state;
+	struct vop_zpos *pzpos;
+	int dsp_layer_sel = 0;
+	int i, j, cnt = 0, ret = 0;
+
+	ret = vop_afbdc_atomic_check(crtc, crtc_state);
+	if (ret)
+		return ret;
+
+	s->yuv_overlay = 0;
+	if (VOP_CTRL_SUPPORT(vop, overlay_mode))
+		s->yuv_overlay = is_yuv_output(s->bus_format);
+
+	ret = vop_hdr_atomic_check(crtc, crtc_state);
+	if (ret)
+		return ret;
+	ret = vop_csc_atomic_check(crtc, crtc_state);
+	if (ret)
+		return ret;
+
+	pzpos = kmalloc_array(vop_data->win_size, sizeof(*pzpos), GFP_KERNEL);
+	if (!pzpos)
+		return -ENOMEM;
+
+	for (i = 0; i < vop_data->win_size; i++) {
+		const struct vop_win_data *win_data = &vop_data->win[i];
+		struct vop_win *win;
+
+		if (!win_data->phy)
+			continue;
 
-	VOP_INTR_SET_TYPE(vop, clear, FS_INTR, 1);
-	VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 1);
+		for (j = 0; j < vop->num_wins; j++) {
+			win = &vop->win[j];
 
-	spin_unlock_irqrestore(&vop->irq_lock, flags);
+			if (win->win_id == i && !win->area_id)
+				break;
+		}
+		if (WARN_ON(j >= vop->num_wins)) {
+			ret = -EINVAL;
+			goto err_free_pzpos;
+		}
 
-	return 0;
-}
+		plane = &win->base;
+		pstate = state->planes[drm_plane_index(plane)].state;
+		/*
+		 * plane might not have changed, in which case take
+		 * current state:
+		 */
+		if (!pstate)
+			pstate = plane->state;
+		plane_state = to_vop_plane_state(pstate);
 
-static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
-{
-	struct vop *vop = to_vop(crtc);
-	unsigned long flags;
+		if (!pstate->visible)
+			pzpos[cnt].zpos = INT_MAX;
+		else
+			pzpos[cnt].zpos = plane_state->zpos;
+		pzpos[cnt++].win_id = win->win_id;
+	}
 
-	if (WARN_ON(!vop->is_enabled))
-		return;
+	sort(pzpos, cnt, sizeof(pzpos[0]), vop_zpos_cmp, NULL);
 
-	spin_lock_irqsave(&vop->irq_lock, flags);
+	for (i = 0, cnt = 0; i < vop_data->win_size; i++) {
+		const struct vop_win_data *win_data = &vop_data->win[i];
+		int shift = i * 2;
 
-	VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
+		if (win_data->phy) {
+			struct vop_zpos *zpos = &pzpos[cnt++];
 
-	spin_unlock_irqrestore(&vop->irq_lock, flags);
-}
+			dsp_layer_sel |= zpos->win_id << shift;
+		} else {
+			dsp_layer_sel |= i << shift;
+		}
+	}
 
-static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
-				const struct drm_display_mode *mode,
-				struct drm_display_mode *adjusted_mode)
-{
-	struct vop *vop = to_vop(crtc);
-	unsigned long rate;
+	s->dsp_layer_sel = dsp_layer_sel;
 
-	/*
-	 * Clock craziness.
-	 *
-	 * Key points:
-	 *
-	 * - DRM works in in kHz.
-	 * - Clock framework works in Hz.
-	 * - Rockchip's clock driver picks the clock rate that is the
-	 *   same _OR LOWER_ than the one requested.
-	 *
-	 * Action plan:
-	 *
-	 * 1. When DRM gives us a mode, we should add 999 Hz to it.  That way
-	 *    if the clock we need is 60000001 Hz (~60 MHz) and DRM tells us to
-	 *    make 60000 kHz then the clock framework will actually give us
-	 *    the right clock.
-	 *
-	 *    NOTE: if the PLL (maybe through a divider) could actually make
-	 *    a clock rate 999 Hz higher instead of the one we want then this
-	 *    could be a problem.  Unfortunately there's not much we can do
-	 *    since it's baked into DRM to use kHz.  It shouldn't matter in
-	 *    practice since Rockchip PLLs are controlled by tables and
-	 *    even if there is a divider in the middle I wouldn't expect PLL
-	 *    rates in the table that are just a few kHz different.
-	 *
-	 * 2. Get the clock framework to round the rate for us to tell us
-	 *    what it will actually make.
-	 *
-	 * 3. Store the rounded up rate so that we don't need to worry about
-	 *    this in the actual clk_set_rate().
-	 */
-	rate = clk_round_rate(vop->dclk, adjusted_mode->clock * 1000 + 999);
-	adjusted_mode->clock = DIV_ROUND_UP(rate, 1000);
+	vop_dclk_source_generate(crtc, crtc_state);
 
-	return true;
+err_free_pzpos:
+	kfree(pzpos);
+	return ret;
 }
 
-static bool vop_dsp_lut_is_enabled(struct vop *vop)
+static void vop_post_config(struct drm_crtc *crtc)
 {
-	return vop_read_reg(vop, 0, &vop->data->common->dsp_lut_en);
+	struct vop *vop = to_vop(crtc);
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state);
+	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+	u16 vtotal = mode->crtc_vtotal;
+	u16 hdisplay = mode->crtc_hdisplay;
+	u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start;
+	u16 vdisplay = mode->crtc_vdisplay;
+	u16 vact_st = mode->crtc_vtotal - mode->crtc_vsync_start;
+	u16 hsize = hdisplay * (s->left_margin + s->right_margin) / 200;
+	u16 vsize = vdisplay * (s->top_margin + s->bottom_margin) / 200;
+	u16 hact_end, vact_end;
+	u32 val;
+
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+		vsize = rounddown(vsize, 2);
+
+	hact_st += hdisplay * (100 - s->left_margin) / 200;
+	hact_end = hact_st + hsize;
+	val = hact_st << 16;
+	val |= hact_end;
+	VOP_CTRL_SET(vop, hpost_st_end, val);
+	vact_st += vdisplay * (100 - s->top_margin) / 200;
+	vact_end = vact_st + vsize;
+	val = vact_st << 16;
+	val |= vact_end;
+	VOP_CTRL_SET(vop, vpost_st_end, val);
+	val = scl_cal_scale2(vdisplay, vsize) << 16;
+	val |= scl_cal_scale2(hdisplay, hsize);
+	VOP_CTRL_SET(vop, post_scl_factor, val);
+
+#define POST_HORIZONTAL_SCALEDOWN_EN(x)		((x) << 0)
+#define POST_VERTICAL_SCALEDOWN_EN(x)		((x) << 1)
+	VOP_CTRL_SET(vop, post_scl_ctrl,
+		     POST_HORIZONTAL_SCALEDOWN_EN(hdisplay != hsize) |
+		     POST_VERTICAL_SCALEDOWN_EN(vdisplay != vsize));
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+		u16 vact_st_f1 = vtotal + vact_st + 1;
+		u16 vact_end_f1 = vact_st_f1 + vsize;
+
+		val = vact_st_f1 << 16 | vact_end_f1;
+		VOP_CTRL_SET(vop, vpost_st_end_f1, val);
+	}
 }
 
-static void vop_crtc_write_gamma_lut(struct vop *vop, struct drm_crtc *crtc)
+static void vop_update_hdr(struct drm_crtc *crtc,
+			   struct drm_crtc_state *old_crtc_state)
 {
-	struct drm_color_lut *lut = crtc->state->gamma_lut->data;
-	unsigned int i;
+	struct rockchip_crtc_state *s =
+			to_rockchip_crtc_state(crtc->state);
+	struct vop *vop = to_vop(crtc);
+	struct rockchip_sdr2hdr_state *sdr2hdr_state = &s->hdr.sdr2hdr_state;
 
-	for (i = 0; i < crtc->gamma_size; i++) {
-		u32 word;
+	if (!vop->data->hdr_table)
+		return;
 
-		word = (drm_color_lut_extract(lut[i].red, 10) << 20) |
-		       (drm_color_lut_extract(lut[i].green, 10) << 10) |
-			drm_color_lut_extract(lut[i].blue, 10);
-		writel(word, vop->lut_regs + i * 4);
+	if (s->hdr.hdr2sdr_en) {
+		vop_load_hdr2sdr_table(vop);
+		/* This is ic design bug, when in hdr2sdr mode, the overlay mode
+		 * is rgb domain, so the win0 is do yuv2rgb, but in this case,
+		 * we must close win0 y2r.
+		 */
+		VOP_CTRL_SET(vop, hdr2sdr_en_win0_csc, 0);
 	}
+	VOP_CTRL_SET(vop, hdr2sdr_en, s->hdr.hdr2sdr_en);
+
+	VOP_CTRL_SET(vop, bt1886eotf_pre_conv_en,
+		     sdr2hdr_state->bt1886eotf_pre_conv_en);
+	VOP_CTRL_SET(vop, bt1886eotf_post_conv_en,
+		     sdr2hdr_state->bt1886eotf_post_conv_en);
+
+	VOP_CTRL_SET(vop, rgb2rgb_pre_conv_en,
+		     sdr2hdr_state->rgb2rgb_pre_conv_en);
+	VOP_CTRL_SET(vop, rgb2rgb_pre_conv_mode,
+		     sdr2hdr_state->rgb2rgb_pre_conv_mode);
+	VOP_CTRL_SET(vop, st2084oetf_pre_conv_en,
+		     sdr2hdr_state->st2084oetf_pre_conv_en);
+
+	VOP_CTRL_SET(vop, rgb2rgb_post_conv_en,
+		     sdr2hdr_state->rgb2rgb_post_conv_en);
+	VOP_CTRL_SET(vop, rgb2rgb_post_conv_mode,
+		     sdr2hdr_state->rgb2rgb_post_conv_mode);
+	VOP_CTRL_SET(vop, st2084oetf_post_conv_en,
+		     sdr2hdr_state->st2084oetf_post_conv_en);
+
+	if (sdr2hdr_state->bt1886eotf_pre_conv_en ||
+	    sdr2hdr_state->bt1886eotf_post_conv_en)
+		vop_load_sdr2hdr_table(vop, sdr2hdr_state->sdr2hdr_func);
+	VOP_CTRL_SET(vop, win_csc_mode_sel, 1);
 }
 
-static void vop_crtc_gamma_set(struct vop *vop, struct drm_crtc *crtc,
-			       struct drm_crtc_state *old_state)
+static void vop_tv_config_update(struct drm_crtc *crtc,
+				 struct drm_crtc_state *old_crtc_state)
 {
-	struct drm_crtc_state *state = crtc->state;
-	unsigned int idle;
-	int ret;
+	struct rockchip_crtc_state *s =
+			to_rockchip_crtc_state(crtc->state);
+	struct rockchip_crtc_state *old_s =
+			to_rockchip_crtc_state(old_crtc_state);
+	int brightness, contrast, saturation, hue, sin_hue, cos_hue;
+	struct vop *vop = to_vop(crtc);
+	const struct vop_data *vop_data = vop->data;
 
-	if (!vop->lut_regs)
+	if (!s->tv_state)
 		return;
-	/*
-	 * To disable gamma (gamma_lut is null) or to write
-	 * an update to the LUT, clear dsp_lut_en.
-	 */
-	spin_lock(&vop->reg_lock);
-	VOP_REG_SET(vop, common, dsp_lut_en, 0);
-	vop_cfg_done(vop);
-	spin_unlock(&vop->reg_lock);
 
 	/*
-	 * In order to write the LUT to the internal memory,
-	 * we need to first make sure the dsp_lut_en bit is cleared.
+	 * The BCSH only need to config once except one of the following
+	 * condition changed:
+	 *   1. tv_state: include brightness,contrast,saturation and hue;
+	 *   2. yuv_overlay: it is related to BCSH r2y module;
+	 *   3. mode_update: it is indicate mode change and resume from suspend;
+	 *   4. bcsh_en: control the BCSH module enable or disable state;
+	 *   5. bus_format: it is related to BCSH y2r module;
 	 */
-	ret = readx_poll_timeout(vop_dsp_lut_is_enabled, vop,
-				 idle, !idle, 5, 30 * 1000);
-	if (ret) {
-		DRM_DEV_ERROR(vop->dev, "display LUT RAM enable timeout!\n");
+	if (!memcmp(s->tv_state,
+		    &vop->active_tv_state, sizeof(*s->tv_state)) &&
+	    s->yuv_overlay == old_s->yuv_overlay && s->mode_update &&
+	    s->bcsh_en == old_s->bcsh_en && s->bus_format == old_s->bus_format)
 		return;
+
+	memcpy(&vop->active_tv_state, s->tv_state, sizeof(*s->tv_state));
+	/* post BCSH CSC */
+	s->post_r2y_en = 0;
+	s->post_y2r_en = 0;
+	s->bcsh_en = 0;
+	if (s->tv_state) {
+		if (s->tv_state->brightness != 50 ||
+		    s->tv_state->contrast != 50 ||
+		    s->tv_state->saturation != 50 || s->tv_state->hue != 50)
+			s->bcsh_en = 1;
+	}
+
+	if (s->bcsh_en) {
+		if (!s->yuv_overlay)
+			s->post_r2y_en = 1;
+		if (!is_yuv_output(s->bus_format))
+			s->post_y2r_en = 1;
+	} else {
+		if (!s->yuv_overlay && is_yuv_output(s->bus_format))
+			s->post_r2y_en = 1;
+		if (s->yuv_overlay && !is_yuv_output(s->bus_format))
+			s->post_y2r_en = 1;
 	}
 
-	if (!state->gamma_lut)
+	s->post_csc_mode = to_vop_csc_mode(s->color_space);
+	VOP_CTRL_SET(vop, bcsh_r2y_en, s->post_r2y_en);
+	VOP_CTRL_SET(vop, bcsh_y2r_en, s->post_y2r_en);
+	VOP_CTRL_SET(vop, bcsh_r2y_csc_mode, s->post_csc_mode);
+	VOP_CTRL_SET(vop, bcsh_y2r_csc_mode, s->post_csc_mode);
+	if (!s->bcsh_en) {
+		VOP_CTRL_SET(vop, bcsh_en, s->bcsh_en);
 		return;
+	}
 
-	spin_lock(&vop->reg_lock);
-	vop_crtc_write_gamma_lut(vop, crtc);
-	VOP_REG_SET(vop, common, dsp_lut_en, 1);
-	vop_cfg_done(vop);
-	spin_unlock(&vop->reg_lock);
-}
+	if (vop_data->feature & VOP_FEATURE_OUTPUT_10BIT)
+		brightness = interpolate(0, -128, 100, 127, s->tv_state->brightness);
+	else if (VOP_MAJOR(vop->version) == 2 && VOP_MINOR(vop->version) == 6) /* px30 vopb */
+		brightness = interpolate(0, -64, 100, 63, s->tv_state->brightness);
+	else
+		brightness = interpolate(0, -32, 100, 31, s->tv_state->brightness);
 
-static void vop_crtc_atomic_begin(struct drm_crtc *crtc,
-				  struct drm_crtc_state *old_crtc_state)
-{
-	struct vop *vop = to_vop(crtc);
+	if ((VOP_MAJOR(vop->version) == 3) ||
+	    (VOP_MAJOR(vop->version) == 2 && VOP_MINOR(vop->version) == 6)) { /* px30 vopb */
+		contrast = interpolate(0, 0, 100, 511, s->tv_state->contrast);
+		saturation = interpolate(0, 0, 100, 511, s->tv_state->saturation);
+		/*
+		 *  a:[-30~0]:
+		 *    sin_hue = 0x100 - sin(a)*256;
+		 *    cos_hue = cos(a)*256;
+		 *  a:[0~30]
+		 *    sin_hue = sin(a)*256;
+		 *    cos_hue = cos(a)*256;
+		 */
+		hue = interpolate(0, -30, 100, 30, s->tv_state->hue);
+		sin_hue = fixp_sin32(hue) >> 23;
+		cos_hue = fixp_cos32(hue) >> 23;
+		VOP_CTRL_SET(vop, bcsh_sat_con, saturation * contrast / 0x100);
 
-	/*
-	 * Only update GAMMA if the 'active' flag is not changed,
-	 * otherwise it's updated by .atomic_enable.
-	 */
-	if (crtc->state->color_mgmt_changed &&
-	    !crtc->state->active_changed)
-		vop_crtc_gamma_set(vop, crtc, old_crtc_state);
+	} else {
+		contrast = interpolate(0, 0, 100, 255, s->tv_state->contrast);
+		saturation = interpolate(0, 0, 100, 255, s->tv_state->saturation);
+		/*
+		 *  a:[-30~0]:
+		 *    sin_hue = 0x100 - sin(a)*128;
+		 *    cos_hue = cos(a)*128;
+		 *  a:[0~30]
+		 *    sin_hue = sin(a)*128;
+		 *    cos_hue = cos(a)*128;
+		 */
+		hue = interpolate(0, -30, 100, 30, s->tv_state->hue);
+		sin_hue = fixp_sin32(hue) >> 24;
+		cos_hue = fixp_cos32(hue) >> 24;
+		VOP_CTRL_SET(vop, bcsh_sat_con, saturation * contrast / 0x80);
+	}
+
+	VOP_CTRL_SET(vop, bcsh_brightness, brightness);
+	VOP_CTRL_SET(vop, bcsh_contrast, contrast);
+	VOP_CTRL_SET(vop, bcsh_sin_hue, sin_hue);
+	VOP_CTRL_SET(vop, bcsh_cos_hue, cos_hue);
+	VOP_CTRL_SET(vop, bcsh_out_mode, BCSH_OUT_MODE_NORMAL_VIDEO);
+	if (VOP_MAJOR(vop->version) == 3 && VOP_MINOR(vop->version) == 0)
+		VOP_CTRL_SET(vop, auto_gate_en, 0);
+	VOP_CTRL_SET(vop, bcsh_en, s->bcsh_en);
 }
 
-static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
-				   struct drm_crtc_state *old_state)
+static void vop_cfg_update(struct drm_crtc *crtc,
+			   struct drm_crtc_state *old_crtc_state)
 {
+	struct rockchip_crtc_state *s =
+			to_rockchip_crtc_state(crtc->state);
 	struct vop *vop = to_vop(crtc);
 	const struct vop_data *vop_data = vop->data;
-	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state);
-	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
-	u16 hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
-	u16 hdisplay = adjusted_mode->hdisplay;
-	u16 htotal = adjusted_mode->htotal;
-	u16 hact_st = adjusted_mode->htotal - adjusted_mode->hsync_start;
-	u16 hact_end = hact_st + hdisplay;
-	u16 vdisplay = adjusted_mode->vdisplay;
-	u16 vtotal = adjusted_mode->vtotal;
-	u16 vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
-	u16 vact_st = adjusted_mode->vtotal - adjusted_mode->vsync_start;
-	u16 vact_end = vact_st + vdisplay;
-	uint32_t pin_pol, val;
-	int dither_bpc = s->output_bpc ? s->output_bpc : 10;
-	int ret;
-
-	if (old_state && old_state->self_refresh_active) {
-		drm_crtc_vblank_on(crtc);
-		rockchip_drm_set_win_enabled(crtc, true);
-		return;
-	}
-
-	/*
-	 * If we have a GAMMA LUT in the state, then let's make sure
-	 * it's updated. We might be coming out of suspend,
-	 * which means the LUT internal memory needs to be re-written.
-	 */
-	if (crtc->state->gamma_lut)
-		vop_crtc_gamma_set(vop, crtc, old_state);
-
-	mutex_lock(&vop->vop_lock);
 
-	WARN_ON(vop->event);
+	spin_lock(&vop->reg_lock);
 
-	ret = vop_enable(crtc, old_state);
-	if (ret) {
-		mutex_unlock(&vop->vop_lock);
-		DRM_DEV_ERROR(vop->dev, "Failed to enable vop (%d)\n", ret);
-		return;
-	}
-	pin_pol = (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) ?
-		   BIT(HSYNC_POSITIVE) : 0;
-	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) ?
-		   BIT(VSYNC_POSITIVE) : 0;
-	VOP_REG_SET(vop, output, pin_pol, pin_pol);
-	VOP_REG_SET(vop, output, mipi_dual_channel_en, 0);
+	vop_update_csc(crtc);
 
-	switch (s->output_type) {
-	case DRM_MODE_CONNECTOR_LVDS:
-		VOP_REG_SET(vop, output, rgb_dclk_pol, 1);
-		VOP_REG_SET(vop, output, rgb_pin_pol, pin_pol);
-		VOP_REG_SET(vop, output, rgb_en, 1);
-		break;
-	case DRM_MODE_CONNECTOR_eDP:
-		VOP_REG_SET(vop, output, edp_dclk_pol, 1);
-		VOP_REG_SET(vop, output, edp_pin_pol, pin_pol);
-		VOP_REG_SET(vop, output, edp_en, 1);
-		break;
-	case DRM_MODE_CONNECTOR_HDMIA:
-		VOP_REG_SET(vop, output, hdmi_dclk_pol, 1);
-		VOP_REG_SET(vop, output, hdmi_pin_pol, pin_pol);
-		VOP_REG_SET(vop, output, hdmi_en, 1);
-		break;
-	case DRM_MODE_CONNECTOR_DSI:
-		VOP_REG_SET(vop, output, mipi_dclk_pol, 1);
-		VOP_REG_SET(vop, output, mipi_pin_pol, pin_pol);
-		VOP_REG_SET(vop, output, mipi_en, 1);
-		VOP_REG_SET(vop, output, mipi_dual_channel_en,
-			    !!(s->output_flags & ROCKCHIP_OUTPUT_DSI_DUAL));
-		break;
-	case DRM_MODE_CONNECTOR_DisplayPort:
-		VOP_REG_SET(vop, output, dp_dclk_pol, 0);
-		VOP_REG_SET(vop, output, dp_pin_pol, pin_pol);
-		VOP_REG_SET(vop, output, dp_en, 1);
-		break;
-	default:
-		DRM_DEV_ERROR(vop->dev, "unsupported connector_type [%d]\n",
-			      s->output_type);
-	}
+	vop_tv_config_update(crtc, old_crtc_state);
 
-	/*
-	 * if vop is not support RGB10 output, need force RGB10 to RGB888.
-	 */
-	if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA &&
-	    !(vop_data->feature & VOP_FEATURE_OUTPUT_RGB10))
-		s->output_mode = ROCKCHIP_OUT_MODE_P888;
+	if (s->afbdc_en) {
+		u32 pic_size, pic_offset;
 
-	if (s->output_mode == ROCKCHIP_OUT_MODE_AAAA && dither_bpc <= 8)
-		VOP_REG_SET(vop, common, pre_dither_down, 1);
-	else
-		VOP_REG_SET(vop, common, pre_dither_down, 0);
+		VOP_CTRL_SET(vop, afbdc_format, s->afbdc_win_format | 1 << 4);
+		VOP_CTRL_SET(vop, afbdc_hreg_block_split, 0);
+		VOP_CTRL_SET(vop, afbdc_sel, s->afbdc_win_id);
+		VOP_CTRL_SET(vop, afbdc_hdr_ptr, s->afbdc_win_ptr);
+		pic_size = (s->afbdc_win_width & 0xffff);
+		pic_size |= s->afbdc_win_height << 16;
+		VOP_CTRL_SET(vop, afbdc_pic_size, pic_size);
 
-	if (dither_bpc == 6) {
-		VOP_REG_SET(vop, common, dither_down_sel, DITHER_DOWN_ALLEGRO);
-		VOP_REG_SET(vop, common, dither_down_mode, RGB888_TO_RGB666);
-		VOP_REG_SET(vop, common, dither_down_en, 1);
-	} else {
-		VOP_REG_SET(vop, common, dither_down_en, 0);
+		VOP_CTRL_SET(vop, afbdc_pic_vir_width, s->afbdc_win_vir_width);
+		pic_offset = (s->afbdc_win_xoffset & 0xffff);
+		pic_offset |= s->afbdc_win_yoffset << 16;
+		VOP_CTRL_SET(vop, afbdc_pic_offset, pic_offset);
 	}
 
-	VOP_REG_SET(vop, common, out_mode, s->output_mode);
-
-	VOP_REG_SET(vop, modeset, htotal_pw, (htotal << 16) | hsync_len);
-	val = hact_st << 16;
-	val |= hact_end;
-	VOP_REG_SET(vop, modeset, hact_st_end, val);
-	VOP_REG_SET(vop, modeset, hpost_st_end, val);
-
-	VOP_REG_SET(vop, modeset, vtotal_pw, (vtotal << 16) | vsync_len);
-	val = vact_st << 16;
-	val |= vact_end;
-	VOP_REG_SET(vop, modeset, vact_st_end, val);
-	VOP_REG_SET(vop, modeset, vpost_st_end, val);
-
-	VOP_REG_SET(vop, intr, line_flag_num[0], vact_end);
+	VOP_CTRL_SET(vop, afbdc_en, s->afbdc_en);
 
-	clk_set_rate(vop->dclk, adjusted_mode->clock * 1000);
+	VOP_CTRL_SET(vop, dsp_layer_sel, s->dsp_layer_sel);
+	if (vop_data->feature & VOP_FEATURE_OVERSCAN)
+		vop_post_config(crtc);
 
-	VOP_REG_SET(vop, common, standby, 0);
-	mutex_unlock(&vop->vop_lock);
+	spin_unlock(&vop->reg_lock);
 }
 
 static bool vop_fs_irq_is_pending(struct vop *vop)
 {
-	return VOP_INTR_GET_TYPE(vop, status, FS_INTR);
+	if (VOP_MAJOR(vop->version) == 3 && VOP_MINOR(vop->version) >= 7)
+		return VOP_INTR_GET_TYPE(vop, status, FS_FIELD_INTR);
+	else
+		return VOP_INTR_GET_TYPE(vop, status, FS_INTR);
 }
 
 static void vop_wait_for_irq_handler(struct vop *vop)
@@ -1413,72 +3749,66 @@ static void vop_wait_for_irq_handler(struct vop *vop)
 	synchronize_irq(vop->irq);
 }
 
-static int vop_crtc_atomic_check(struct drm_crtc *crtc,
-				 struct drm_crtc_state *crtc_state)
+static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
+				  struct drm_crtc_state *old_crtc_state)
 {
+	struct drm_atomic_state *old_state = old_crtc_state->state;
+	struct drm_plane_state *old_plane_state;
 	struct vop *vop = to_vop(crtc);
 	struct drm_plane *plane;
-	struct drm_plane_state *plane_state;
-	struct rockchip_crtc_state *s;
-	int afbc_planes = 0;
+	int i;
+	unsigned long flags;
+	struct rockchip_crtc_state *s =
+		to_rockchip_crtc_state(crtc->state);
 
-	if (vop->lut_regs && crtc_state->color_mgmt_changed &&
-	    crtc_state->gamma_lut) {
-		unsigned int len;
+	vop_cfg_update(crtc, old_crtc_state);
 
-		len = drm_color_lut_size(crtc_state->gamma_lut);
-		if (len != crtc->gamma_size) {
-			DRM_DEBUG_KMS("Invalid LUT size; got %d, expected %d\n",
-				      len, crtc->gamma_size);
-			return -EINVAL;
-		}
-	}
+	if (!vop->is_iommu_enabled && vop->is_iommu_needed) {
+		int ret;
 
-	drm_atomic_crtc_state_for_each_plane(plane, crtc_state) {
-		plane_state =
-			drm_atomic_get_plane_state(crtc_state->state, plane);
-		if (IS_ERR(plane_state)) {
-			DRM_DEBUG_KMS("Cannot get plane state for plane %s\n",
-				      plane->name);
-			return PTR_ERR(plane_state);
-		}
+		if (s->mode_update)
+			VOP_CTRL_SET(vop, dma_stop, 1);
 
-		if (drm_is_afbc(plane_state->fb->modifier))
-			++afbc_planes;
+		ret = rockchip_drm_dma_attach_device(vop->drm_dev, vop->dev);
+		if (ret) {
+			vop->is_iommu_enabled = false;
+			vop_disable_all_planes(vop);
+			dev_err(vop->dev, "failed to attach dma mapping, %d\n",
+				ret);
+		} else {
+			vop->is_iommu_enabled = true;
+			VOP_CTRL_SET(vop, dma_stop, 0);
+		}
 	}
 
-	if (afbc_planes > 1) {
-		DRM_DEBUG_KMS("Invalid number of AFBC planes; got %d, expected at most 1\n", afbc_planes);
-		return -EINVAL;
+	vop_update_hdr(crtc, old_crtc_state);
+	if (old_crtc_state->color_mgmt_changed || old_crtc_state->active_changed) {
+		if (crtc->state->gamma_lut || vop->gamma_lut) {
+			if (old_crtc_state->gamma_lut)
+				vop->gamma_lut = old_crtc_state->gamma_lut->data;
+			vop_crtc_atomic_gamma_set(crtc, old_crtc_state);
+		}
 	}
 
-	s = to_rockchip_crtc_state(crtc_state);
-	s->enable_afbc = afbc_planes > 0;
-
-	return 0;
-}
-
-static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
-				  struct drm_crtc_state *old_crtc_state)
-{
-	struct drm_atomic_state *old_state = old_crtc_state->state;
-	struct drm_plane_state *old_plane_state, *new_plane_state;
-	struct vop *vop = to_vop(crtc);
-	struct drm_plane *plane;
-	struct rockchip_crtc_state *s;
-	int i;
-
-	if (WARN_ON(!vop->is_enabled))
-		return;
-
-	spin_lock(&vop->reg_lock);
-
-	/* Enable AFBC if there is some AFBC window, disable otherwise. */
-	s = to_rockchip_crtc_state(crtc->state);
-	VOP_AFBC_SET(vop, enable, s->enable_afbc);
+	spin_lock_irqsave(&vop->irq_lock, flags);
+	vop->pre_overlay = s->hdr.pre_overlay;
 	vop_cfg_done(vop);
+	/*
+	 * rk322x and rk332x odd-even field will mistake when in interlace mode.
+	 * we must switch to frame effect before switch screen and switch to
+	 * field effect after switch screen complete.
+	 */
+	if (VOP_MAJOR(vop->version) == 3 &&
+	    (VOP_MINOR(vop->version) == 7 || VOP_MINOR(vop->version) == 8)) {
+		if (!s->mode_update && VOP_CTRL_GET(vop, reg_done_frm))
+			VOP_CTRL_SET(vop, reg_done_frm, 0);
+	} else {
+		VOP_CTRL_SET(vop, reg_done_frm, 0);
+	}
+	if (vop->mcu_timing.mcu_pix_total)
+		VOP_CTRL_SET(vop, mcu_hold_mode, 0);
 
-	spin_unlock(&vop->reg_lock);
+	spin_unlock_irqrestore(&vop->irq_lock, flags);
 
 	/*
 	 * There is a (rather unlikely) possiblity that a vblank interrupt
@@ -1496,13 +3826,11 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
 		crtc->state->event = NULL;
 	}
 	spin_unlock_irq(&crtc->dev->event_lock);
-
-	for_each_oldnew_plane_in_state(old_state, plane, old_plane_state,
-				       new_plane_state, i) {
+	for_each_old_plane_in_state(old_state, plane, old_plane_state, i) {
 		if (!old_plane_state->fb)
 			continue;
 
-		if (old_plane_state->fb == new_plane_state->fb)
+		if (old_plane_state->fb == plane->state->fb)
 			continue;
 
 		drm_framebuffer_get(old_plane_state->fb);
@@ -1515,7 +3843,6 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
 static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = {
 	.mode_fixup = vop_crtc_mode_fixup,
 	.atomic_check = vop_crtc_atomic_check,
-	.atomic_begin = vop_crtc_atomic_begin,
 	.atomic_flush = vop_crtc_atomic_flush,
 	.atomic_enable = vop_crtc_atomic_enable,
 	.atomic_disable = vop_crtc_atomic_disable,
@@ -1526,14 +3853,33 @@ static void vop_crtc_destroy(struct drm_crtc *crtc)
 	drm_crtc_cleanup(crtc);
 }
 
-static struct drm_crtc_state *vop_crtc_duplicate_state(struct drm_crtc *crtc)
+static void vop_crtc_reset(struct drm_crtc *crtc)
 {
-	struct rockchip_crtc_state *rockchip_state;
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state);
 
-	if (WARN_ON(!crtc->state))
-		return NULL;
+	if (crtc->state) {
+		__drm_atomic_helper_crtc_destroy_state(crtc->state);
+		kfree(s);
+	}
+
+	s = kzalloc(sizeof(*s), GFP_KERNEL);
+	if (!s)
+		return;
+	crtc->state = &s->base;
+	crtc->state->crtc = crtc;
+
+	s->left_margin = 100;
+	s->right_margin = 100;
+	s->top_margin = 100;
+	s->bottom_margin = 100;
+}
+
+static struct drm_crtc_state *vop_crtc_duplicate_state(struct drm_crtc *crtc)
+{
+	struct rockchip_crtc_state *rockchip_state, *old_state;
 
-	rockchip_state = kzalloc(sizeof(*rockchip_state), GFP_KERNEL);
+	old_state = to_rockchip_crtc_state(crtc->state);
+	rockchip_state = kmemdup(old_state, sizeof(*old_state), GFP_KERNEL);
 	if (!rockchip_state)
 		return NULL;
 
@@ -1550,17 +3896,6 @@ static void vop_crtc_destroy_state(struct drm_crtc *crtc,
 	kfree(s);
 }
 
-static void vop_crtc_reset(struct drm_crtc *crtc)
-{
-	struct rockchip_crtc_state *crtc_state =
-		kzalloc(sizeof(*crtc_state), GFP_KERNEL);
-
-	if (crtc->state)
-		vop_crtc_destroy_state(crtc, crtc->state);
-
-	__drm_atomic_helper_crtc_reset(crtc, &crtc_state->base);
-}
-
 #ifdef CONFIG_DRM_ANALOGIX_DP
 static struct drm_connector *vop_get_edp_connector(struct vop *vop)
 {
@@ -1611,33 +3946,131 @@ vop_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name,
 	return 0;
 }
 
-#else
-static int vop_crtc_set_crc_source(struct drm_crtc *crtc,
-				   const char *source_name)
-{
-	return -ENODEV;
-}
+#else
+static int vop_crtc_set_crc_source(struct drm_crtc *crtc,
+				   const char *source_name)
+{
+	return -ENODEV;
+}
+
+static int
+vop_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name,
+			   size_t *values_cnt)
+{
+	return -ENODEV;
+}
+#endif
+
+static int vop_crtc_atomic_get_property(struct drm_crtc *crtc,
+					const struct drm_crtc_state *state,
+					struct drm_property *property,
+					uint64_t *val)
+{
+	struct drm_device *drm_dev = crtc->dev;
+	struct rockchip_drm_private *private = drm_dev->dev_private;
+	struct drm_mode_config *mode_config = &drm_dev->mode_config;
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(state);
+	struct vop *vop = to_vop(crtc);
+
+	if (property == mode_config->tv_left_margin_property) {
+		*val = s->left_margin;
+		return 0;
+	}
+
+	if (property == mode_config->tv_right_margin_property) {
+		*val = s->right_margin;
+		return 0;
+	}
+
+	if (property == mode_config->tv_top_margin_property) {
+		*val = s->top_margin;
+		return 0;
+	}
+
+	if (property == mode_config->tv_bottom_margin_property) {
+		*val = s->bottom_margin;
+		return 0;
+	}
+
+	if (property == private->aclk_prop) {
+		/* KHZ, keep align with mode->clock */
+		*val = clk_get_rate(vop->aclk) / 1000;
+		return 0;
+	}
+
+	if (property == private->bg_prop) {
+		*val = vop->background;
+		return 0;
+	}
+
+	if (property == private->line_flag_prop) {
+		*val = vop->line_flag;
+		return 0;
+	}
+
+	DRM_ERROR("failed to get vop crtc property\n");
+	return -EINVAL;
+}
+
+static int vop_crtc_atomic_set_property(struct drm_crtc *crtc,
+					struct drm_crtc_state *state,
+					struct drm_property *property,
+					uint64_t val)
+{
+	struct drm_device *drm_dev = crtc->dev;
+	struct rockchip_drm_private *private = drm_dev->dev_private;
+	struct drm_mode_config *mode_config = &drm_dev->mode_config;
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(state);
+	struct vop *vop = to_vop(crtc);
+
+	if (property == mode_config->tv_left_margin_property) {
+		s->left_margin = val;
+		return 0;
+	}
+
+	if (property == mode_config->tv_right_margin_property) {
+		s->right_margin = val;
+		return 0;
+	}
+
+	if (property == mode_config->tv_top_margin_property) {
+		s->top_margin = val;
+		return 0;
+	}
+
+	if (property == mode_config->tv_bottom_margin_property) {
+		s->bottom_margin = val;
+		return 0;
+	}
+
+	if (property == private->bg_prop) {
+		vop->background = val;
+		return 0;
+	}
+
+	if (property == private->line_flag_prop) {
+		vop->line_flag = val;
+		return 0;
+	}
 
-static int
-vop_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name,
-			   size_t *values_cnt)
-{
-	return -ENODEV;
+	DRM_ERROR("failed to set vop crtc property\n");
+	return -EINVAL;
 }
-#endif
 
 static const struct drm_crtc_funcs vop_crtc_funcs = {
+	.gamma_set = vop_crtc_legacy_gamma_set,
 	.set_config = drm_atomic_helper_set_config,
 	.page_flip = drm_atomic_helper_page_flip,
 	.destroy = vop_crtc_destroy,
 	.reset = vop_crtc_reset,
+	.atomic_get_property = vop_crtc_atomic_get_property,
+	.atomic_set_property = vop_crtc_atomic_set_property,
 	.atomic_duplicate_state = vop_crtc_duplicate_state,
 	.atomic_destroy_state = vop_crtc_destroy_state,
 	.enable_vblank = vop_crtc_enable_vblank,
 	.disable_vblank = vop_crtc_disable_vblank,
 	.set_crc_source = vop_crtc_set_crc_source,
 	.verify_crc_source = vop_crtc_verify_crc_source,
-	.gamma_set = drm_atomic_helper_legacy_gamma_set,
 };
 
 static void vop_fb_unref_worker(struct drm_flip_work *work, void *val)
@@ -1645,22 +4078,23 @@ static void vop_fb_unref_worker(struct drm_flip_work *work, void *val)
 	struct vop *vop = container_of(work, struct vop, fb_unref_work);
 	struct drm_framebuffer *fb = val;
 
-	drm_crtc_vblank_put(&vop->crtc);
+	drm_crtc_vblank_put(&vop->rockchip_crtc.crtc);
 	drm_framebuffer_put(fb);
 }
 
 static void vop_handle_vblank(struct vop *vop)
 {
 	struct drm_device *drm = vop->drm_dev;
-	struct drm_crtc *crtc = &vop->crtc;
+	struct drm_crtc *crtc = &vop->rockchip_crtc.crtc;
+	unsigned long flags;
 
-	spin_lock(&drm->event_lock);
+	spin_lock_irqsave(&drm->event_lock, flags);
 	if (vop->event) {
 		drm_crtc_send_vblank_event(crtc, vop->event);
 		drm_crtc_vblank_put(crtc);
 		vop->event = NULL;
 	}
-	spin_unlock(&drm->event_lock);
+	spin_unlock_irqrestore(&drm->event_lock, flags);
 
 	if (test_and_clear_bit(VOP_PENDING_FB_UNREF, &vop->pending))
 		drm_flip_work_commit(&vop->fb_unref_work, system_unbound_wq);
@@ -1669,8 +4103,9 @@ static void vop_handle_vblank(struct vop *vop)
 static irqreturn_t vop_isr(int irq, void *data)
 {
 	struct vop *vop = data;
-	struct drm_crtc *crtc = &vop->crtc;
+	struct drm_crtc *crtc = &vop->rockchip_crtc.crtc;
 	uint32_t active_irqs;
+	unsigned long flags;
 	int ret = IRQ_NONE;
 
 	/*
@@ -1689,14 +4124,14 @@ static irqreturn_t vop_isr(int irq, void *data)
 	 * interrupt register has interrupt status, enable and clear bits, we
 	 * must hold irq_lock to avoid a race with enable/disable_vblank().
 	*/
-	spin_lock(&vop->irq_lock);
+	spin_lock_irqsave(&vop->irq_lock, flags);
 
 	active_irqs = VOP_INTR_GET_TYPE(vop, status, INTR_MASK);
 	/* Clear all active interrupt sources */
 	if (active_irqs)
 		VOP_INTR_SET_TYPE(vop, clear, active_irqs, 1);
 
-	spin_unlock(&vop->irq_lock);
+	spin_unlock_irqrestore(&vop->irq_lock, flags);
 
 	/* This is expected for vop iommu irqs, since the irq is shared */
 	if (!active_irqs)
@@ -1714,17 +4149,41 @@ static irqreturn_t vop_isr(int irq, void *data)
 		ret = IRQ_HANDLED;
 	}
 
-	if (active_irqs & FS_INTR) {
+	if ((active_irqs & FS_INTR) || (active_irqs & FS_FIELD_INTR)) {
+		/* This is IC design not reasonable, this two register bit need
+		 * frame effective, but actually it's effective immediately, so
+		 * we config this register at frame start.
+		 */
+		spin_lock_irqsave(&vop->irq_lock, flags);
+		VOP_CTRL_SET(vop, level2_overlay_en, vop->pre_overlay);
+		VOP_CTRL_SET(vop, alpha_hard_calc, vop->pre_overlay);
+		spin_unlock_irqrestore(&vop->irq_lock, flags);
 		drm_crtc_handle_vblank(crtc);
 		vop_handle_vblank(vop);
-		active_irqs &= ~FS_INTR;
+		active_irqs &= ~(FS_INTR | FS_FIELD_INTR);
 		ret = IRQ_HANDLED;
 	}
 
+#define ERROR_HANDLER(x) \
+	do { \
+		if (active_irqs & x##_INTR) {\
+			DRM_DEV_ERROR_RATELIMITED(vop->dev, #x " irq err\n"); \
+			active_irqs &= ~x##_INTR; \
+			ret = IRQ_HANDLED; \
+		} \
+	} while (0)
+
+	ERROR_HANDLER(BUS_ERROR);
+	ERROR_HANDLER(WIN0_EMPTY);
+	ERROR_HANDLER(WIN1_EMPTY);
+	ERROR_HANDLER(WIN2_EMPTY);
+	ERROR_HANDLER(WIN3_EMPTY);
+	ERROR_HANDLER(HWC_EMPTY);
+	ERROR_HANDLER(POST_BUF_EMPTY);
+
 	/* Unhandled irqs are spurious. */
 	if (active_irqs)
-		DRM_DEV_ERROR(vop->dev, "Unknown VOP IRQs: %#02x\n",
-			      active_irqs);
+		DRM_ERROR("Unknown VOP IRQs: %#02x\n", active_irqs);
 
 out_disable:
 	vop_core_clks_disable(vop);
@@ -1733,27 +4192,254 @@ static irqreturn_t vop_isr(int irq, void *data)
 	return ret;
 }
 
-static void vop_plane_add_properties(struct drm_plane *plane,
-				     const struct vop_win_data *win_data)
+static void vop_plane_add_properties(struct vop *vop,
+				     struct drm_plane *plane,
+				     const struct vop_win *win)
 {
 	unsigned int flags = 0;
 
-	flags |= VOP_WIN_HAS_REG(win_data, x_mir_en) ? DRM_MODE_REFLECT_X : 0;
-	flags |= VOP_WIN_HAS_REG(win_data, y_mir_en) ? DRM_MODE_REFLECT_Y : 0;
+	flags |= (VOP_WIN_SUPPORT(vop, win, xmirror)) ? DRM_MODE_REFLECT_X : 0;
+	flags |= (VOP_WIN_SUPPORT(vop, win, ymirror)) ? DRM_MODE_REFLECT_Y : 0;
+
 	if (flags)
 		drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0,
 						   DRM_MODE_ROTATE_0 | flags);
 }
 
-static int vop_create_crtc(struct vop *vop)
+static int vop_plane_create_name_property(struct vop *vop, struct vop_win *win)
+{
+	struct drm_prop_enum_list *props = vop->plane_name_list;
+	struct drm_property *prop;
+	uint64_t bits = BIT_ULL(win->plane_id);
+
+	prop = drm_property_create_bitmask(vop->drm_dev,
+					   DRM_MODE_PROP_IMMUTABLE, "NAME",
+					   props, vop->num_wins, bits);
+	if (!prop) {
+		DRM_DEV_ERROR(vop->dev, "create Name prop for %s failed\n", win->name);
+		return -ENOMEM;
+	}
+	win->name_prop = prop;
+	drm_object_attach_property(&win->base.base, win->name_prop, bits);
+
+	return 0;
+}
+
+static int vop_plane_init(struct vop *vop, struct vop_win *win,
+			  unsigned long possible_crtcs)
+{
+	struct rockchip_drm_private *private = vop->drm_dev->dev_private;
+	unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) | BIT(DRM_MODE_BLEND_PREMULTI) |
+				  BIT(DRM_MODE_BLEND_COVERAGE);
+	const struct vop_data *vop_data = vop->data;
+	uint64_t feature = 0;
+	int ret;
+
+	ret = drm_universal_plane_init(vop->drm_dev, &win->base, possible_crtcs, &vop_plane_funcs,
+				       win->data_formats, win->nformats, win->format_modifiers,
+				       win->type, win->name);
+	if (ret) {
+		DRM_ERROR("failed to initialize plane %d\n", ret);
+		return ret;
+	}
+	drm_plane_helper_add(&win->base, &plane_helper_funcs);
+
+	if (win->phy->scl)
+		feature |= BIT(ROCKCHIP_DRM_PLANE_FEATURE_SCALE);
+	if (VOP_WIN_SUPPORT(vop, win, src_alpha_ctl) ||
+	    VOP_WIN_SUPPORT(vop, win, alpha_en))
+		feature |= BIT(ROCKCHIP_DRM_PLANE_FEATURE_ALPHA);
+	if (win->feature & WIN_FEATURE_HDR2SDR)
+		feature |= BIT(ROCKCHIP_DRM_PLANE_FEATURE_HDR2SDR);
+	if (win->feature & WIN_FEATURE_SDR2HDR)
+		feature |= BIT(ROCKCHIP_DRM_PLANE_FEATURE_SDR2HDR);
+	if (win->feature & WIN_FEATURE_AFBDC)
+		feature |= BIT(ROCKCHIP_DRM_PLANE_FEATURE_AFBDC);
+
+	drm_object_attach_property(&win->base.base, vop->plane_feature_prop,
+				   feature);
+	drm_object_attach_property(&win->base.base, private->eotf_prop, 0);
+	drm_object_attach_property(&win->base.base,
+				   private->color_space_prop, 0);
+	if (VOP_WIN_SUPPORT(vop, win, global_alpha_val))
+		drm_plane_create_alpha_property(&win->base);
+	drm_object_attach_property(&win->base.base,
+				   private->async_commit_prop, 0);
+
+	if (win->parent)
+		drm_object_attach_property(&win->base.base, private->share_id_prop,
+					   win->parent->base.base.id);
+	else
+		drm_object_attach_property(&win->base.base, private->share_id_prop,
+					   win->base.base.id);
+
+	drm_plane_create_blend_mode_property(&win->base, blend_caps);
+	drm_plane_create_zpos_property(&win->base, win->win_id, 0, vop->num_wins - 1);
+	vop_plane_create_name_property(vop, win);
+
+
+	win->input_width_prop = drm_property_create_range(vop->drm_dev, DRM_MODE_PROP_IMMUTABLE,
+							  "INPUT_WIDTH", 0, vop_data->max_input.width);
+	win->input_height_prop = drm_property_create_range(vop->drm_dev, DRM_MODE_PROP_IMMUTABLE,
+							   "INPUT_HEIGHT", 0, vop_data->max_input.height);
+
+	win->output_width_prop = drm_property_create_range(vop->drm_dev, DRM_MODE_PROP_IMMUTABLE,
+							   "OUTPUT_WIDTH", 0, vop_data->max_input.width);
+	win->output_height_prop = drm_property_create_range(vop->drm_dev, DRM_MODE_PROP_IMMUTABLE,
+							    "OUTPUT_HEIGHT", 0, vop_data->max_input.height);
+
+	win->scale_prop = drm_property_create_range(vop->drm_dev, DRM_MODE_PROP_IMMUTABLE,
+						    "SCALE_RATE", 8, 8);
+	/*
+	 * Support 24 bit(RGB888) or 16 bit(rgb565) color key.
+	 * Bit 31 is used as a flag to disable (0) or enable
+	 * color keying (1).
+	 */
+	win->color_key_prop = drm_property_create_range(vop->drm_dev, 0,
+							"colorkey", 0, 0x80ffffff);
+	if (!win->input_width_prop || !win->input_height_prop ||
+	    !win->scale_prop || !win->color_key_prop) {
+		DRM_ERROR("failed to create property\n");
+		return -ENOMEM;
+	}
+
+	drm_object_attach_property(&win->base.base, win->input_width_prop, 0);
+	drm_object_attach_property(&win->base.base, win->input_height_prop, 0);
+	drm_object_attach_property(&win->base.base, win->output_width_prop, 0);
+	drm_object_attach_property(&win->base.base, win->output_height_prop, 0);
+	drm_object_attach_property(&win->base.base, win->scale_prop, 0);
+	drm_object_attach_property(&win->base.base, win->color_key_prop, 0);
+
+	return 0;
+}
+
+static int vop_of_init_display_lut(struct vop *vop)
+{
+	struct device_node *node = vop->dev->of_node;
+	struct device_node *dsp_lut;
+	u32 lut_len = vop->lut_len;
+	struct property *prop;
+	int length, i, j;
+	int ret;
+
+	if (!vop->lut)
+		return -ENOMEM;
+
+	dsp_lut = of_parse_phandle(node, "dsp-lut", 0);
+	if (!dsp_lut)
+		return -ENXIO;
+
+	prop = of_find_property(dsp_lut, "gamma-lut", &length);
+	if (!prop) {
+		dev_err(vop->dev, "failed to find gamma_lut\n");
+		return -ENXIO;
+	}
+
+	length >>= 2;
+
+	if (length != lut_len) {
+		u32 r, g, b;
+		u32 *lut = kmalloc_array(length, sizeof(*lut), GFP_KERNEL);
+
+		if (!lut)
+			return -ENOMEM;
+		ret = of_property_read_u32_array(dsp_lut, "gamma-lut", lut,
+						 length);
+		if (ret) {
+			dev_err(vop->dev, "load gamma-lut failed\n");
+			kfree(lut);
+			return -EINVAL;
+		}
+
+		for (i = 0; i < lut_len; i++) {
+			j = i * length / lut_len;
+			r = lut[j] / length / length * lut_len / length;
+			g = lut[j] / length % length * lut_len / length;
+			b = lut[j] % length * lut_len / length;
+
+			vop->lut[i] = r * lut_len * lut_len + g * lut_len + b;
+		}
+
+		kfree(lut);
+	} else {
+		of_property_read_u32_array(dsp_lut, "gamma-lut",
+					   vop->lut, vop->lut_len);
+	}
+	vop->lut_active = true;
+
+	return 0;
+}
+
+static int vop_crtc_create_plane_mask_property(struct vop *vop, struct drm_crtc *crtc)
+{
+	struct drm_property *prop;
+
+	static const struct drm_prop_enum_list props[] = {
+		{ ROCKCHIP_VOP_WIN0, "Win0" },
+		{ ROCKCHIP_VOP_WIN1, "Win1" },
+		{ ROCKCHIP_VOP_WIN2, "Win2" },
+		{ ROCKCHIP_VOP_WIN3, "Win3" },
+	};
+
+	prop = drm_property_create_bitmask(vop->drm_dev,
+					   DRM_MODE_PROP_IMMUTABLE, "PLANE_MASK",
+					   props, ARRAY_SIZE(props),
+					   0xffffffff);
+	if (!prop) {
+		DRM_DEV_ERROR(vop->dev, "create plane_mask prop for vp%d failed\n", vop->id);
+		return -ENOMEM;
+	}
+
+	vop->plane_mask_prop = prop;
+	drm_object_attach_property(&crtc->base, vop->plane_mask_prop, vop->plane_mask);
+
+	return 0;
+}
+
+static int vop_crtc_create_feature_property(struct vop *vop, struct drm_crtc *crtc)
 {
 	const struct vop_data *vop_data = vop->data;
+
+	struct drm_property *prop;
+	u64 feature = 0;
+
+	static const struct drm_prop_enum_list props[] = {
+		{ ROCKCHIP_DRM_CRTC_FEATURE_ALPHA_SCALE, "ALPHA_SCALE" },
+		{ ROCKCHIP_DRM_CRTC_FEATURE_HDR10, "HDR10" },
+		{ ROCKCHIP_DRM_CRTC_FEATURE_NEXT_HDR, "NEXT_HDR" },
+	};
+
+	if (vop_data->feature & VOP_FEATURE_ALPHA_SCALE)
+		feature |= BIT(ROCKCHIP_DRM_CRTC_FEATURE_ALPHA_SCALE);
+	if (vop_data->feature & VOP_FEATURE_HDR10)
+		feature |= BIT(ROCKCHIP_DRM_CRTC_FEATURE_HDR10);
+	if (vop_data->feature & VOP_FEATURE_NEXT_HDR)
+		feature |= BIT(ROCKCHIP_DRM_CRTC_FEATURE_NEXT_HDR);
+
+	prop = drm_property_create_bitmask(vop->drm_dev,
+					   DRM_MODE_PROP_IMMUTABLE, "FEATURE",
+					   props, ARRAY_SIZE(props),
+					   0xffffffff);
+	if (!prop) {
+		DRM_DEV_ERROR(vop->dev, "create FEATURE prop for vop%d failed\n", vop->id);
+		return -ENOMEM;
+	}
+
+	vop->feature_prop = prop;
+	drm_object_attach_property(&crtc->base, vop->feature_prop, feature);
+
+	return 0;
+}
+
+static int vop_create_crtc(struct vop *vop)
+{
 	struct device *dev = vop->dev;
 	struct drm_device *drm_dev = vop->drm_dev;
+	struct rockchip_drm_private *private = drm_dev->dev_private;
 	struct drm_plane *primary = NULL, *cursor = NULL, *plane, *tmp;
-	struct drm_crtc *crtc = &vop->crtc;
+	struct drm_crtc *crtc = &vop->rockchip_crtc.crtc;
 	struct device_node *port;
-	int ret;
+	int ret = 0;
 	int i;
 
 	/*
@@ -1761,29 +4447,19 @@ static int vop_create_crtc(struct vop *vop)
 	 * to pass them to drm_crtc_init_with_planes, which sets the
 	 * "possible_crtcs" to the newly initialized crtc.
 	 */
-	for (i = 0; i < vop_data->win_size; i++) {
-		struct vop_win *vop_win = &vop->win[i];
-		const struct vop_win_data *win_data = vop_win->data;
+	for (i = 0; i < vop->num_wins; i++) {
+		struct vop_win *win = &vop->win[i];
 
-		if (win_data->type != DRM_PLANE_TYPE_PRIMARY &&
-		    win_data->type != DRM_PLANE_TYPE_CURSOR)
+		if (win->type != DRM_PLANE_TYPE_PRIMARY &&
+		    win->type != DRM_PLANE_TYPE_CURSOR)
 			continue;
 
-		ret = drm_universal_plane_init(vop->drm_dev, &vop_win->base,
-					       0, &vop_plane_funcs,
-					       win_data->phy->data_formats,
-					       win_data->phy->nformats,
-					       win_data->phy->format_modifiers,
-					       win_data->type, NULL);
-		if (ret) {
-			DRM_DEV_ERROR(vop->dev, "failed to init plane %d\n",
-				      ret);
+		if (vop_plane_init(vop, win, 0)) {
+			DRM_DEV_ERROR(vop->dev, "failed to init plane\n");
 			goto err_cleanup_planes;
 		}
 
-		plane = &vop_win->base;
-		drm_plane_helper_add(plane, &plane_helper_funcs);
-		vop_plane_add_properties(plane, win_data);
+		plane = &win->base;
 		if (plane->type == DRM_PLANE_TYPE_PRIMARY)
 			primary = plane;
 		else if (plane->type == DRM_PLANE_TYPE_CURSOR)
@@ -1796,37 +4472,23 @@ static int vop_create_crtc(struct vop *vop)
 		goto err_cleanup_planes;
 
 	drm_crtc_helper_add(crtc, &vop_crtc_helper_funcs);
-	if (vop->lut_regs) {
-		drm_mode_crtc_set_gamma_size(crtc, vop_data->lut_size);
-		drm_crtc_enable_color_mgmt(crtc, 0, false, vop_data->lut_size);
-	}
 
 	/*
 	 * Create drm_planes for overlay windows with possible_crtcs restricted
 	 * to the newly created crtc.
 	 */
-	for (i = 0; i < vop_data->win_size; i++) {
-		struct vop_win *vop_win = &vop->win[i];
-		const struct vop_win_data *win_data = vop_win->data;
+	for (i = 0; i < vop->num_wins; i++) {
+		struct vop_win *win = &vop->win[i];
 		unsigned long possible_crtcs = drm_crtc_mask(crtc);
 
-		if (win_data->type != DRM_PLANE_TYPE_OVERLAY)
+		if (win->type != DRM_PLANE_TYPE_OVERLAY)
 			continue;
 
-		ret = drm_universal_plane_init(vop->drm_dev, &vop_win->base,
-					       possible_crtcs,
-					       &vop_plane_funcs,
-					       win_data->phy->data_formats,
-					       win_data->phy->nformats,
-					       win_data->phy->format_modifiers,
-					       win_data->type, NULL);
-		if (ret) {
-			DRM_DEV_ERROR(vop->dev, "failed to init overlay %d\n",
-				      ret);
+		if (vop_plane_init(vop, win, possible_crtcs)) {
+			DRM_DEV_ERROR(vop->dev, "failed to init overlay\n");
 			goto err_cleanup_crtc;
 		}
-		drm_plane_helper_add(&vop_win->base, &plane_helper_funcs);
-		vop_plane_add_properties(&vop_win->base, win_data);
+		vop_plane_add_properties(vop, &win->base, win);
 	}
 
 	port = of_get_child_by_name(dev->of_node, "port");
@@ -1843,15 +4505,60 @@ static int vop_create_crtc(struct vop *vop)
 	init_completion(&vop->dsp_hold_completion);
 	init_completion(&vop->line_flag_completion);
 	crtc->port = port;
+	rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
 
-	ret = drm_self_refresh_helper_init(crtc);
-	if (ret)
-		DRM_DEV_DEBUG_KMS(vop->dev,
-			"Failed to init %s with SR helpers %d, ignoring\n",
-			crtc->name, ret);
+	drm_object_attach_property(&crtc->base, private->soc_id_prop, vop->soc_id);
+	drm_object_attach_property(&crtc->base, private->port_id_prop, vop->id);
+	drm_object_attach_property(&crtc->base, private->aclk_prop, 0);
+	drm_object_attach_property(&crtc->base, private->bg_prop, 0);
+	drm_object_attach_property(&crtc->base, private->line_flag_prop, 0);
+
+#define VOP_ATTACH_MODE_CONFIG_PROP(prop, v) \
+	drm_object_attach_property(&crtc->base, drm_dev->mode_config.prop, v)
+
+	VOP_ATTACH_MODE_CONFIG_PROP(tv_left_margin_property, 100);
+	VOP_ATTACH_MODE_CONFIG_PROP(tv_right_margin_property, 100);
+	VOP_ATTACH_MODE_CONFIG_PROP(tv_top_margin_property, 100);
+	VOP_ATTACH_MODE_CONFIG_PROP(tv_bottom_margin_property, 100);
+#undef VOP_ATTACH_MODE_CONFIG_PROP
+	vop_crtc_create_plane_mask_property(vop, crtc);
+	vop_crtc_create_feature_property(vop, crtc);
 
+	if (vop->lut_regs) {
+		u16 *r_base, *g_base, *b_base;
+		u32 lut_len = vop->lut_len;
+
+		vop->lut = devm_kmalloc_array(dev, lut_len, sizeof(*vop->lut),
+					      GFP_KERNEL);
+		if (!vop->lut)
+			goto err_unregister_crtc_funcs;
+
+		if (vop_of_init_display_lut(vop)) {
+			for (i = 0; i < lut_len; i++) {
+				u32 r = i * lut_len * lut_len;
+				u32 g = i * lut_len;
+				u32 b = i;
+
+				vop->lut[i] = r | g | b;
+			}
+		}
+
+		drm_mode_crtc_set_gamma_size(crtc, lut_len);
+		drm_crtc_enable_color_mgmt(crtc, 0, false, lut_len);
+		r_base = crtc->gamma_store;
+		g_base = r_base + crtc->gamma_size;
+		b_base = g_base + crtc->gamma_size;
+
+		for (i = 0; i < lut_len; i++) {
+			rockchip_vop_crtc_fb_gamma_get(crtc, &r_base[i],
+						       &g_base[i], &b_base[i],
+						       i);
+		}
+	}
 	return 0;
 
+err_unregister_crtc_funcs:
+	rockchip_unregister_crtc_funcs(crtc);
 err_cleanup_crtc:
 	drm_crtc_cleanup(crtc);
 err_cleanup_planes:
@@ -1863,12 +4570,10 @@ static int vop_create_crtc(struct vop *vop)
 
 static void vop_destroy_crtc(struct vop *vop)
 {
-	struct drm_crtc *crtc = &vop->crtc;
+	struct drm_crtc *crtc = &vop->rockchip_crtc.crtc;
 	struct drm_device *drm_dev = vop->drm_dev;
 	struct drm_plane *plane, *tmp;
 
-	drm_self_refresh_helper_cleanup(crtc);
-
 	of_node_put(crtc->port);
 
 	/*
@@ -1891,137 +4596,129 @@ static void vop_destroy_crtc(struct vop *vop)
 	drm_flip_work_cleanup(&vop->fb_unref_work);
 }
 
-static int vop_initial(struct vop *vop)
+/*
+ * Win_id is the order in vop_win_data array.
+ * This is related to the actual hardware plane.
+ * But in the Linux platform, such as video hardware and camera preview,
+ * it can only be played on the nv12 plane.
+ * So set the order of zpos to PRIMARY < OVERLAY (if have) < CURSOR (if have).
+ */
+static int vop_plane_get_zpos(enum drm_plane_type type, unsigned int size)
 {
-	struct reset_control *ahb_rst;
-	int i, ret;
-
-	vop->hclk = devm_clk_get(vop->dev, "hclk_vop");
-	if (IS_ERR(vop->hclk)) {
-		DRM_DEV_ERROR(vop->dev, "failed to get hclk source\n");
-		return PTR_ERR(vop->hclk);
-	}
-	vop->aclk = devm_clk_get(vop->dev, "aclk_vop");
-	if (IS_ERR(vop->aclk)) {
-		DRM_DEV_ERROR(vop->dev, "failed to get aclk source\n");
-		return PTR_ERR(vop->aclk);
-	}
-	vop->dclk = devm_clk_get(vop->dev, "dclk_vop");
-	if (IS_ERR(vop->dclk)) {
-		DRM_DEV_ERROR(vop->dev, "failed to get dclk source\n");
-		return PTR_ERR(vop->dclk);
-	}
-
-	ret = pm_runtime_get_sync(vop->dev);
-	if (ret < 0) {
-		DRM_DEV_ERROR(vop->dev, "failed to get pm runtime: %d\n", ret);
-		return ret;
-	}
-
-	ret = clk_prepare(vop->dclk);
-	if (ret < 0) {
-		DRM_DEV_ERROR(vop->dev, "failed to prepare dclk\n");
-		goto err_put_pm_runtime;
-	}
-
-	/* Enable both the hclk and aclk to setup the vop */
-	ret = clk_prepare_enable(vop->hclk);
-	if (ret < 0) {
-		DRM_DEV_ERROR(vop->dev, "failed to prepare/enable hclk\n");
-		goto err_unprepare_dclk;
+	switch (type) {
+	case DRM_PLANE_TYPE_PRIMARY:
+		return 0;
+	case DRM_PLANE_TYPE_OVERLAY:
+		return 1;
+	case DRM_PLANE_TYPE_CURSOR:
+		return size - 1;
 	}
+	return 0;
+}
 
-	ret = clk_prepare_enable(vop->aclk);
-	if (ret < 0) {
-		DRM_DEV_ERROR(vop->dev, "failed to prepare/enable aclk\n");
-		goto err_disable_hclk;
-	}
+/*
+ * Initialize the vop->win array elements.
+ */
+static int vop_win_init(struct vop *vop)
+{
+	const struct vop_data *vop_data = vop->data;
+	unsigned int i, j;
+	unsigned int num_wins = 0;
+	char name[DRM_PROP_NAME_LEN];
+	uint8_t plane_id = 0;
+	struct drm_prop_enum_list *plane_name_list;
+	static const struct drm_prop_enum_list props[] = {
+		{ ROCKCHIP_DRM_PLANE_FEATURE_SCALE, "scale" },
+		{ ROCKCHIP_DRM_PLANE_FEATURE_ALPHA, "alpha" },
+		{ ROCKCHIP_DRM_PLANE_FEATURE_HDR2SDR, "hdr2sdr" },
+		{ ROCKCHIP_DRM_PLANE_FEATURE_SDR2HDR, "sdr2hdr" },
+		{ ROCKCHIP_DRM_PLANE_FEATURE_AFBDC, "afbdc" },
+	};
 
-	/*
-	 * do hclk_reset, reset all vop registers.
-	 */
-	ahb_rst = devm_reset_control_get(vop->dev, "ahb");
-	if (IS_ERR(ahb_rst)) {
-		DRM_DEV_ERROR(vop->dev, "failed to get ahb reset\n");
-		ret = PTR_ERR(ahb_rst);
-		goto err_disable_aclk;
-	}
-	reset_control_assert(ahb_rst);
-	usleep_range(10, 20);
-	reset_control_deassert(ahb_rst);
+	for (i = 0; i < vop_data->win_size; i++) {
+		struct vop_win *vop_win = &vop->win[num_wins];
+		const struct vop_win_data *win_data = &vop_data->win[i];
 
-	VOP_INTR_SET_TYPE(vop, clear, INTR_MASK, 1);
-	VOP_INTR_SET_TYPE(vop, enable, INTR_MASK, 0);
+		if (!win_data->phy)
+			continue;
 
-	for (i = 0; i < vop->len; i += sizeof(u32))
-		vop->regsbak[i / 4] = readl_relaxed(vop->regs + i);
+		vop_win->phy = win_data->phy;
+		vop_win->csc = win_data->csc;
+		vop_win->offset = win_data->base;
+		vop_win->type = win_data->type;
+		vop_win->data_formats = win_data->phy->data_formats;
+		vop_win->nformats = win_data->phy->nformats;
+		vop_win->format_modifiers = win_data->format_modifiers;
+		vop_win->feature = win_data->feature;
+		vop_win->vop = vop;
+		vop_win->win_id = i;
+		vop_win->area_id = 0;
+		vop_win->plane_id = plane_id++;
+		snprintf(name, sizeof(name), "VOP%d-win%d-%d", vop->id, vop_win->win_id, vop_win->area_id);
+		vop_win->name = devm_kstrdup(vop->dev, name, GFP_KERNEL);
+		vop_win->zpos = vop_plane_get_zpos(win_data->type,
+						   vop_data->win_size);
 
-	VOP_REG_SET(vop, misc, global_regdone_en, 1);
-	VOP_REG_SET(vop, common, dsp_blank, 0);
+		num_wins++;
 
-	for (i = 0; i < vop->data->win_size; i++) {
-		struct vop_win *vop_win = &vop->win[i];
-		const struct vop_win_data *win = vop_win->data;
-		int channel = i * 2 + 1;
+		if (!vop->support_multi_area)
+			continue;
 
-		VOP_WIN_SET(vop, win, channel, (channel + 1) << 4 | channel);
-		vop_win_disable(vop, vop_win);
-		VOP_WIN_SET(vop, win, gate, 1);
+		for (j = 0; j < win_data->area_size; j++) {
+			struct vop_win *vop_area = &vop->win[num_wins];
+			const struct vop_win_phy *area = win_data->area[j];
+
+			vop_area->parent = vop_win;
+			vop_area->offset = vop_win->offset;
+			vop_area->phy = area;
+			vop_area->type = DRM_PLANE_TYPE_OVERLAY;
+			vop_area->data_formats = vop_win->data_formats;
+			vop_area->nformats = vop_win->nformats;
+			vop_area->format_modifiers = win_data->format_modifiers;
+			vop_area->vop = vop;
+			vop_area->win_id = i;
+			vop_area->area_id = j + 1;
+			vop_area->plane_id = plane_id++;
+			snprintf(name, sizeof(name), "VOP%d-win%d-%d", vop->id, vop_area->win_id, vop_area->area_id);
+			vop_area->name = devm_kstrdup(vop->dev, name, GFP_KERNEL);
+			num_wins++;
+		}
+		vop->plane_mask |= BIT(vop_win->win_id);
 	}
 
-	vop_cfg_done(vop);
+	vop->num_wins = num_wins;
+
+	vop->plane_feature_prop = drm_property_create_bitmask(vop->drm_dev,
+				DRM_MODE_PROP_IMMUTABLE, "FEATURE",
+				props, ARRAY_SIZE(props),
+				BIT(ROCKCHIP_DRM_PLANE_FEATURE_SCALE) |
+				BIT(ROCKCHIP_DRM_PLANE_FEATURE_ALPHA) |
+				BIT(ROCKCHIP_DRM_PLANE_FEATURE_HDR2SDR) |
+				BIT(ROCKCHIP_DRM_PLANE_FEATURE_SDR2HDR) |
+				BIT(ROCKCHIP_DRM_PLANE_FEATURE_AFBDC));
+	if (!vop->plane_feature_prop) {
+		DRM_ERROR("failed to create feature property\n");
+		return -EINVAL;
+	}
 
-	/*
-	 * do dclk_reset, let all config take affect.
-	 */
-	vop->dclk_rst = devm_reset_control_get(vop->dev, "dclk");
-	if (IS_ERR(vop->dclk_rst)) {
-		DRM_DEV_ERROR(vop->dev, "failed to get dclk reset\n");
-		ret = PTR_ERR(vop->dclk_rst);
-		goto err_disable_aclk;
+	plane_name_list = devm_kzalloc(vop->dev,
+				       vop->num_wins * sizeof(*plane_name_list),
+				       GFP_KERNEL);
+	if (!plane_name_list) {
+		DRM_DEV_ERROR(vop->dev, "failed to alloc memory for plane_name_list\n");
+		return -ENOMEM;
 	}
-	reset_control_assert(vop->dclk_rst);
-	usleep_range(10, 20);
-	reset_control_deassert(vop->dclk_rst);
 
-	clk_disable(vop->hclk);
-	clk_disable(vop->aclk);
+	for (i = 0; i < vop->num_wins; i++) {
+		struct vop_win *vop_win = &vop->win[i];
 
-	vop->is_enabled = false;
+		plane_name_list[i].type = vop_win->plane_id;
+		plane_name_list[i].name = vop_win->name;
+	}
 
-	pm_runtime_put_sync(vop->dev);
+	vop->plane_name_list = plane_name_list;
 
 	return 0;
-
-err_disable_aclk:
-	clk_disable_unprepare(vop->aclk);
-err_disable_hclk:
-	clk_disable_unprepare(vop->hclk);
-err_unprepare_dclk:
-	clk_unprepare(vop->dclk);
-err_put_pm_runtime:
-	pm_runtime_put_sync(vop->dev);
-	return ret;
-}
-
-/*
- * Initialize the vop->win array elements.
- */
-static void vop_win_init(struct vop *vop)
-{
-	const struct vop_data *vop_data = vop->data;
-	unsigned int i;
-
-	for (i = 0; i < vop_data->win_size; i++) {
-		struct vop_win *vop_win = &vop->win[i];
-		const struct vop_win_data *win_data = &vop_data->win[i];
-
-		vop_win->data = win_data;
-		vop_win->vop = vop;
-
-		if (vop_data->win_yuv2yuv)
-			vop_win->yuv2yuv_data = &vop_data->win_yuv2yuv[i];
-	}
 }
 
 /**
@@ -2080,46 +4777,97 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
 	struct drm_device *drm_dev = data;
 	struct vop *vop;
 	struct resource *res;
-	int ret, irq;
+	size_t alloc_size;
+	int ret, irq, i;
+	int num_wins = 0;
+	bool dual_channel_swap = false;
+	struct device_node *mcu = NULL;
 
 	vop_data = of_device_get_match_data(dev);
 	if (!vop_data)
 		return -ENODEV;
 
+	for (i = 0; i < vop_data->win_size; i++) {
+		const struct vop_win_data *win_data = &vop_data->win[i];
+
+		num_wins += win_data->area_size + 1;
+	}
+
 	/* Allocate vop struct and its vop_win array */
-	vop = devm_kzalloc(dev, struct_size(vop, win, vop_data->win_size),
-			   GFP_KERNEL);
+	alloc_size = sizeof(*vop) + sizeof(*vop->win) * num_wins;
+	vop = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
 	if (!vop)
 		return -ENOMEM;
 
 	vop->dev = dev;
 	vop->data = vop_data;
 	vop->drm_dev = drm_dev;
+	vop->num_wins = num_wins;
+	vop->version = vop_data->version;
+	vop->soc_id = vop_data->soc_id;
+	vop->id = vop_data->vop_id;
 	dev_set_drvdata(dev, vop);
+	vop->support_multi_area = of_property_read_bool(dev->of_node, "support-multi-area");
 
-	vop_win_init(vop);
+	ret = vop_win_init(vop);
+	if (ret)
+		return ret;
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+	if (!res) {
+		dev_warn(vop->dev, "failed to get vop register byname\n");
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	}
 	vop->regs = devm_ioremap_resource(dev, res);
 	if (IS_ERR(vop->regs))
 		return PTR_ERR(vop->regs);
 	vop->len = resource_size(res);
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	vop->regsbak = devm_kzalloc(dev, vop->len, GFP_KERNEL);
+	if (!vop->regsbak)
+		return -ENOMEM;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gamma_lut");
 	if (res) {
-		if (!vop_data->lut_size) {
-			DRM_DEV_ERROR(dev, "no gamma LUT size defined\n");
+		vop->lut_len = resource_size(res) / sizeof(*vop->lut);
+		if (vop->lut_len != 256 && vop->lut_len != 1024) {
+			dev_err(vop->dev, "unsupported lut sizes %d\n",
+				vop->lut_len);
 			return -EINVAL;
 		}
+
 		vop->lut_regs = devm_ioremap_resource(dev, res);
 		if (IS_ERR(vop->lut_regs))
 			return PTR_ERR(vop->lut_regs);
 	}
-
-	vop->regsbak = devm_kzalloc(dev, vop->len, GFP_KERNEL);
-	if (!vop->regsbak)
-		return -ENOMEM;
-
+	vop->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
+						   "rockchip,grf");
+	if (IS_ERR(vop->grf))
+		dev_err(dev, "missing rockchip,grf property\n");
+	vop->hclk = devm_clk_get(vop->dev, "hclk_vop");
+	if (IS_ERR(vop->hclk)) {
+		dev_err(vop->dev, "failed to get hclk source\n");
+		return PTR_ERR(vop->hclk);
+	}
+	vop->aclk = devm_clk_get(vop->dev, "aclk_vop");
+	if (IS_ERR(vop->aclk)) {
+		dev_err(vop->dev, "failed to get aclk source\n");
+		return PTR_ERR(vop->aclk);
+	}
+	vop->dclk = devm_clk_get(vop->dev, "dclk_vop");
+	if (IS_ERR(vop->dclk)) {
+		dev_err(vop->dev, "failed to get dclk source\n");
+		return PTR_ERR(vop->dclk);
+	}
+	vop->dclk_source = devm_clk_get(vop->dev, "dclk_source");
+	if (PTR_ERR(vop->dclk_source) == -ENOENT) {
+		vop->dclk_source = NULL;
+	} else if (PTR_ERR(vop->dclk_source) == -EPROBE_DEFER) {
+		return -EPROBE_DEFER;
+	} else if (IS_ERR(vop->dclk_source)) {
+		dev_err(vop->dev, "failed to get dclk source parent\n");
+		return PTR_ERR(vop->dclk_source);
+	}
 	irq = platform_get_irq(pdev, 0);
 	if (irq < 0) {
 		DRM_DEV_ERROR(dev, "cannot find irq for vop\n");
@@ -2131,53 +4879,51 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
 	spin_lock_init(&vop->irq_lock);
 	mutex_init(&vop->vop_lock);
 
+	ret = devm_request_irq(dev, vop->irq, vop_isr,
+			       IRQF_SHARED, dev_name(dev), vop);
+	if (ret)
+		return ret;
 	ret = vop_create_crtc(vop);
 	if (ret)
 		return ret;
 
 	pm_runtime_enable(&pdev->dev);
 
-	ret = vop_initial(vop);
-	if (ret < 0) {
-		DRM_DEV_ERROR(&pdev->dev,
-			      "cannot initial vop dev - err %d\n", ret);
-		goto err_disable_pm_runtime;
-	}
-
-	ret = devm_request_irq(dev, vop->irq, vop_isr,
-			       IRQF_SHARED, dev_name(dev), vop);
-	if (ret)
-		goto err_disable_pm_runtime;
 
-	if (vop->data->feature & VOP_FEATURE_INTERNAL_RGB) {
-		vop->rgb = rockchip_rgb_init(dev, &vop->crtc, vop->drm_dev);
-		if (IS_ERR(vop->rgb)) {
-			ret = PTR_ERR(vop->rgb);
-			goto err_disable_pm_runtime;
-		}
+	mcu = of_get_child_by_name(dev->of_node, "mcu-timing");
+	if (!mcu) {
+		dev_dbg(dev, "no mcu-timing node found in %s\n",
+			dev->of_node->full_name);
+	} else {
+		u32 val;
+
+		if (!of_property_read_u32(mcu, "mcu-pix-total", &val))
+			vop->mcu_timing.mcu_pix_total = val;
+		if (!of_property_read_u32(mcu, "mcu-cs-pst", &val))
+			vop->mcu_timing.mcu_cs_pst = val;
+		if (!of_property_read_u32(mcu, "mcu-cs-pend", &val))
+			vop->mcu_timing.mcu_cs_pend = val;
+		if (!of_property_read_u32(mcu, "mcu-rw-pst", &val))
+			vop->mcu_timing.mcu_rw_pst = val;
+		if (!of_property_read_u32(mcu, "mcu-rw-pend", &val))
+			vop->mcu_timing.mcu_rw_pend = val;
+		if (!of_property_read_u32(mcu, "mcu-hold-mode", &val))
+			vop->mcu_timing.mcu_hold_mode = val;
 	}
 
-	return 0;
+	dual_channel_swap = of_property_read_bool(dev->of_node,
+						  "rockchip,dual-channel-swap");
+	vop->dual_channel_swap = dual_channel_swap;
 
-err_disable_pm_runtime:
-	pm_runtime_disable(&pdev->dev);
-	vop_destroy_crtc(vop);
-	return ret;
+	return 0;
 }
 
 static void vop_unbind(struct device *dev, struct device *master, void *data)
 {
 	struct vop *vop = dev_get_drvdata(dev);
 
-	if (vop->rgb)
-		rockchip_rgb_fini(vop->rgb);
-
 	pm_runtime_disable(dev);
 	vop_destroy_crtc(vop);
-
-	clk_unprepare(vop->aclk);
-	clk_unprepare(vop->hclk);
-	clk_unprepare(vop->dclk);
 }
 
 const struct component_ops vop_component_ops = {
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index 857d97cdc..8c2b38419 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -7,6 +7,9 @@
 #ifndef _ROCKCHIP_DRM_VOP_H
 #define _ROCKCHIP_DRM_VOP_H
 
+#include <drm/drm_plane.h>
+#include <drm/drm_modes.h>
+
 /*
  * major: IP major version, used for IP structure
  * minor: big feature change under same structure
@@ -15,104 +18,334 @@
 #define VOP_MAJOR(version)		((version) >> 8)
 #define VOP_MINOR(version)		((version) & 0xff)
 
-#define NUM_YUV2YUV_COEFFICIENTS 12
+#define VOP_VERSION_RK3568	VOP_VERSION(0x40, 0x15)
+#define VOP_VERSION_RK3588	VOP_VERSION(0x40, 0x17)
 
-/* AFBC supports a number of configurable modes. Relevant to us is block size
- * (16x16 or 32x8), storage modifiers (SPARSE, SPLIT), and the YUV-like
- * colourspace transform (YTR). 16x16 SPARSE mode is always used. SPLIT mode
- * could be enabled via the hreg_block_split register, but is not currently
- * handled. The colourspace transform is implicitly always assumed by the
- * decoder, so consumers must use this transform as well.
- *
- * Failure to match modifiers will cause errors displaying AFBC buffers
- * produced by conformant AFBC producers, including Mesa.
+#define ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE	BIT(0)
+#define ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE	BIT(1)
+#define ROCKCHIP_OUTPUT_DATA_SWAP			BIT(2)
+/* MIPI DSI DataStream(cmd) mode on rk3588 */
+#define ROCKCHIP_OUTPUT_MIPI_DS_MODE			BIT(3)
+
+#define AFBDC_FMT_RGB565	0x0
+#define AFBDC_FMT_U8U8U8U8	0x5
+#define AFBDC_FMT_U8U8U8	0x4
+
+#define VOP_FEATURE_OUTPUT_RGB10	BIT(0)
+#define VOP_FEATURE_INTERNAL_RGB	BIT(1)
+#define VOP_FEATURE_ALPHA_SCALE		BIT(2)
+#define VOP_FEATURE_HDR10		BIT(3)
+#define VOP_FEATURE_NEXT_HDR		BIT(4)
+/* a feature to splice two windows and two vps to support resolution > 4096 */
+#define VOP_FEATURE_SPLICE		BIT(5)
+#define VOP_FEATURE_OVERSCAN		BIT(6)
+
+#define VOP_FEATURE_OUTPUT_10BIT	VOP_FEATURE_OUTPUT_RGB10
+
+
+#define WIN_FEATURE_HDR2SDR		BIT(0)
+#define WIN_FEATURE_SDR2HDR		BIT(1)
+#define WIN_FEATURE_PRE_OVERLAY		BIT(2)
+#define WIN_FEATURE_AFBDC		BIT(3)
+#define WIN_FEATURE_CLUSTER_MAIN	BIT(4)
+#define WIN_FEATURE_CLUSTER_SUB		BIT(5)
+/* Left win in splice mode */
+#define WIN_FEATURE_SPLICE_LEFT		BIT(6)
+/* a mirror win can only get fb address
+ * from source win:
+ * Cluster1---->Cluster0
+ * Esmart1 ---->Esmart0
+ * Smart1  ---->Smart0
+ * This is a feather on rk3566
  */
-#define ROCKCHIP_AFBC_MOD \
-	DRM_FORMAT_MOD_ARM_AFBC( \
-		AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | AFBC_FORMAT_MOD_SPARSE \
-			| AFBC_FORMAT_MOD_YTR \
-	)
+#define WIN_FEATURE_MIRROR		BIT(6)
+#define WIN_FEATURE_MULTI_AREA		BIT(7)
+
+
+#define VOP2_SOC_VARIANT		4
+
+#define ROCKCHIP_DSC_PPS_SIZE_BYTE	88
+
+enum vop_win_phy_id {
+	ROCKCHIP_VOP_WIN0 = 0,
+	ROCKCHIP_VOP_WIN1,
+	ROCKCHIP_VOP_WIN2,
+	ROCKCHIP_VOP_WIN3,
+	ROCKCHIP_VOP_PHY_ID_INVALID = -1,
+};
+
+enum bcsh_out_mode {
+	BCSH_OUT_MODE_BLACK,
+	BCSH_OUT_MODE_BLUE,
+	BCSH_OUT_MODE_COLOR_BAR,
+	BCSH_OUT_MODE_NORMAL_VIDEO,
+};
+
+enum cabc_stage_mode {
+	LAST_FRAME_PWM_VAL	= 0x0,
+	CUR_FRAME_PWM_VAL	= 0x1,
+	STAGE_BY_STAGE		= 0x2
+};
+
+enum cabc_stage_up_mode {
+	MUL_MODE,
+	ADD_MODE,
+};
+
+/*
+ *  the delay number of a window in different mode.
+ */
+enum vop2_win_dly_mode {
+	VOP2_DLY_MODE_DEFAULT,   /**< default mode */
+	VOP2_DLY_MODE_HISO_S,    /** HDR in SDR out mode, as a SDR window */
+	VOP2_DLY_MODE_HIHO_H,    /** HDR in HDR out mode, as a HDR window */
+	VOP2_DLY_MODE_MAX,
+};
+
+/*
+ * vop2 internal power domain id,
+ * should be all none zero, 0 will be
+ * treat as invalid;
+ */
+#define VOP2_PD_CLUSTER0	BIT(0)
+#define VOP2_PD_CLUSTER1	BIT(1)
+#define VOP2_PD_CLUSTER2	BIT(2)
+#define VOP2_PD_CLUSTER3	BIT(3)
+#define VOP2_PD_DSC_8K		BIT(5)
+#define VOP2_PD_DSC_4K		BIT(6)
+#define VOP2_PD_ESMART0		BIT(7)
+
+/*
+ * vop2 submem power gate,
+ * should be all none zero, 0 will be
+ * treat as invalid;
+ */
+#define VOP2_MEM_PG_VP0		BIT(0)
+#define VOP2_MEM_PG_VP1		BIT(1)
+#define VOP2_MEM_PG_VP2		BIT(2)
+#define VOP2_MEM_PG_VP3		BIT(3)
+#define VOP2_MEM_PG_DB0		BIT(4)
+#define VOP2_MEM_PG_DB1		BIT(5)
+#define VOP2_MEM_PG_DB2		BIT(6)
+#define VOP2_MEM_PG_WB		BIT(7)
+
+#define DSP_BG_SWAP		0x1
+#define DSP_RB_SWAP		0x2
+#define DSP_RG_SWAP		0x4
+#define DSP_DELTA_SWAP		0x8
+
+enum vop_csc_format {
+	CSC_BT601L,
+	CSC_BT709L,
+	CSC_BT601F,
+	CSC_BT2020,
+};
+
+enum vop_csc_mode {
+	CSC_RGB,
+	CSC_YUV,
+};
 
 enum vop_data_format {
 	VOP_FMT_ARGB8888 = 0,
 	VOP_FMT_RGB888,
-	VOP_FMT_RGB565,
+	VOP_FMT_RGB565 = 2,
+	VOP_FMT_YUYV = 2,
 	VOP_FMT_YUV420SP = 4,
 	VOP_FMT_YUV422SP,
 	VOP_FMT_YUV444SP,
 };
 
+enum vop_dsc_interface_mode {
+	VOP_DSC_IF_DISABLE = 0,
+	VOP_DSC_IF_HDMI = 1,
+	VOP_DSC_IF_MIPI_DS_MODE = 2,
+	VOP_DSC_IF_MIPI_VIDEO_MODE = 3,
+};
+
+struct vop_reg_data {
+	uint32_t offset;
+	uint32_t value;
+};
+
 struct vop_reg {
 	uint32_t mask;
-	uint16_t offset;
-	uint8_t shift;
-	bool write_mask;
-	bool relaxed;
+	uint32_t offset:17;
+	uint32_t shift:5;
+	uint32_t begin_minor:4;
+	uint32_t end_minor:4;
+	uint32_t reserved:2;
+	uint32_t major:3;
+	uint32_t write_mask:1;
 };
 
-struct vop_afbc {
-	struct vop_reg enable;
-	struct vop_reg win_sel;
-	struct vop_reg format;
-	struct vop_reg hreg_block_split;
-	struct vop_reg pic_size;
-	struct vop_reg hdr_ptr;
-	struct vop_reg rstn;
+struct vop_csc {
+	struct vop_reg y2r_en;
+	struct vop_reg r2r_en;
+	struct vop_reg r2y_en;
+	struct vop_reg csc_mode;
+
+	uint32_t y2r_offset;
+	uint32_t r2r_offset;
+	uint32_t r2y_offset;
 };
 
-struct vop_modeset {
+struct vop_rect {
+	int width;
+	int height;
+};
+
+struct vop_ctrl {
+	struct vop_reg version;
+	struct vop_reg standby;
+	struct vop_reg dma_stop;
+	struct vop_reg axi_outstanding_max_num;
+	struct vop_reg axi_max_outstanding_en;
 	struct vop_reg htotal_pw;
 	struct vop_reg hact_st_end;
-	struct vop_reg hpost_st_end;
 	struct vop_reg vtotal_pw;
 	struct vop_reg vact_st_end;
+	struct vop_reg vact_st_end_f1;
+	struct vop_reg vs_st_end_f1;
+	struct vop_reg hpost_st_end;
 	struct vop_reg vpost_st_end;
-};
-
-struct vop_output {
-	struct vop_reg pin_pol;
-	struct vop_reg dp_pin_pol;
-	struct vop_reg dp_dclk_pol;
-	struct vop_reg edp_pin_pol;
-	struct vop_reg edp_dclk_pol;
-	struct vop_reg hdmi_pin_pol;
-	struct vop_reg hdmi_dclk_pol;
-	struct vop_reg mipi_pin_pol;
-	struct vop_reg mipi_dclk_pol;
-	struct vop_reg rgb_pin_pol;
-	struct vop_reg rgb_dclk_pol;
-	struct vop_reg dp_en;
+	struct vop_reg vpost_st_end_f1;
+	struct vop_reg post_scl_factor;
+	struct vop_reg post_scl_ctrl;
+	struct vop_reg dsp_interlace;
+	struct vop_reg global_regdone_en;
+	struct vop_reg auto_gate_en;
+	struct vop_reg post_lb_mode;
+	struct vop_reg dsp_layer_sel;
+	struct vop_reg overlay_mode;
+	struct vop_reg core_dclk_div;
+	struct vop_reg dclk_ddr;
+	struct vop_reg p2i_en;
+	struct vop_reg hdmi_dclk_out_en;
+	struct vop_reg rgb_en;
+	struct vop_reg lvds_en;
 	struct vop_reg edp_en;
 	struct vop_reg hdmi_en;
 	struct vop_reg mipi_en;
+	struct vop_reg data01_swap;
 	struct vop_reg mipi_dual_channel_en;
-	struct vop_reg rgb_en;
-};
-
-struct vop_common {
-	struct vop_reg cfg_done;
-	struct vop_reg dsp_blank;
-	struct vop_reg data_blank;
-	struct vop_reg pre_dither_down;
+	struct vop_reg dp_en;
+	struct vop_reg dclk_pol;
+	struct vop_reg pin_pol;
+	struct vop_reg rgb_dclk_pol;
+	struct vop_reg rgb_pin_pol;
+	struct vop_reg lvds_dclk_pol;
+	struct vop_reg lvds_pin_pol;
+	struct vop_reg hdmi_dclk_pol;
+	struct vop_reg hdmi_pin_pol;
+	struct vop_reg edp_dclk_pol;
+	struct vop_reg edp_pin_pol;
+	struct vop_reg mipi_dclk_pol;
+	struct vop_reg mipi_pin_pol;
+	struct vop_reg dp_dclk_pol;
+	struct vop_reg dp_pin_pol;
 	struct vop_reg dither_down_sel;
 	struct vop_reg dither_down_mode;
 	struct vop_reg dither_down_en;
-	struct vop_reg dither_up;
+	struct vop_reg pre_dither_down_en;
+	struct vop_reg dither_up_en;
+
+	struct vop_reg sw_dac_sel;
+	struct vop_reg tve_sw_mode;
+	struct vop_reg tve_dclk_pol;
+	struct vop_reg tve_dclk_en;
+	struct vop_reg sw_genlock;
+	struct vop_reg sw_uv_offset_en;
+	struct vop_reg dsp_out_yuv;
+	struct vop_reg dsp_data_swap;
+	struct vop_reg yuv_clip;
+	struct vop_reg dsp_ccir656_avg;
+	struct vop_reg dsp_black;
+	struct vop_reg dsp_blank;
+	struct vop_reg dsp_outzero;
+	struct vop_reg update_gamma_lut;
+	struct vop_reg lut_buffer_index;
 	struct vop_reg dsp_lut_en;
-	struct vop_reg gate_en;
-	struct vop_reg mmu_en;
+
 	struct vop_reg out_mode;
-	struct vop_reg standby;
-};
 
-struct vop_misc {
-	struct vop_reg global_regdone_en;
+	struct vop_reg xmirror;
+	struct vop_reg ymirror;
+	struct vop_reg dsp_background;
+
+	/* AFBDC */
+	struct vop_reg afbdc_en;
+	struct vop_reg afbdc_sel;
+	struct vop_reg afbdc_format;
+	struct vop_reg afbdc_hreg_block_split;
+	struct vop_reg afbdc_pic_size;
+	struct vop_reg afbdc_hdr_ptr;
+	struct vop_reg afbdc_rstn;
+	struct vop_reg afbdc_pic_vir_width;
+	struct vop_reg afbdc_pic_offset;
+	struct vop_reg afbdc_axi_ctrl;
+
+	/* BCSH */
+	struct vop_reg bcsh_brightness;
+	struct vop_reg bcsh_contrast;
+	struct vop_reg bcsh_sat_con;
+	struct vop_reg bcsh_sin_hue;
+	struct vop_reg bcsh_cos_hue;
+	struct vop_reg bcsh_r2y_csc_mode;
+	struct vop_reg bcsh_r2y_en;
+	struct vop_reg bcsh_y2r_csc_mode;
+	struct vop_reg bcsh_y2r_en;
+	struct vop_reg bcsh_color_bar;
+	struct vop_reg bcsh_out_mode;
+	struct vop_reg bcsh_en;
+
+	/* HDR */
+	struct vop_reg level2_overlay_en;
+	struct vop_reg alpha_hard_calc;
+	struct vop_reg hdr2sdr_en;
+	struct vop_reg hdr2sdr_en_win0_csc;
+	struct vop_reg hdr2sdr_src_min;
+	struct vop_reg hdr2sdr_src_max;
+	struct vop_reg hdr2sdr_normfaceetf;
+	struct vop_reg hdr2sdr_dst_min;
+	struct vop_reg hdr2sdr_dst_max;
+	struct vop_reg hdr2sdr_normfacgamma;
+
+	struct vop_reg bt1886eotf_pre_conv_en;
+	struct vop_reg rgb2rgb_pre_conv_en;
+	struct vop_reg rgb2rgb_pre_conv_mode;
+	struct vop_reg st2084oetf_pre_conv_en;
+	struct vop_reg bt1886eotf_post_conv_en;
+	struct vop_reg rgb2rgb_post_conv_en;
+	struct vop_reg rgb2rgb_post_conv_mode;
+	struct vop_reg st2084oetf_post_conv_en;
+	struct vop_reg win_csc_mode_sel;
+
+	/* MCU OUTPUT */
+	struct vop_reg mcu_pix_total;
+	struct vop_reg mcu_cs_pst;
+	struct vop_reg mcu_cs_pend;
+	struct vop_reg mcu_rw_pst;
+	struct vop_reg mcu_rw_pend;
+	struct vop_reg mcu_clk_sel;
+	struct vop_reg mcu_hold_mode;
+	struct vop_reg mcu_frame_st;
+	struct vop_reg mcu_rs;
+	struct vop_reg mcu_bypass;
+	struct vop_reg mcu_type;
+	struct vop_reg mcu_rw_bypass_port;
+
+	/* bt1120 */
+	struct vop_reg bt1120_yc_swap;
+	struct vop_reg bt1120_en;
+
+	struct vop_reg reg_done_frm;
+	struct vop_reg cfg_done;
 };
 
 struct vop_intr {
 	const int *intrs;
 	uint32_t nintrs;
-
 	struct vop_reg line_flag_num[2];
 	struct vop_reg enable;
 	struct vop_reg clear;
@@ -152,19 +385,129 @@ struct vop_scl_regs {
 	struct vop_reg scale_cbcr_y;
 };
 
-struct vop_yuv2yuv_phy {
-	struct vop_reg y2r_coefficients[NUM_YUV2YUV_COEFFICIENTS];
+struct vop_afbc {
+	struct vop_reg enable;
+	struct vop_reg win_sel;
+	struct vop_reg format;
+	struct vop_reg rb_swap;
+	struct vop_reg uv_swap;
+	struct vop_reg auto_gating_en;
+	struct vop_reg rotate;
+	struct vop_reg block_split_en;
+	struct vop_reg pic_vir_width;
+	struct vop_reg tile_num;
+	struct vop_reg pic_offset;
+	struct vop_reg pic_size;
+	struct vop_reg dsp_offset;
+	struct vop_reg transform_offset;
+	struct vop_reg hdr_ptr;
+	struct vop_reg half_block_en;
+	struct vop_reg xmirror;
+	struct vop_reg ymirror;
+	struct vop_reg rotate_270;
+	struct vop_reg rotate_90;
+	struct vop_reg rstn;
+};
+
+struct vop_csc_table {
+	const uint32_t *y2r_bt601;
+	const uint32_t *y2r_bt601_12_235;
+	const uint32_t *y2r_bt601_10bit;
+	const uint32_t *y2r_bt601_10bit_12_235;
+	const uint32_t *r2y_bt601;
+	const uint32_t *r2y_bt601_12_235;
+	const uint32_t *r2y_bt601_10bit;
+	const uint32_t *r2y_bt601_10bit_12_235;
+
+	const uint32_t *y2r_bt709;
+	const uint32_t *y2r_bt709_10bit;
+	const uint32_t *r2y_bt709;
+	const uint32_t *r2y_bt709_10bit;
+
+	const uint32_t *y2r_bt2020;
+	const uint32_t *r2y_bt2020;
+
+	const uint32_t *r2r_bt709_to_bt2020;
+	const uint32_t *r2r_bt2020_to_bt709;
+};
+
+struct vop_hdr_table {
+	const uint32_t hdr2sdr_eetf_oetf_y0_offset;
+	const uint32_t hdr2sdr_eetf_oetf_y1_offset;
+	const uint32_t *hdr2sdr_eetf_yn;
+	const uint32_t *hdr2sdr_bt1886oetf_yn;
+	const uint32_t hdr2sdr_sat_y0_offset;
+	const uint32_t hdr2sdr_sat_y1_offset;
+	const uint32_t *hdr2sdr_sat_yn;
+
+	const uint32_t hdr2sdr_src_range_min;
+	const uint32_t hdr2sdr_src_range_max;
+	const uint32_t hdr2sdr_normfaceetf;
+	const uint32_t hdr2sdr_dst_range_min;
+	const uint32_t hdr2sdr_dst_range_max;
+	const uint32_t hdr2sdr_normfacgamma;
+
+	const uint32_t sdr2hdr_eotf_oetf_y0_offset;
+	const uint32_t sdr2hdr_eotf_oetf_y1_offset;
+	const uint32_t *sdr2hdr_bt1886eotf_yn_for_hlg_hdr;
+	const uint32_t *sdr2hdr_bt1886eotf_yn_for_bt2020;
+	const uint32_t *sdr2hdr_bt1886eotf_yn_for_hdr;
+	const uint32_t *sdr2hdr_st2084oetf_yn_for_hlg_hdr;
+	const uint32_t *sdr2hdr_st2084oetf_yn_for_bt2020;
+	const uint32_t *sdr2hdr_st2084oetf_yn_for_hdr;
+	const uint32_t sdr2hdr_oetf_dx_dxpow1_offset;
+	const uint32_t *sdr2hdr_st2084oetf_dxn_pow2;
+	const uint32_t *sdr2hdr_st2084oetf_dxn;
+	const uint32_t sdr2hdr_oetf_xn1_offset;
+	const uint32_t *sdr2hdr_st2084oetf_xn;
+};
+
+enum {
+	VOP_CSC_Y2R_BT601,
+	VOP_CSC_Y2R_BT709,
+	VOP_CSC_Y2R_BT2020,
+	VOP_CSC_R2Y_BT601,
+	VOP_CSC_R2Y_BT709,
+	VOP_CSC_R2Y_BT2020,
+	VOP_CSC_R2R_BT2020_TO_BT709,
+	VOP_CSC_R2R_BT709_TO_2020,
+};
+
+enum _vop_overlay_mode {
+	VOP_RGB_DOMAIN,
+	VOP_YUV_DOMAIN
+};
+
+enum _vop_sdr2hdr_func {
+	SDR2HDR_FOR_BT2020,
+	SDR2HDR_FOR_HDR,
+	SDR2HDR_FOR_HLG_HDR,
+};
+
+enum _vop_rgb2rgb_conv_mode {
+	BT709_TO_BT2020,
+	BT2020_TO_BT709,
+};
+
+enum _MCU_IOCTL {
+	MCU_WRCMD = 0,
+	MCU_WRDATA,
+	MCU_SETBYPASS,
 };
 
 struct vop_win_phy {
 	const struct vop_scl_regs *scl;
 	const uint32_t *data_formats;
 	uint32_t nformats;
-	const uint64_t *format_modifiers;
 
-	struct vop_reg enable;
 	struct vop_reg gate;
+	struct vop_reg enable;
 	struct vop_reg format;
+	struct vop_reg fmt_10;
+	struct vop_reg fmt_yuyv;
+	struct vop_reg csc_mode;
+	struct vop_reg xmirror;
+	struct vop_reg ymirror;
 	struct vop_reg rb_swap;
 	struct vop_reg act_info;
 	struct vop_reg dsp_info;
@@ -173,56 +516,609 @@ struct vop_win_phy {
 	struct vop_reg uv_mst;
 	struct vop_reg yrgb_vir;
 	struct vop_reg uv_vir;
-	struct vop_reg y_mir_en;
-	struct vop_reg x_mir_en;
 
+	struct vop_reg channel;
 	struct vop_reg dst_alpha_ctl;
 	struct vop_reg src_alpha_ctl;
-	struct vop_reg alpha_pre_mul;
 	struct vop_reg alpha_mode;
 	struct vop_reg alpha_en;
-	struct vop_reg channel;
+	struct vop_reg alpha_pre_mul;
+	struct vop_reg global_alpha_val;
+	struct vop_reg key_color;
+	struct vop_reg key_en;
 };
 
-struct vop_win_yuv2yuv_data {
+struct vop_win_data {
 	uint32_t base;
-	const struct vop_yuv2yuv_phy *phy;
+	enum drm_plane_type type;
+	const struct vop_win_phy *phy;
+	const struct vop_win_phy **area;
+	const uint64_t *format_modifiers;
+	const struct vop_csc *csc;
+	unsigned int area_size;
+	u64 feature;
+};
+
+struct vop2_cluster_regs {
+	struct vop_reg enable;
+	struct vop_reg afbc_enable;
+	struct vop_reg lb_mode;
+};
+
+struct vop2_scl_regs {
+	struct vop_reg scale_yrgb_x;
+	struct vop_reg scale_yrgb_y;
+	struct vop_reg scale_cbcr_x;
+	struct vop_reg scale_cbcr_y;
+	struct vop_reg yrgb_hor_scl_mode;
+	struct vop_reg yrgb_hscl_filter_mode;
+	struct vop_reg yrgb_ver_scl_mode;
+	struct vop_reg yrgb_vscl_filter_mode;
+	struct vop_reg cbcr_ver_scl_mode;
+	struct vop_reg cbcr_hscl_filter_mode;
+	struct vop_reg cbcr_hor_scl_mode;
+	struct vop_reg cbcr_vscl_filter_mode;
+	struct vop_reg vsd_cbcr_gt2;
+	struct vop_reg vsd_cbcr_gt4;
+	struct vop_reg vsd_yrgb_gt2;
+	struct vop_reg vsd_yrgb_gt4;
+	struct vop_reg bic_coe_sel;
+};
+
+struct vop2_win_regs {
+	const struct vop2_scl_regs *scl;
+	const struct vop2_cluster_regs *cluster;
+	const struct vop_afbc *afbc;
+
+	struct vop_reg gate;
+	struct vop_reg enable;
+	struct vop_reg format;
+	struct vop_reg csc_mode;
+	struct vop_reg xmirror;
+	struct vop_reg ymirror;
+	struct vop_reg rb_swap;
+	struct vop_reg uv_swap;
+	struct vop_reg act_info;
+	struct vop_reg dsp_info;
+	struct vop_reg dsp_st;
+	struct vop_reg yrgb_mst;
+	struct vop_reg uv_mst;
+	struct vop_reg yrgb_vir;
+	struct vop_reg uv_vir;
+	struct vop_reg yuv_clip;
+	struct vop_reg lb_mode;
 	struct vop_reg y2r_en;
+	struct vop_reg r2y_en;
+	struct vop_reg channel;
+	struct vop_reg dst_alpha_ctl;
+	struct vop_reg src_alpha_ctl;
+	struct vop_reg alpha_mode;
+	struct vop_reg alpha_en;
+	struct vop_reg global_alpha_val;
+	struct vop_reg color_key;
+	struct vop_reg color_key_en;
+	struct vop_reg dither_up;
+	struct vop_reg axi_id;
+	struct vop_reg axi_yrgb_id;
+	struct vop_reg axi_uv_id;
 };
 
-struct vop_win_data {
+struct vop2_video_port_regs {
+	struct vop_reg cfg_done;
+	struct vop_reg overlay_mode;
+	struct vop_reg dsp_background;
+	struct vop_reg port_mux;
+	struct vop_reg out_mode;
+	struct vop_reg standby;
+	struct vop_reg dsp_interlace;
+	struct vop_reg dsp_filed_pol;
+	struct vop_reg dsp_data_swap;
+	struct vop_reg post_dsp_out_r2y;
+	struct vop_reg pre_scan_htiming;
+	struct vop_reg htotal_pw;
+	struct vop_reg hact_st_end;
+	struct vop_reg vtotal_pw;
+	struct vop_reg vact_st_end;
+	struct vop_reg vact_st_end_f1;
+	struct vop_reg vs_st_end_f1;
+	struct vop_reg hpost_st_end;
+	struct vop_reg vpost_st_end;
+	struct vop_reg vpost_st_end_f1;
+	struct vop_reg post_scl_factor;
+	struct vop_reg post_scl_ctrl;
+	struct vop_reg dither_down_sel;
+	struct vop_reg dither_down_mode;
+	struct vop_reg dither_down_en;
+	struct vop_reg pre_dither_down_en;
+	struct vop_reg dither_up_en;
+	struct vop_reg bg_dly;
+
+	struct vop_reg core_dclk_div;
+	struct vop_reg p2i_en;
+	struct vop_reg dual_channel_en;
+	struct vop_reg dual_channel_swap;
+	struct vop_reg dsp_lut_en;
+
+	struct vop_reg dclk_div2;
+	struct vop_reg dclk_div2_phase_lock;
+
+	struct vop_reg hdr10_en;
+	struct vop_reg hdr_lut_update_en;
+	struct vop_reg hdr_lut_mode;
+	struct vop_reg hdr_lut_mst;
+	struct vop_reg sdr2hdr_eotf_en;
+	struct vop_reg sdr2hdr_r2r_en;
+	struct vop_reg sdr2hdr_r2r_mode;
+	struct vop_reg sdr2hdr_oetf_en;
+	struct vop_reg sdr2hdr_bypass_en;
+	struct vop_reg sdr2hdr_auto_gating_en;
+	struct vop_reg sdr2hdr_path_en;
+	struct vop_reg hdr2sdr_en;
+	struct vop_reg hdr2sdr_bypass_en;
+	struct vop_reg hdr2sdr_auto_gating_en;
+	struct vop_reg hdr2sdr_src_min;
+	struct vop_reg hdr2sdr_src_max;
+	struct vop_reg hdr2sdr_normfaceetf;
+	struct vop_reg hdr2sdr_dst_min;
+	struct vop_reg hdr2sdr_dst_max;
+	struct vop_reg hdr2sdr_normfacgamma;
+	uint32_t hdr2sdr_eetf_oetf_y0_offset;
+	uint32_t hdr2sdr_sat_y0_offset;
+	uint32_t sdr2hdr_eotf_oetf_y0_offset;
+	uint32_t sdr2hdr_oetf_dx_pow1_offset;
+	uint32_t sdr2hdr_oetf_xn1_offset;
+	struct vop_reg hdr_src_color_ctrl;
+	struct vop_reg hdr_dst_color_ctrl;
+	struct vop_reg hdr_src_alpha_ctrl;
+	struct vop_reg hdr_dst_alpha_ctrl;
+	struct vop_reg bg_mix_ctrl;
+
+	/* BCSH */
+	struct vop_reg bcsh_brightness;
+	struct vop_reg bcsh_contrast;
+	struct vop_reg bcsh_sat_con;
+	struct vop_reg bcsh_sin_hue;
+	struct vop_reg bcsh_cos_hue;
+	struct vop_reg bcsh_r2y_csc_mode;
+	struct vop_reg bcsh_r2y_en;
+	struct vop_reg bcsh_y2r_csc_mode;
+	struct vop_reg bcsh_y2r_en;
+	struct vop_reg bcsh_out_mode;
+	struct vop_reg bcsh_en;
+
+	/* 3d lut */
+	struct vop_reg cubic_lut_en;
+	struct vop_reg cubic_lut_update_en;
+	struct vop_reg cubic_lut_mst;
+
+	/* cru */
+	struct vop_reg dclk_core_div;
+	struct vop_reg dclk_out_div;
+	struct vop_reg dclk_src_sel;
+
+	struct vop_reg splice_en;
+
+	struct vop_reg edpi_wms_hold_en;
+	struct vop_reg edpi_te_en;
+	struct vop_reg edpi_wms_fs;
+	struct vop_reg gamma_update_en;
+	struct vop_reg lut_dma_rid;
+};
+
+struct vop2_power_domain_regs {
+	struct vop_reg pd;
+	struct vop_reg status;
+	struct vop_reg bisr_en_status;
+	struct vop_reg pmu_status;
+};
+
+struct vop2_dsc_regs {
+	/* DSC SYS CTRL */
+	struct vop_reg dsc_port_sel;
+	struct vop_reg dsc_man_mode;
+	struct vop_reg dsc_interface_mode;
+	struct vop_reg dsc_pixel_num;
+	struct vop_reg dsc_pxl_clk_div;
+	struct vop_reg dsc_cds_clk_div;
+	struct vop_reg dsc_txp_clk_div;
+	struct vop_reg dsc_init_dly_mode;
+	struct vop_reg dsc_scan_en;
+	struct vop_reg dsc_halt_en;
+	struct vop_reg rst_deassert;
+	struct vop_reg dsc_flush;
+	struct vop_reg dsc_cfg_done;
+	struct vop_reg dsc_init_dly_num;
+	struct vop_reg scan_timing_para_imd_en;
+	struct vop_reg dsc_htotal_pw;
+	struct vop_reg dsc_hact_st_end;
+	struct vop_reg dsc_vtotal_pw;
+	struct vop_reg dsc_vact_st_end;
+	struct vop_reg dsc_error_status;
+
+	/* DSC encoder */
+	struct vop_reg dsc_pps0_3;
+	struct vop_reg dsc_en;
+	struct vop_reg dsc_rbit;
+	struct vop_reg dsc_rbyt;
+	struct vop_reg dsc_flal;
+	struct vop_reg dsc_mer;
+	struct vop_reg dsc_epb;
+	struct vop_reg dsc_epl;
+	struct vop_reg dsc_nslc;
+	struct vop_reg dsc_sbo;
+	struct vop_reg dsc_ifep;
+	struct vop_reg dsc_pps_upd;
+	struct vop_reg dsc_status;
+	struct vop_reg dsc_ecw;
+};
+
+struct vop2_wb_regs {
+	struct vop_reg enable;
+	struct vop_reg format;
+	struct vop_reg dither_en;
+	struct vop_reg r2y_en;
+	struct vop_reg yrgb_mst;
+	struct vop_reg uv_mst;
+	struct vop_reg vp_id;
+	struct vop_reg fifo_throd;
+	struct vop_reg scale_x_factor;
+	struct vop_reg scale_x_en;
+	struct vop_reg scale_y_en;
+	struct vop_reg axi_yrgb_id;
+	struct vop_reg axi_uv_id;
+};
+
+struct vop2_power_domain_data {
+	uint8_t id;
+	uint8_t parent_id;
+	const struct vop2_power_domain_regs *regs;
+};
+
+/*
+ * connector interface(RGB/HDMI/eDP/DP/MIPI) data
+ */
+struct vop2_connector_if_data {
+	u32 id;
+	const char *clk_src_name;
+	const char *clk_parent_name;
+	const char *pixclk_name;
+	const char *dclk_name;
+	u32 post_proc_div_shift;
+	u32 if_div_shift;
+	u32 if_div_yuv420_shift;
+	u32 bus_div_shift;
+	u32 pixel_clk_div_shift;
+};
+
+struct vop2_win_data {
+	const char *name;
+	uint8_t phys_id;
+	uint8_t splice_win_id;
+	uint8_t pd_id;
+	uint8_t axi_id;
+	uint8_t axi_yrgb_id;
+	uint8_t axi_uv_id;
+
 	uint32_t base;
-	const struct vop_win_phy *phy;
 	enum drm_plane_type type;
+
+	uint32_t nformats;
+	const uint32_t *formats;
+	const uint64_t *format_modifiers;
+	const unsigned int supported_rotations;
+
+	const struct vop2_win_regs *regs;
+	const struct vop2_win_regs **area;
+	unsigned int area_size;
+
+	/*
+	 * vertical/horizontal scale up/down filter mode
+	 */
+	const u8 hsu_filter_mode;
+	const u8 hsd_filter_mode;
+	const u8 vsu_filter_mode;
+	const u8 vsd_filter_mode;
+	/**
+	 * @layer_sel_id: defined by register OVERLAY_LAYER_SEL of VOP2
+	 */
+	int layer_sel_id;
+	uint64_t feature;
+
+	unsigned int max_upscale_factor;
+	unsigned int max_downscale_factor;
+	const uint8_t dly[VOP2_DLY_MODE_MAX];
+};
+
+struct dsc_error_info {
+	u32 dsc_error_val;
+	char dsc_error_info[50];
+};
+
+struct vop2_dsc_data {
+	uint8_t id;
+	uint8_t pd_id;
+	uint8_t max_slice_num;
+	uint8_t max_linebuf_depth;	/* used to generate the bitstream */
+	uint8_t min_bits_per_pixel;	/* bit num after encoder compress */
+	const char *dsc_txp_clk_src_name;
+	const char *dsc_txp_clk_name;
+	const char *dsc_pxl_clk_name;
+	const char *dsc_cds_clk_name;
+	const struct vop2_dsc_regs *regs;
+};
+
+struct vop2_wb_data {
+	uint32_t nformats;
+	const uint32_t *formats;
+	struct vop_rect max_output;
+	const struct vop2_wb_regs *regs;
+	uint32_t fifo_depth;
+};
+
+struct vop2_video_port_data {
+	char id;
+	uint8_t splice_vp_id;
+	uint16_t lut_dma_rid;
+	uint32_t feature;
+	uint64_t soc_id[VOP2_SOC_VARIANT];
+	uint16_t gamma_lut_len;
+	uint16_t cubic_lut_len;
+	unsigned long dclk_max;
+	struct vop_rect max_output;
+	const u8 pre_scan_max_dly[4];
+	const struct vop_intr *intr;
+	const struct vop_hdr_table *hdr_table;
+	const struct vop2_video_port_regs *regs;
+};
+
+struct vop2_layer_regs {
+	struct vop_reg layer_sel;
+};
+
+/**
+ * struct vop2_layer_data - The logic graphic layer in vop2
+ *
+ * The zorder:
+ *   LAYERn
+ *   LAYERn-1
+ *     .
+ *     .
+ *     .
+ *   LAYER5
+ *   LAYER4
+ *   LAYER3
+ *   LAYER2
+ *   LAYER1
+ *   LAYER0
+ *
+ * Each layer can select a unused window as input than feed to
+ * mixer for overlay.
+ *
+ * The pipeline in vop2:
+ *
+ * win-->layer-->mixer-->vp--->connector(RGB/LVDS/HDMI/MIPI)
+ *
+ */
+struct vop2_layer_data {
+	char id;
+	const struct vop2_layer_regs *regs;
+};
+
+struct vop_grf_ctrl {
+	struct vop_reg grf_dclk_inv;
+	struct vop_reg grf_bt1120_clk_inv;
+	struct vop_reg grf_bt656_clk_inv;
+	struct vop_reg grf_edp0_en;
+	struct vop_reg grf_edp1_en;
+	struct vop_reg grf_hdmi0_en;
+	struct vop_reg grf_hdmi1_en;
+	struct vop_reg grf_hdmi0_dsc_en;
+	struct vop_reg grf_hdmi1_dsc_en;
+	struct vop_reg grf_hdmi0_pin_pol;
+	struct vop_reg grf_hdmi1_pin_pol;
 };
 
 struct vop_data {
-	uint32_t version;
+	const struct vop_reg_data *init_table;
+	unsigned int table_size;
+	const struct vop_ctrl *ctrl;
 	const struct vop_intr *intr;
-	const struct vop_common *common;
-	const struct vop_misc *misc;
-	const struct vop_modeset *modeset;
-	const struct vop_output *output;
-	const struct vop_afbc *afbc;
-	const struct vop_win_yuv2yuv_data *win_yuv2yuv;
 	const struct vop_win_data *win;
+	const struct vop_csc_table *csc_table;
+	const struct vop_hdr_table *hdr_table;
+	const struct vop_grf_ctrl *grf_ctrl;
 	unsigned int win_size;
-	unsigned int lut_size;
-
-#define VOP_FEATURE_OUTPUT_RGB10	BIT(0)
-#define VOP_FEATURE_INTERNAL_RGB	BIT(1)
+	uint32_t version;
+	struct vop_rect max_input;
+	struct vop_rect max_output;
 	u64 feature;
+	u64 soc_id;
+	u8 vop_id;
 };
 
+struct vop2_ctrl {
+	struct vop_reg cfg_done_en;
+	struct vop_reg wb_cfg_done;
+	struct vop_reg auto_gating_en;
+	struct vop_reg ovl_cfg_done_port;
+	struct vop_reg ovl_port_mux_cfg_done_imd;
+	struct vop_reg ovl_port_mux_cfg;
+	struct vop_reg if_ctrl_cfg_done_imd;
+	struct vop_reg version;
+	struct vop_reg standby;
+	struct vop_reg dma_stop;
+	struct vop_reg lut_dma_en;
+	struct vop_reg axi_outstanding_max_num;
+	struct vop_reg axi_max_outstanding_en;
+	struct vop_reg hdmi_dclk_out_en;
+	struct vop_reg rgb_en;
+	struct vop_reg hdmi0_en;
+	struct vop_reg hdmi1_en;
+	struct vop_reg dp0_en;
+	struct vop_reg dp1_en;
+	struct vop_reg edp0_en;
+	struct vop_reg edp1_en;
+	struct vop_reg mipi0_en;
+	struct vop_reg mipi1_en;
+	struct vop_reg lvds0_en;
+	struct vop_reg lvds1_en;
+	struct vop_reg bt656_en;
+	struct vop_reg bt1120_en;
+	struct vop_reg dclk_pol;
+	struct vop_reg pin_pol;
+	struct vop_reg rgb_dclk_pol;
+	struct vop_reg rgb_pin_pol;
+	struct vop_reg lvds_dclk_pol;
+	struct vop_reg lvds_pin_pol;
+	struct vop_reg hdmi_dclk_pol;
+	struct vop_reg hdmi_pin_pol;
+	struct vop_reg edp_dclk_pol;
+	struct vop_reg edp_pin_pol;
+	struct vop_reg mipi_dclk_pol;
+	struct vop_reg mipi_pin_pol;
+	struct vop_reg dp0_dclk_pol;
+	struct vop_reg dp0_pin_pol;
+	struct vop_reg dp1_dclk_pol;
+	struct vop_reg dp1_pin_pol;
+
+	/* This will be reference by win_phy_id */
+	struct vop_reg win_vp_id[16];
+	struct vop_reg win_dly[16];
+
+	/* connector mux */
+	struct vop_reg rgb_mux;
+	struct vop_reg hdmi0_mux;
+	struct vop_reg hdmi1_mux;
+	struct vop_reg dp0_mux;
+	struct vop_reg dp1_mux;
+	struct vop_reg edp0_mux;
+	struct vop_reg edp1_mux;
+	struct vop_reg mipi0_mux;
+	struct vop_reg mipi1_mux;
+	struct vop_reg lvds0_mux;
+	struct vop_reg lvds1_mux;
+
+	struct vop_reg lvds_dual_en;
+	struct vop_reg lvds_dual_mode;
+	struct vop_reg lvds_dual_channel_swap;
+
+	struct vop_reg dp_dual_en;
+	struct vop_reg edp_dual_en;
+	struct vop_reg hdmi_dual_en;
+	struct vop_reg mipi_dual_en;
+
+	struct vop_reg hdmi0_dclk_div;
+	struct vop_reg hdmi0_pixclk_div;
+	struct vop_reg edp0_dclk_div;
+	struct vop_reg edp0_pixclk_div;
+
+	struct vop_reg hdmi1_dclk_div;
+	struct vop_reg hdmi1_pixclk_div;
+	struct vop_reg edp1_dclk_div;
+	struct vop_reg edp1_pixclk_div;
+
+	struct vop_reg mipi0_pixclk_div;
+	struct vop_reg mipi1_pixclk_div;
+	struct vop_reg mipi0_ds_mode;
+	struct vop_reg mipi1_ds_mode;
+
+	struct vop_reg cluster0_src_color_ctrl;
+	struct vop_reg cluster0_dst_color_ctrl;
+	struct vop_reg cluster0_src_alpha_ctrl;
+	struct vop_reg cluster0_dst_alpha_ctrl;
+	struct vop_reg src_color_ctrl;
+	struct vop_reg dst_color_ctrl;
+	struct vop_reg src_alpha_ctrl;
+	struct vop_reg dst_alpha_ctrl;
+
+	struct vop_reg bt1120_yc_swap;
+	struct vop_reg bt656_yc_swap;
+	struct vop_reg gamma_port_sel;
+	struct vop_reg pd_off_imd;
+
+	struct vop_reg otp_en;
+	struct vop_reg reg_done_frm;
+	struct vop_reg cfg_done;
+};
+
+/**
+ * VOP2 data structe
+ *
+ * @version: VOP IP version
+ * @win_size: hardware win number
+ */
+struct vop2_data {
+	uint32_t version;
+	uint32_t feature;
+	uint8_t nr_dscs;
+	uint8_t nr_dsc_ecw;
+	uint8_t nr_dsc_buffer_flow;
+	uint8_t nr_vps;
+	uint8_t nr_mixers;
+	uint8_t nr_layers;
+	uint8_t nr_axi_intr;
+	uint8_t nr_gammas;
+	uint8_t nr_conns;
+	uint8_t nr_pds;
+	uint8_t nr_mem_pgs;
+	bool delayed_pd;
+	const struct vop_intr *axi_intr;
+	const struct vop2_ctrl *ctrl;
+	const struct vop2_dsc_data *dsc;
+	const struct dsc_error_info *dsc_error_ecw;
+	const struct dsc_error_info *dsc_error_buffer_flow;
+	const struct vop2_win_data *win;
+	const struct vop2_video_port_data *vp;
+	const struct vop2_connector_if_data *conn;
+	const struct vop2_wb_data *wb;
+	const struct vop2_layer_data *layer;
+	const struct vop2_power_domain_data *pd;
+	const struct vop2_power_domain_data *mem_pg;
+	const struct vop_csc_table *csc_table;
+	const struct vop_hdr_table *hdr_table;
+	const struct vop_grf_ctrl *sys_grf;
+	const struct vop_grf_ctrl *grf;
+	const struct vop_grf_ctrl *vo0_grf;
+	const struct vop_grf_ctrl *vo1_grf;
+	struct vop_rect max_input;
+	struct vop_rect max_output;
+
+	unsigned int win_size;
+};
+
+#define CVBS_PAL_VDISPLAY		288
+
 /* interrupt define */
-#define DSP_HOLD_VALID_INTR		(1 << 0)
-#define FS_INTR				(1 << 1)
-#define LINE_FLAG_INTR			(1 << 2)
-#define BUS_ERROR_INTR			(1 << 3)
+#define DSP_HOLD_VALID_INTR		BIT(0)
+#define FS_INTR				BIT(1)
+#define LINE_FLAG_INTR			BIT(2)
+#define BUS_ERROR_INTR			BIT(3)
+#define FS_NEW_INTR			BIT(4)
+#define ADDR_SAME_INTR			BIT(5)
+#define LINE_FLAG1_INTR			BIT(6)
+#define WIN0_EMPTY_INTR			BIT(7)
+#define WIN1_EMPTY_INTR			BIT(8)
+#define WIN2_EMPTY_INTR			BIT(9)
+#define WIN3_EMPTY_INTR			BIT(10)
+#define HWC_EMPTY_INTR			BIT(11)
+#define POST_BUF_EMPTY_INTR		BIT(12)
+#define PWM_GEN_INTR			BIT(13)
+#define DMA_FINISH_INTR			BIT(14)
+#define FS_FIELD_INTR			BIT(15)
+#define FE_INTR				BIT(16)
+#define WB_UV_FIFO_FULL_INTR		BIT(17)
+#define WB_YRGB_FIFO_FULL_INTR		BIT(18)
+#define WB_COMPLETE_INTR		BIT(19)
 
 #define INTR_MASK			(DSP_HOLD_VALID_INTR | FS_INTR | \
-					 LINE_FLAG_INTR | BUS_ERROR_INTR)
-
+					 LINE_FLAG_INTR | BUS_ERROR_INTR | \
+					 FS_NEW_INTR | LINE_FLAG1_INTR | \
+					 WIN0_EMPTY_INTR | WIN1_EMPTY_INTR | \
+					 WIN2_EMPTY_INTR | WIN3_EMPTY_INTR | \
+					 HWC_EMPTY_INTR | \
+					 POST_BUF_EMPTY_INTR | \
+					 DMA_FINISH_INTR | FS_FIELD_INTR | \
+					 FE_INTR)
 #define DSP_HOLD_VALID_INTR_EN(x)	((x) << 4)
 #define FS_INTR_EN(x)			((x) << 5)
 #define LINE_FLAG_INTR_EN(x)		((x) << 6)
@@ -256,14 +1152,19 @@ struct vop_data {
 /*
  * display output interface supported by rockchip lcdc
  */
-#define ROCKCHIP_OUT_MODE_P888	0
-#define ROCKCHIP_OUT_MODE_P666	1
-#define ROCKCHIP_OUT_MODE_P565	2
+#define ROCKCHIP_OUT_MODE_P888		0
+#define ROCKCHIP_OUT_MODE_BT1120	0
+#define ROCKCHIP_OUT_MODE_P666		1
+#define ROCKCHIP_OUT_MODE_P565		2
+#define ROCKCHIP_OUT_MODE_BT656		5
+#define ROCKCHIP_OUT_MODE_S888		8
+#define ROCKCHIP_OUT_MODE_S888_DUMMY	12
+#define ROCKCHIP_OUT_MODE_YUV420	14
 /* for use special outface */
-#define ROCKCHIP_OUT_MODE_AAAA	15
+#define ROCKCHIP_OUT_MODE_AAAA		15
 
-/* output flags */
-#define ROCKCHIP_OUTPUT_DSI_DUAL	BIT(0)
+#define ROCKCHIP_OUT_MODE_TYPE(x)	((x) >> 16)
+#define ROCKCHIP_OUT_MODE(x)		((x) & 0xffff)
 
 enum alpha_mode {
 	ALPHA_STRAIGHT,
@@ -292,6 +1193,25 @@ enum factor_mode {
 	ALPHA_SRC,
 	ALPHA_SRC_INVERSE,
 	ALPHA_SRC_GLOBAL,
+	ALPHA_DST_GLOBAL,
+};
+
+enum src_factor_mode {
+	SRC_FAC_ALPHA_ZERO,
+	SRC_FAC_ALPHA_ONE,
+	SRC_FAC_ALPHA_DST,
+	SRC_FAC_ALPHA_DST_INVERSE,
+	SRC_FAC_ALPHA_SRC,
+	SRC_FAC_ALPHA_SRC_GLOBAL,
+};
+
+enum dst_factor_mode {
+	DST_FAC_ALPHA_ZERO,
+	DST_FAC_ALPHA_ONE,
+	DST_FAC_ALPHA_SRC,
+	DST_FAC_ALPHA_SRC_INVERSE,
+	DST_FAC_ALPHA_DST,
+	DST_FAC_ALPHA_DST_GLOBAL,
 };
 
 enum scale_mode {
@@ -319,6 +1239,18 @@ enum scale_down_mode {
 	SCALE_DOWN_AVG = 0x1
 };
 
+enum vop2_scale_up_mode {
+	VOP2_SCALE_UP_NRST_NBOR,
+	VOP2_SCALE_UP_BIL,
+	VOP2_SCALE_UP_BIC,
+};
+
+enum vop2_scale_down_mode {
+	VOP2_SCALE_DOWN_NRST_NBOR,
+	VOP2_SCALE_DOWN_BIL,
+	VOP2_SCALE_DOWN_AVG,
+};
+
 enum dither_down_mode {
 	RGB888_TO_RGB565 = 0x0,
 	RGB888_TO_RGB666 = 0x1
@@ -332,9 +1264,11 @@ enum dither_down_mode_sel {
 enum vop_pol {
 	HSYNC_POSITIVE = 0,
 	VSYNC_POSITIVE = 1,
-	DEN_NEGATIVE   = 2
+	DEN_NEGATIVE   = 2,
+	DCLK_INVERT    = 3
 };
 
+
 #define FRAC_16_16(mult, div)    (((mult) << 16) / (div))
 #define SCL_FT_DEFAULT_FIXPOINT_SHIFT	12
 #define SCL_MAX_VSKIPLINES		4
@@ -359,7 +1293,7 @@ static inline uint16_t scl_get_bili_dn_vskip(int src_h, int dst_h,
 {
 	int act_height;
 
-	act_height = DIV_ROUND_UP(src_h, vskiplines);
+	act_height = (src_h + vskiplines - 1) / vskiplines;
 
 	if (act_height == dst_h)
 		return GET_SCL_FT_BILI_DN(src_h, dst_h) / vskiplines;
@@ -409,5 +1343,17 @@ static inline int scl_vop_cal_lb_mode(int width, bool is_yuv)
 	return lb_mode;
 }
 
+static inline int us_to_vertical_line(struct drm_display_mode *mode, int us)
+{
+	return us * mode->clock / mode->htotal / 1000;
+}
+
+static inline int interpolate(int x1, int y1, int x2, int y2, int x)
+{
+	return y1 + (y2 - y1) * (x - x1) / (x2 - x1);
+}
+
+extern void vop2_standby(struct drm_crtc *crtc, bool standby);
 extern const struct component_ops vop_component_ops;
+extern const struct component_ops vop2_component_ops;
 #endif /* _ROCKCHIP_DRM_VOP_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
index e2487937c..ca2838db8 100644
--- a/drivers/gpu/drm/rockchip/rockchip_lvds.c
+++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -6,98 +6,141 @@
  *      Sandy Huang <hjc@rock-chips.com>
  */
 
-#include <linux/clk.h>
 #include <linux/component.h>
 #include <linux/mfd/syscon.h>
 #include <linux/of_graph.h>
 #include <linux/phy/phy.h>
-#include <linux/pinctrl/devinfo.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
+#include <linux/of_platform.h>
 #include <linux/regmap.h>
-#include <linux/reset.h>
-
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
-#include <drm/drm_dp_helper.h>
 #include <drm/drm_of.h>
 #include <drm/drm_panel.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_simple_kms_helper.h>
 
+#include <uapi/linux/videodev2.h>
+
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_vop.h"
-#include "rockchip_lvds.h"
 
-#define DISPLAY_OUTPUT_RGB		0
-#define DISPLAY_OUTPUT_LVDS		1
-#define DISPLAY_OUTPUT_DUAL_LVDS	2
+#define HIWORD_UPDATE(v, h, l)  (((v) << (l)) | (GENMASK(h, l) << 16))
+
+#define PX30_GRF_PD_VO_CON1		0x0438
+#define PX30_LVDS_SELECT(x)		HIWORD_UPDATE(x, 14, 13)
+#define PX30_LVDS_MODE_EN(x)		HIWORD_UPDATE(x, 12, 12)
+#define PX30_LVDS_MSBSEL(x)		HIWORD_UPDATE(x, 11, 11)
+#define PX30_LVDS_P2S_EN(x)		HIWORD_UPDATE(x,  6,  6)
+#define PX30_LVDS_VOP_SEL(x)		HIWORD_UPDATE(x,  1,  1)
+
+#define RK3126_GRF_LVDS_CON0		0x0150
+#define RK3126_LVDS_P2S_EN(x)		HIWORD_UPDATE(x,  9,  9)
+#define RK3126_LVDS_MODE_EN(x)		HIWORD_UPDATE(x,  6,  6)
+#define RK3126_LVDS_MSBSEL(x)		HIWORD_UPDATE(x,  3,  3)
+#define RK3126_LVDS_SELECT(x)		HIWORD_UPDATE(x,  2,  1)
+
+#define RK3288_GRF_SOC_CON6		0x025c
+#define RK3288_LVDS_LCDC_SEL(x)		HIWORD_UPDATE(x,  3,  3)
+#define RK3288_GRF_SOC_CON7		0x0260
+#define RK3288_LVDS_PWRDWN(x)		HIWORD_UPDATE(x, 15, 15)
+#define RK3288_LVDS_CON_ENABLE_2(x)	HIWORD_UPDATE(x, 12, 12)
+#define RK3288_LVDS_CON_ENABLE_1(x)	HIWORD_UPDATE(x, 11, 11)
+#define RK3288_LVDS_CON_DEN_POL(x)	HIWORD_UPDATE(x, 10, 10)
+#define RK3288_LVDS_CON_HS_POL(x)	HIWORD_UPDATE(x,  9,  9)
+#define RK3288_LVDS_CON_CLKINV(x)	HIWORD_UPDATE(x,  8,  8)
+#define RK3288_LVDS_CON_STARTPHASE(x)	HIWORD_UPDATE(x,  7,  7)
+#define RK3288_LVDS_CON_TTL_EN(x)	HIWORD_UPDATE(x,  6,  6)
+#define RK3288_LVDS_CON_STARTSEL(x)	HIWORD_UPDATE(x,  5,  5)
+#define RK3288_LVDS_CON_CHASEL(x)	HIWORD_UPDATE(x,  4,  4)
+#define RK3288_LVDS_CON_MSBSEL(x)	HIWORD_UPDATE(x,  3,  3)
+#define RK3288_LVDS_CON_SELECT(x)	HIWORD_UPDATE(x,  2,  0)
+
+#define RK3368_GRF_SOC_CON7		0x041c
+#define RK3368_LVDS_SELECT(x)		HIWORD_UPDATE(x, 14, 13)
+#define RK3368_LVDS_MODE_EN(x)		HIWORD_UPDATE(x, 12, 12)
+#define RK3368_LVDS_MSBSEL(x)		HIWORD_UPDATE(x, 11, 11)
+#define RK3368_LVDS_P2S_EN(x)		HIWORD_UPDATE(x,  6,  6)
+
+#define RK3568_GRF_VO_CON0		0x0360
+#define RK3568_LVDS1_SELECT(x)		HIWORD_UPDATE(x, 13, 12)
+#define RK3568_LVDS1_MSBSEL(x)		HIWORD_UPDATE(x, 11, 11)
+#define RK3568_LVDS0_SELECT(x)		HIWORD_UPDATE(x,  5,  4)
+#define RK3568_LVDS0_MSBSEL(x)		HIWORD_UPDATE(x,  3,  3)
+#define RK3568_GRF_VO_CON2		0x0368
+#define RK3568_LVDS0_DCLK_INV_SEL(x)	HIWORD_UPDATE(x,  9,  9)
+#define RK3568_LVDS0_DCLK_DIV2_SEL(x)	HIWORD_UPDATE(x,  8,  8)
+#define RK3568_LVDS0_MODE_EN(x)		HIWORD_UPDATE(x,  1,  1)
+#define RK3568_LVDS0_P2S_EN(x)		HIWORD_UPDATE(x,  0,  0)
+#define RK3568_GRF_VO_CON3		0x036c
+#define RK3568_LVDS1_DCLK_INV_SEL(x)	HIWORD_UPDATE(x,  9,  9)
+#define RK3568_LVDS1_DCLK_DIV2_SEL(x)	HIWORD_UPDATE(x,  8,  8)
+#define RK3568_LVDS1_MODE_EN(x)		HIWORD_UPDATE(x,  1,  1)
+#define RK3568_LVDS1_P2S_EN(x)		HIWORD_UPDATE(x,  0,  0)
+
+enum lvds_format {
+	LVDS_8BIT_MODE_FORMAT_1,
+	LVDS_8BIT_MODE_FORMAT_2,
+	LVDS_8BIT_MODE_FORMAT_3,
+	LVDS_6BIT_MODE,
+	LVDS_10BIT_MODE_FORMAT_1,
+	LVDS_10BIT_MODE_FORMAT_2,
+};
 
 struct rockchip_lvds;
 
-#define connector_to_lvds(c) \
-		container_of(c, struct rockchip_lvds, connector)
-
-#define encoder_to_lvds(c) \
-		container_of(c, struct rockchip_lvds, encoder)
-
-/**
- * rockchip_lvds_soc_data - rockchip lvds Soc private data
- * @probe: LVDS platform probe function
- * @helper_funcs: LVDS connector helper functions
- */
-struct rockchip_lvds_soc_data {
-	int (*probe)(struct platform_device *pdev, struct rockchip_lvds *lvds);
-	const struct drm_encoder_helper_funcs *helper_funcs;
+struct rockchip_lvds_funcs {
+	int (*probe)(struct rockchip_lvds *lvds);
+	void (*enable)(struct rockchip_lvds *lvds);
+	void (*disable)(struct rockchip_lvds *lvds);
 };
 
 struct rockchip_lvds {
+	int id;
 	struct device *dev;
-	void __iomem *regs;
+	struct phy *phy;
 	struct regmap *grf;
-	struct clk *pclk;
-	struct phy *dphy;
-	const struct rockchip_lvds_soc_data *soc_data;
-	int output; /* rgb lvds or dual lvds output */
-	int format; /* vesa or jeida format */
-	struct drm_device *drm_dev;
+	const struct rockchip_lvds_funcs *funcs;
+	enum lvds_format format;
+	bool data_swap;
+	bool dual_channel;
+	enum drm_lvds_dual_link_pixels pixel_order;
+
+	struct rockchip_lvds *primary;
+	struct rockchip_lvds *secondary;
+
 	struct drm_panel *panel;
 	struct drm_bridge *bridge;
 	struct drm_connector connector;
 	struct drm_encoder encoder;
-	struct dev_pin_info *pins;
+	struct drm_display_mode mode;
+	struct rockchip_drm_sub_dev sub_dev;
 };
 
-static inline void rk3288_writel(struct rockchip_lvds *lvds, u32 offset,
-				 u32 val)
+static inline struct rockchip_lvds *connector_to_lvds(struct drm_connector *c)
 {
-	writel_relaxed(val, lvds->regs + offset);
-	if (lvds->output == DISPLAY_OUTPUT_LVDS)
-		return;
-	writel_relaxed(val, lvds->regs + offset + RK3288_LVDS_CH1_OFFSET);
+	return container_of(c, struct rockchip_lvds, connector);
 }
 
-static inline int rockchip_lvds_name_to_format(const char *s)
+static inline struct rockchip_lvds *encoder_to_lvds(struct drm_encoder *e)
 {
-	if (strncmp(s, "jeida-18", 8) == 0)
-		return LVDS_JEIDA_18;
-	else if (strncmp(s, "jeida-24", 8) == 0)
-		return LVDS_JEIDA_24;
-	else if (strncmp(s, "vesa-24", 7) == 0)
-		return LVDS_VESA_24;
-
-	return -EINVAL;
+	return container_of(e, struct rockchip_lvds, encoder);
 }
 
-static inline int rockchip_lvds_name_to_output(const char *s)
+static int
+rockchip_lvds_atomic_connector_get_property(struct drm_connector *connector,
+					    const struct drm_connector_state *state,
+					    struct drm_property *property,
+					    uint64_t *val)
 {
-	if (strncmp(s, "rgb", 3) == 0)
-		return DISPLAY_OUTPUT_RGB;
-	else if (strncmp(s, "lvds", 4) == 0)
-		return DISPLAY_OUTPUT_LVDS;
-	else if (strncmp(s, "duallvds", 8) == 0)
-		return DISPLAY_OUTPUT_DUAL_LVDS;
+	struct rockchip_lvds *lvds = connector_to_lvds(connector);
+	struct rockchip_drm_private *private = connector->dev->dev_private;
 
+	if (property == private->connector_id_prop) {
+		*val = lvds->id;
+		return 0;
+	}
+
+	DRM_ERROR("failed to get rockchip LVDS property\n");
 	return -EINVAL;
 }
 
@@ -107,6 +150,7 @@ static const struct drm_connector_funcs rockchip_lvds_connector_funcs = {
 	.reset = drm_atomic_helper_connector_reset,
 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+	.atomic_get_property = rockchip_lvds_atomic_connector_get_property,
 };
 
 static int rockchip_lvds_connector_get_modes(struct drm_connector *connector)
@@ -122,504 +166,235 @@ struct drm_connector_helper_funcs rockchip_lvds_connector_helper_funcs = {
 	.get_modes = rockchip_lvds_connector_get_modes,
 };
 
-static int
-rockchip_lvds_encoder_atomic_check(struct drm_encoder *encoder,
-				   struct drm_crtc_state *crtc_state,
-				   struct drm_connector_state *conn_state)
-{
-	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
-
-	s->output_mode = ROCKCHIP_OUT_MODE_P888;
-	s->output_type = DRM_MODE_CONNECTOR_LVDS;
-
-	return 0;
-}
-
-static int rk3288_lvds_poweron(struct rockchip_lvds *lvds)
+static void
+rockchip_lvds_encoder_atomic_mode_set(struct drm_encoder *encoder,
+				      struct drm_crtc_state *crtc_state,
+				      struct drm_connector_state *conn_state)
 {
-	int ret;
-	u32 val;
-
-	ret = clk_enable(lvds->pclk);
-	if (ret < 0) {
-		DRM_DEV_ERROR(lvds->dev, "failed to enable lvds pclk %d\n", ret);
-		return ret;
-	}
-	ret = pm_runtime_resume_and_get(lvds->dev);
-	if (ret < 0) {
-		DRM_DEV_ERROR(lvds->dev, "failed to get pm runtime: %d\n", ret);
-		clk_disable(lvds->pclk);
-		return ret;
-	}
-	val = RK3288_LVDS_CH0_REG0_LANE4_EN | RK3288_LVDS_CH0_REG0_LANE3_EN |
-		RK3288_LVDS_CH0_REG0_LANE2_EN | RK3288_LVDS_CH0_REG0_LANE1_EN |
-		RK3288_LVDS_CH0_REG0_LANE0_EN;
-	if (lvds->output == DISPLAY_OUTPUT_RGB) {
-		val |= RK3288_LVDS_CH0_REG0_TTL_EN |
-			RK3288_LVDS_CH0_REG0_LANECK_EN;
-		rk3288_writel(lvds, RK3288_LVDS_CH0_REG0, val);
-		rk3288_writel(lvds, RK3288_LVDS_CH0_REG2,
-			      RK3288_LVDS_PLL_FBDIV_REG2(0x46));
-		rk3288_writel(lvds, RK3288_LVDS_CH0_REG4,
-			      RK3288_LVDS_CH0_REG4_LANECK_TTL_MODE |
-			      RK3288_LVDS_CH0_REG4_LANE4_TTL_MODE |
-			      RK3288_LVDS_CH0_REG4_LANE3_TTL_MODE |
-			      RK3288_LVDS_CH0_REG4_LANE2_TTL_MODE |
-			      RK3288_LVDS_CH0_REG4_LANE1_TTL_MODE |
-			      RK3288_LVDS_CH0_REG4_LANE0_TTL_MODE);
-		rk3288_writel(lvds, RK3288_LVDS_CH0_REG5,
-			      RK3288_LVDS_CH0_REG5_LANECK_TTL_DATA |
-			      RK3288_LVDS_CH0_REG5_LANE4_TTL_DATA |
-			      RK3288_LVDS_CH0_REG5_LANE3_TTL_DATA |
-			      RK3288_LVDS_CH0_REG5_LANE2_TTL_DATA |
-			      RK3288_LVDS_CH0_REG5_LANE1_TTL_DATA |
-			      RK3288_LVDS_CH0_REG5_LANE0_TTL_DATA);
-	} else {
-		val |= RK3288_LVDS_CH0_REG0_LVDS_EN |
-			    RK3288_LVDS_CH0_REG0_LANECK_EN;
-		rk3288_writel(lvds, RK3288_LVDS_CH0_REG0, val);
-		rk3288_writel(lvds, RK3288_LVDS_CH0_REG1,
-			      RK3288_LVDS_CH0_REG1_LANECK_BIAS |
-			      RK3288_LVDS_CH0_REG1_LANE4_BIAS |
-			      RK3288_LVDS_CH0_REG1_LANE3_BIAS |
-			      RK3288_LVDS_CH0_REG1_LANE2_BIAS |
-			      RK3288_LVDS_CH0_REG1_LANE1_BIAS |
-			      RK3288_LVDS_CH0_REG1_LANE0_BIAS);
-		rk3288_writel(lvds, RK3288_LVDS_CH0_REG2,
-			      RK3288_LVDS_CH0_REG2_RESERVE_ON |
-			      RK3288_LVDS_CH0_REG2_LANECK_LVDS_MODE |
-			      RK3288_LVDS_CH0_REG2_LANE4_LVDS_MODE |
-			      RK3288_LVDS_CH0_REG2_LANE3_LVDS_MODE |
-			      RK3288_LVDS_CH0_REG2_LANE2_LVDS_MODE |
-			      RK3288_LVDS_CH0_REG2_LANE1_LVDS_MODE |
-			      RK3288_LVDS_CH0_REG2_LANE0_LVDS_MODE |
-			      RK3288_LVDS_PLL_FBDIV_REG2(0x46));
-		rk3288_writel(lvds, RK3288_LVDS_CH0_REG4, 0x00);
-		rk3288_writel(lvds, RK3288_LVDS_CH0_REG5, 0x00);
+	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
+	struct drm_connector *connector = &lvds->connector;
+	struct drm_display_info *info = &connector->display_info;
+	u32 bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG;
+
+	if (info->num_bus_formats)
+		bus_format = info->bus_formats[0];
+
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:	/* jeida-24 */
+		lvds->format = LVDS_8BIT_MODE_FORMAT_2;
+		break;
+	case MEDIA_BUS_FMT_RGB101010_1X7X5_JEIDA: /* jeida-30 */
+		lvds->format = LVDS_10BIT_MODE_FORMAT_2;
+		break;
+	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:	/* vesa-18 */
+		lvds->format = LVDS_8BIT_MODE_FORMAT_3;
+		break;
+	case MEDIA_BUS_FMT_RGB101010_1X7X5_SPWG: /* vesa-30 */
+		lvds->format = LVDS_10BIT_MODE_FORMAT_1;
+		break;
+	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:	/* vesa-24 */
+	default:
+		lvds->format = LVDS_8BIT_MODE_FORMAT_1;
+		break;
 	}
-	rk3288_writel(lvds, RK3288_LVDS_CH0_REG3,
-		      RK3288_LVDS_PLL_FBDIV_REG3(0x46));
-	rk3288_writel(lvds, RK3288_LVDS_CH0_REGD,
-		      RK3288_LVDS_PLL_PREDIV_REGD(0x0a));
-	rk3288_writel(lvds, RK3288_LVDS_CH0_REG20,
-		      RK3288_LVDS_CH0_REG20_LSB);
-
-	rk3288_writel(lvds, RK3288_LVDS_CFG_REGC,
-		      RK3288_LVDS_CFG_REGC_PLL_ENABLE);
-	rk3288_writel(lvds, RK3288_LVDS_CFG_REG21,
-		      RK3288_LVDS_CFG_REG21_TX_ENABLE);
-
-	return 0;
-}
 
-static void rk3288_lvds_poweroff(struct rockchip_lvds *lvds)
-{
-	int ret;
-	u32 val;
+	if (lvds->secondary)
+		lvds->secondary->format = lvds->format;
 
-	rk3288_writel(lvds, RK3288_LVDS_CFG_REG21,
-		      RK3288_LVDS_CFG_REG21_TX_ENABLE);
-	rk3288_writel(lvds, RK3288_LVDS_CFG_REGC,
-		      RK3288_LVDS_CFG_REGC_PLL_ENABLE);
-	val = LVDS_DUAL | LVDS_TTL_EN | LVDS_CH0_EN | LVDS_CH1_EN | LVDS_PWRDN;
-	val |= val << 16;
-	ret = regmap_write(lvds->grf, RK3288_LVDS_GRF_SOC_CON7, val);
-	if (ret != 0)
-		DRM_DEV_ERROR(lvds->dev, "Could not write to GRF: %d\n", ret);
-
-	pm_runtime_put(lvds->dev);
-	clk_disable(lvds->pclk);
+	drm_mode_copy(&lvds->mode, &crtc_state->adjusted_mode);
 }
 
-static int rk3288_lvds_grf_config(struct drm_encoder *encoder,
-				  struct drm_display_mode *mode)
+static int
+rockchip_lvds_encoder_atomic_check(struct drm_encoder *encoder,
+				   struct drm_crtc_state *crtc_state,
+				   struct drm_connector_state *conn_state)
 {
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
 	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
-	u8 pin_hsync = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 1 : 0;
-	u8 pin_dclk = (mode->flags & DRM_MODE_FLAG_PCSYNC) ? 1 : 0;
-	u32 val;
-	int ret;
-
-	/* iomux to LCD data/sync mode */
-	if (lvds->output == DISPLAY_OUTPUT_RGB)
-		if (lvds->pins && !IS_ERR(lvds->pins->default_state))
-			pinctrl_select_state(lvds->pins->p,
-					     lvds->pins->default_state);
-	val = lvds->format | LVDS_CH0_EN;
-	if (lvds->output == DISPLAY_OUTPUT_RGB)
-		val |= LVDS_TTL_EN | LVDS_CH1_EN;
-	else if (lvds->output == DISPLAY_OUTPUT_DUAL_LVDS)
-		val |= LVDS_DUAL | LVDS_CH1_EN;
-
-	if ((mode->htotal - mode->hsync_start) & 0x01)
-		val |= LVDS_START_PHASE_RST_1;
-
-	val |= (pin_dclk << 8) | (pin_hsync << 9);
-	val |= (0xffff << 16);
-	ret = regmap_write(lvds->grf, RK3288_LVDS_GRF_SOC_CON7, val);
-	if (ret)
-		DRM_DEV_ERROR(lvds->dev, "Could not write to GRF: %d\n", ret);
-
-	return ret;
-}
+	struct drm_connector *connector = conn_state->connector;
+	struct drm_display_info *info = &connector->display_info;
 
-static int rk3288_lvds_set_vop_source(struct rockchip_lvds *lvds,
-				      struct drm_encoder *encoder)
-{
-	u32 val;
-	int ret;
+	if (info->num_bus_formats)
+		s->bus_format = info->bus_formats[0];
+	else
+		s->bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG;
 
-	ret = drm_of_encoder_active_endpoint_id(lvds->dev->of_node, encoder);
-	if (ret < 0)
-		return ret;
+	s->output_mode = ROCKCHIP_OUT_MODE_P888;
 
-	val = RK3288_LVDS_SOC_CON6_SEL_VOP_LIT << 16;
-	if (ret)
-		val |= RK3288_LVDS_SOC_CON6_SEL_VOP_LIT;
+	if (s->bus_format == MEDIA_BUS_FMT_RGB101010_1X7X5_SPWG ||
+	    s->bus_format == MEDIA_BUS_FMT_RGB101010_1X7X5_JEIDA)
+		s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
 
-	ret = regmap_write(lvds->grf, RK3288_LVDS_GRF_SOC_CON6, val);
-	if (ret < 0)
-		return ret;
+	s->output_type = DRM_MODE_CONNECTOR_LVDS;
+	s->bus_flags = info->bus_flags;
+	s->tv_state = &conn_state->tv;
+	s->eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR;
+	s->color_space = V4L2_COLORSPACE_DEFAULT;
+
+	switch (lvds->pixel_order) {
+	case DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS:
+		s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE;
+		s->output_if |= VOP_OUTPUT_IF_LVDS1 | VOP_OUTPUT_IF_LVDS0;
+		break;
+	case DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS:
+		s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE;
+		s->output_flags |= ROCKCHIP_OUTPUT_DATA_SWAP;
+		s->output_if |= VOP_OUTPUT_IF_LVDS1 | VOP_OUTPUT_IF_LVDS0;
+		break;
+/*
+ * Fix me: To do it with a GKI compatible version.
+ */
+#if 0
+	case DRM_LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS:
+		s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE;
+		s->output_if |= VOP_OUTPUT_IF_LVDS1 | VOP_OUTPUT_IF_LVDS0;
+		break;
+	case DRM_LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS:
+		s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE;
+		s->output_flags |= ROCKCHIP_OUTPUT_DATA_SWAP;
+		s->output_if |= VOP_OUTPUT_IF_LVDS1 | VOP_OUTPUT_IF_LVDS0;
+		break;
+#endif
+	default:
+		if (lvds->id)
+			s->output_if |= VOP_OUTPUT_IF_LVDS1;
+		else
+			s->output_if |= VOP_OUTPUT_IF_LVDS0;
+		break;
+	}
 
 	return 0;
 }
 
-static void rk3288_lvds_encoder_enable(struct drm_encoder *encoder)
+static void rockchip_lvds_enable(struct rockchip_lvds *lvds)
 {
-	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
-	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
 	int ret;
 
-	drm_panel_prepare(lvds->panel);
+	if (lvds->funcs->enable)
+		lvds->funcs->enable(lvds);
 
-	ret = rk3288_lvds_poweron(lvds);
-	if (ret < 0) {
-		DRM_DEV_ERROR(lvds->dev, "failed to power on LVDS: %d\n", ret);
-		drm_panel_unprepare(lvds->panel);
-		return;
-	}
-
-	ret = rk3288_lvds_grf_config(encoder, mode);
+	ret = phy_set_mode(lvds->phy, PHY_MODE_LVDS);
 	if (ret) {
-		DRM_DEV_ERROR(lvds->dev, "failed to configure LVDS: %d\n", ret);
-		drm_panel_unprepare(lvds->panel);
+		DRM_DEV_ERROR(lvds->dev, "failed to set phy mode: %d\n", ret);
 		return;
 	}
 
-	ret = rk3288_lvds_set_vop_source(lvds, encoder);
-	if (ret) {
-		DRM_DEV_ERROR(lvds->dev, "failed to set VOP source: %d\n", ret);
-		drm_panel_unprepare(lvds->panel);
-		return;
-	}
+	phy_power_on(lvds->phy);
 
-	drm_panel_enable(lvds->panel);
+	if (lvds->secondary)
+		rockchip_lvds_enable(lvds->secondary);
 }
 
-static void rk3288_lvds_encoder_disable(struct drm_encoder *encoder)
+static void rockchip_lvds_disable(struct rockchip_lvds *lvds)
 {
-	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
+	if (lvds->funcs->disable)
+		lvds->funcs->disable(lvds);
 
-	drm_panel_disable(lvds->panel);
-	rk3288_lvds_poweroff(lvds);
-	drm_panel_unprepare(lvds->panel);
-}
+	phy_power_off(lvds->phy);
 
-static int px30_lvds_poweron(struct rockchip_lvds *lvds)
-{
-	int ret;
-
-	ret = pm_runtime_resume_and_get(lvds->dev);
-	if (ret < 0) {
-		DRM_DEV_ERROR(lvds->dev, "failed to get pm runtime: %d\n", ret);
-		return ret;
-	}
-
-	/* Enable LVDS mode */
-	ret = regmap_update_bits(lvds->grf, PX30_LVDS_GRF_PD_VO_CON1,
-				  PX30_LVDS_MODE_EN(1) | PX30_LVDS_P2S_EN(1),
-				  PX30_LVDS_MODE_EN(1) | PX30_LVDS_P2S_EN(1));
-	if (ret)
-		pm_runtime_put(lvds->dev);
-
-	return ret;
-}
-
-static void px30_lvds_poweroff(struct rockchip_lvds *lvds)
-{
-	regmap_update_bits(lvds->grf, PX30_LVDS_GRF_PD_VO_CON1,
-			   PX30_LVDS_MODE_EN(1) | PX30_LVDS_P2S_EN(1),
-			   PX30_LVDS_MODE_EN(0) | PX30_LVDS_P2S_EN(0));
-
-	pm_runtime_put(lvds->dev);
+	if (lvds->secondary)
+		rockchip_lvds_disable(lvds->secondary);
 }
 
-static int px30_lvds_grf_config(struct drm_encoder *encoder,
-				struct drm_display_mode *mode)
+static void rockchip_lvds_encoder_enable(struct drm_encoder *encoder)
 {
 	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
 
-	if (lvds->output != DISPLAY_OUTPUT_LVDS) {
-		DRM_DEV_ERROR(lvds->dev, "Unsupported display output %d\n",
-			      lvds->output);
-		return -EINVAL;
-	}
-
-	/* Set format */
-	return regmap_update_bits(lvds->grf, PX30_LVDS_GRF_PD_VO_CON1,
-				  PX30_LVDS_FORMAT(lvds->format),
-				  PX30_LVDS_FORMAT(lvds->format));
-}
-
-static int px30_lvds_set_vop_source(struct rockchip_lvds *lvds,
-				    struct drm_encoder *encoder)
-{
-	int vop;
-
-	vop = drm_of_encoder_active_endpoint_id(lvds->dev->of_node, encoder);
-	if (vop < 0)
-		return vop;
-
-	return regmap_update_bits(lvds->grf, PX30_LVDS_GRF_PD_VO_CON1,
-				  PX30_LVDS_VOP_SEL(1),
-				  PX30_LVDS_VOP_SEL(vop));
+	if (lvds->panel)
+		drm_panel_prepare(lvds->panel);
+	rockchip_lvds_enable(lvds);
+	if (lvds->panel)
+		drm_panel_enable(lvds->panel);
 }
 
-static void px30_lvds_encoder_enable(struct drm_encoder *encoder)
+static void rockchip_lvds_encoder_disable(struct drm_encoder *encoder)
 {
 	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
-	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
-	int ret;
-
-	drm_panel_prepare(lvds->panel);
-
-	ret = px30_lvds_poweron(lvds);
-	if (ret) {
-		DRM_DEV_ERROR(lvds->dev, "failed to power on LVDS: %d\n", ret);
-		drm_panel_unprepare(lvds->panel);
-		return;
-	}
-
-	ret = px30_lvds_grf_config(encoder, mode);
-	if (ret) {
-		DRM_DEV_ERROR(lvds->dev, "failed to configure LVDS: %d\n", ret);
-		drm_panel_unprepare(lvds->panel);
-		return;
-	}
 
-	ret = px30_lvds_set_vop_source(lvds, encoder);
-	if (ret) {
-		DRM_DEV_ERROR(lvds->dev, "failed to set VOP source: %d\n", ret);
+	if (lvds->panel)
+		drm_panel_disable(lvds->panel);
+	rockchip_lvds_disable(lvds);
+	if (lvds->panel)
 		drm_panel_unprepare(lvds->panel);
-		return;
-	}
-
-	drm_panel_enable(lvds->panel);
 }
 
-static void px30_lvds_encoder_disable(struct drm_encoder *encoder)
+static void rockchip_lvds_encoder_loader_protect(struct drm_encoder *encoder,
+						 bool on)
 {
 	struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
 
-	drm_panel_disable(lvds->panel);
-	px30_lvds_poweroff(lvds);
-	drm_panel_unprepare(lvds->panel);
+	if (lvds->panel)
+		panel_simple_loader_protect(lvds->panel);
 }
 
 static const
-struct drm_encoder_helper_funcs rk3288_lvds_encoder_helper_funcs = {
-	.enable = rk3288_lvds_encoder_enable,
-	.disable = rk3288_lvds_encoder_disable,
+struct drm_encoder_helper_funcs rockchip_lvds_encoder_helper_funcs = {
+	.enable = rockchip_lvds_encoder_enable,
+	.disable = rockchip_lvds_encoder_disable,
 	.atomic_check = rockchip_lvds_encoder_atomic_check,
+	.atomic_mode_set = rockchip_lvds_encoder_atomic_mode_set,
 };
 
-static const
-struct drm_encoder_helper_funcs px30_lvds_encoder_helper_funcs = {
-	.enable = px30_lvds_encoder_enable,
-	.disable = px30_lvds_encoder_disable,
-	.atomic_check = rockchip_lvds_encoder_atomic_check,
+static const struct drm_encoder_funcs rockchip_lvds_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
 };
 
-static int rk3288_lvds_probe(struct platform_device *pdev,
-			     struct rockchip_lvds *lvds)
+static int rockchip_lvds_match_by_id(struct device *dev, const void *data)
 {
-	struct resource *res;
-	int ret;
-
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	lvds->regs = devm_ioremap_resource(lvds->dev, res);
-	if (IS_ERR(lvds->regs))
-		return PTR_ERR(lvds->regs);
-
-	lvds->pclk = devm_clk_get(lvds->dev, "pclk_lvds");
-	if (IS_ERR(lvds->pclk)) {
-		DRM_DEV_ERROR(lvds->dev, "could not get pclk_lvds\n");
-		return PTR_ERR(lvds->pclk);
-	}
-
-	lvds->pins = devm_kzalloc(lvds->dev, sizeof(*lvds->pins),
-				  GFP_KERNEL);
-	if (!lvds->pins)
-		return -ENOMEM;
-
-	lvds->pins->p = devm_pinctrl_get(lvds->dev);
-	if (IS_ERR(lvds->pins->p)) {
-		DRM_DEV_ERROR(lvds->dev, "no pinctrl handle\n");
-		devm_kfree(lvds->dev, lvds->pins);
-		lvds->pins = NULL;
-	} else {
-		lvds->pins->default_state =
-			pinctrl_lookup_state(lvds->pins->p, "lcdc");
-		if (IS_ERR(lvds->pins->default_state)) {
-			DRM_DEV_ERROR(lvds->dev, "no default pinctrl state\n");
-			devm_kfree(lvds->dev, lvds->pins);
-			lvds->pins = NULL;
-		}
-	}
-
-	ret = clk_prepare(lvds->pclk);
-	if (ret < 0) {
-		DRM_DEV_ERROR(lvds->dev, "failed to prepare pclk_lvds\n");
-		return ret;
-	}
+	struct rockchip_lvds *lvds = dev_get_drvdata(dev);
+	unsigned int *id = (unsigned int *)data;
 
-	return 0;
+	return lvds->id == *id;
 }
 
-static int px30_lvds_probe(struct platform_device *pdev,
-			   struct rockchip_lvds *lvds)
+static struct rockchip_lvds *rockchip_lvds_find_by_id(struct device_driver *drv,
+						      unsigned int id)
 {
-	int ret;
-
-	/* MSB */
-	ret =  regmap_update_bits(lvds->grf, PX30_LVDS_GRF_PD_VO_CON1,
-				  PX30_LVDS_MSBSEL(1),
-				  PX30_LVDS_MSBSEL(1));
-	if (ret)
-		return ret;
-
-	/* PHY */
-	lvds->dphy = devm_phy_get(&pdev->dev, "dphy");
-	if (IS_ERR(lvds->dphy))
-		return PTR_ERR(lvds->dphy);
-
-	ret = phy_init(lvds->dphy);
-	if (ret)
-		return ret;
+	struct device *dev;
 
-	ret = phy_set_mode(lvds->dphy, PHY_MODE_LVDS);
-	if (ret)
-		return ret;
+	dev = driver_find_device(drv, NULL, &id, rockchip_lvds_match_by_id);
+	if (!dev)
+		return NULL;
 
-	return phy_power_on(lvds->dphy);
+	return dev_get_drvdata(dev);
 }
 
-static const struct rockchip_lvds_soc_data rk3288_lvds_data = {
-	.probe = rk3288_lvds_probe,
-	.helper_funcs = &rk3288_lvds_encoder_helper_funcs,
-};
-
-static const struct rockchip_lvds_soc_data px30_lvds_data = {
-	.probe = px30_lvds_probe,
-	.helper_funcs = &px30_lvds_encoder_helper_funcs,
-};
-
-static const struct of_device_id rockchip_lvds_dt_ids[] = {
-	{
-		.compatible = "rockchip,rk3288-lvds",
-		.data = &rk3288_lvds_data
-	},
-	{
-		.compatible = "rockchip,px30-lvds",
-		.data = &px30_lvds_data
-	},
-	{}
-};
-MODULE_DEVICE_TABLE(of, rockchip_lvds_dt_ids);
-
 static int rockchip_lvds_bind(struct device *dev, struct device *master,
 			      void *data)
 {
 	struct rockchip_lvds *lvds = dev_get_drvdata(dev);
 	struct drm_device *drm_dev = data;
-	struct drm_encoder *encoder;
-	struct drm_connector *connector;
-	struct device_node *remote = NULL;
-	struct device_node  *port, *endpoint;
-	int ret = 0, child_count = 0;
-	const char *name;
-	u32 endpoint_id = 0;
-
-	lvds->drm_dev = drm_dev;
-	port = of_graph_get_port_by_id(dev->of_node, 1);
-	if (!port) {
-		DRM_DEV_ERROR(dev,
-			      "can't found port point, please init lvds panel port!\n");
-		return -EINVAL;
-	}
-	for_each_child_of_node(port, endpoint) {
-		child_count++;
-		of_property_read_u32(endpoint, "reg", &endpoint_id);
-		ret = drm_of_find_panel_or_bridge(dev->of_node, 1, endpoint_id,
-						  &lvds->panel, &lvds->bridge);
-		if (!ret) {
-			of_node_put(endpoint);
-			break;
-		}
-	}
-	if (!child_count) {
-		DRM_DEV_ERROR(dev, "lvds port does not have any children\n");
-		ret = -EINVAL;
-		goto err_put_port;
-	} else if (ret) {
-		DRM_DEV_ERROR(dev, "failed to find panel and bridge node\n");
-		ret = -EPROBE_DEFER;
-		goto err_put_port;
-	}
-	if (lvds->panel)
-		remote = lvds->panel->dev->of_node;
-	else
-		remote = lvds->bridge->of_node;
-	if (of_property_read_string(dev->of_node, "rockchip,output", &name))
-		/* default set it as output rgb */
-		lvds->output = DISPLAY_OUTPUT_RGB;
-	else
-		lvds->output = rockchip_lvds_name_to_output(name);
-
-	if (lvds->output < 0) {
-		DRM_DEV_ERROR(dev, "invalid output type [%s]\n", name);
-		ret = lvds->output;
-		goto err_put_remote;
-	}
+	struct drm_encoder *encoder = &lvds->encoder;
+	struct drm_connector *connector = &lvds->connector;
+	int ret;
 
-	if (of_property_read_string(remote, "data-mapping", &name))
-		/* default set it as format vesa 18 */
-		lvds->format = LVDS_VESA_18;
-	else
-		lvds->format = rockchip_lvds_name_to_format(name);
+	/*
+	 * dual channel lvds mode only need to register one connector.
+	 */
+	if (lvds->primary)
+		return 0;
 
-	if (lvds->format < 0) {
-		DRM_DEV_ERROR(dev, "invalid data-mapping format [%s]\n", name);
-		ret = lvds->format;
-		goto err_put_remote;
-	}
+	ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1,
+					  &lvds->panel, &lvds->bridge);
+	if (ret)
+		return ret;
 
-	encoder = &lvds->encoder;
-	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
-							     dev->of_node);
+	encoder->possible_crtcs = rockchip_drm_of_find_possible_crtcs(drm_dev,
+								      dev->of_node);
 
-	ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_LVDS);
+	ret = drm_encoder_init(drm_dev, encoder, &rockchip_lvds_encoder_funcs,
+			       DRM_MODE_ENCODER_LVDS, NULL);
 	if (ret < 0) {
-		DRM_DEV_ERROR(drm_dev->dev,
+		DRM_DEV_ERROR(lvds->dev,
 			      "failed to initialize encoder: %d\n", ret);
-		goto err_put_remote;
+		return ret;
 	}
 
-	drm_encoder_helper_add(encoder, lvds->soc_data->helper_funcs);
+	drm_encoder_helper_add(encoder, &rockchip_lvds_encoder_helper_funcs);
 
 	if (lvds->panel) {
-		connector = &lvds->connector;
-		connector->dpms = DRM_MODE_DPMS_OFF;
+		struct rockchip_drm_private *private = drm_dev->dev_private;
+
 		ret = drm_connector_init(drm_dev, connector,
 					 &rockchip_lvds_connector_funcs,
 					 DRM_MODE_CONNECTOR_LVDS);
@@ -634,34 +409,31 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
 
 		ret = drm_connector_attach_encoder(connector, encoder);
 		if (ret < 0) {
-			DRM_DEV_ERROR(drm_dev->dev,
+			DRM_DEV_ERROR(lvds->dev,
 				      "failed to attach encoder: %d\n", ret);
 			goto err_free_connector;
 		}
+
+		lvds->sub_dev.connector = &lvds->connector;
+		lvds->sub_dev.of_node = lvds->dev->of_node;
+		lvds->sub_dev.loader_protect = rockchip_lvds_encoder_loader_protect;
+		rockchip_drm_register_sub_dev(&lvds->sub_dev);
+		drm_object_attach_property(&connector->base, private->connector_id_prop, 0);
 	} else {
 		ret = drm_bridge_attach(encoder, lvds->bridge, NULL, 0);
 		if (ret) {
-			DRM_DEV_ERROR(drm_dev->dev,
+			DRM_DEV_ERROR(lvds->dev,
 				      "failed to attach bridge: %d\n", ret);
 			goto err_free_encoder;
 		}
 	}
 
-	pm_runtime_enable(dev);
-	of_node_put(remote);
-	of_node_put(port);
-
 	return 0;
 
 err_free_connector:
 	drm_connector_cleanup(connector);
 err_free_encoder:
 	drm_encoder_cleanup(encoder);
-err_put_remote:
-	of_node_put(remote);
-err_put_port:
-	of_node_put(port);
-
 	return ret;
 }
 
@@ -669,13 +441,14 @@ static void rockchip_lvds_unbind(struct device *dev, struct device *master,
 				void *data)
 {
 	struct rockchip_lvds *lvds = dev_get_drvdata(dev);
-	const struct drm_encoder_helper_funcs *encoder_funcs;
 
-	encoder_funcs = lvds->soc_data->helper_funcs;
-	encoder_funcs->disable(&lvds->encoder);
-	pm_runtime_disable(dev);
-	drm_connector_cleanup(&lvds->connector);
-	drm_encoder_cleanup(&lvds->encoder);
+	if (lvds->sub_dev.connector)
+		rockchip_drm_unregister_sub_dev(&lvds->sub_dev);
+	if (lvds->panel)
+		drm_connector_cleanup(&lvds->connector);
+
+	if (lvds->encoder.dev)
+		drm_encoder_cleanup(&lvds->encoder);
 }
 
 static const struct component_ops rockchip_lvds_component_ops = {
@@ -687,56 +460,219 @@ static int rockchip_lvds_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct rockchip_lvds *lvds;
-	const struct of_device_id *match;
 	int ret;
 
 	if (!dev->of_node)
 		return -ENODEV;
 
-	lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
+	lvds = devm_kzalloc(dev, sizeof(*lvds), GFP_KERNEL);
 	if (!lvds)
 		return -ENOMEM;
 
+	lvds->id = of_alias_get_id(dev->of_node, "lvds");
+	if (lvds->id < 0)
+		lvds->id = 0;
+
 	lvds->dev = dev;
-	match = of_match_node(rockchip_lvds_dt_ids, dev->of_node);
-	if (!match)
-		return -ENODEV;
-	lvds->soc_data = match->data;
+	lvds->funcs = of_device_get_match_data(dev);
+	platform_set_drvdata(pdev, lvds);
+
+	lvds->dual_channel = of_property_read_bool(dev->of_node,
+						   "dual-channel");
+	lvds->data_swap = of_property_read_bool(dev->of_node,
+						"rockchip,data-swap");
+
+	lvds->phy = devm_phy_get(dev, "phy");
+	if (IS_ERR(lvds->phy)) {
+		ret = PTR_ERR(lvds->phy);
+		DRM_DEV_ERROR(dev, "failed to get phy: %d\n", ret);
+		return ret;
+	}
 
-	lvds->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
-						    "rockchip,grf");
+	lvds->grf = syscon_node_to_regmap(dev->parent->of_node);
 	if (IS_ERR(lvds->grf)) {
-		DRM_DEV_ERROR(dev, "missing rockchip,grf property\n");
-		return PTR_ERR(lvds->grf);
+		ret = PTR_ERR(lvds->grf);
+		DRM_DEV_ERROR(dev, "Unable to get grf: %d\n", ret);
+		return ret;
 	}
 
-	ret = lvds->soc_data->probe(pdev, lvds);
-	if (ret) {
-		DRM_DEV_ERROR(dev, "Platform initialization failed\n");
-		return ret;
+	lvds->pixel_order = -1;
+	if (lvds->funcs->probe) {
+		ret = lvds->funcs->probe(lvds);
+		if (ret)
+			return ret;
 	}
 
-	dev_set_drvdata(dev, lvds);
+	return component_add(dev, &rockchip_lvds_component_ops);
+}
 
-	ret = component_add(&pdev->dev, &rockchip_lvds_component_ops);
-	if (ret < 0) {
-		DRM_DEV_ERROR(dev, "failed to add component\n");
-		clk_unprepare(lvds->pclk);
+static int rockchip_lvds_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &rockchip_lvds_component_ops);
+
+	return 0;
+}
+
+static void px30_lvds_enable(struct rockchip_lvds *lvds)
+{
+	int pipe = drm_of_encoder_active_endpoint_id(lvds->dev->of_node,
+						     &lvds->encoder);
+
+	regmap_write(lvds->grf, PX30_GRF_PD_VO_CON1,
+		     PX30_LVDS_SELECT(lvds->format) |
+		     PX30_LVDS_MODE_EN(1) | PX30_LVDS_MSBSEL(1) |
+		     PX30_LVDS_P2S_EN(1) | PX30_LVDS_VOP_SEL(pipe));
+}
+
+static void px30_lvds_disable(struct rockchip_lvds *lvds)
+{
+	regmap_write(lvds->grf, PX30_GRF_PD_VO_CON1,
+		     PX30_LVDS_MODE_EN(0) | PX30_LVDS_P2S_EN(0));
+}
+
+static const struct rockchip_lvds_funcs px30_lvds_funcs = {
+	.enable = px30_lvds_enable,
+	.disable = px30_lvds_disable,
+};
+
+static void rk3126_lvds_enable(struct rockchip_lvds *lvds)
+{
+	regmap_write(lvds->grf, RK3126_GRF_LVDS_CON0,
+		     RK3126_LVDS_P2S_EN(1) | RK3126_LVDS_MODE_EN(1) |
+		     RK3126_LVDS_MSBSEL(1) | RK3126_LVDS_SELECT(lvds->format));
+}
+
+static void rk3126_lvds_disable(struct rockchip_lvds *lvds)
+{
+	regmap_write(lvds->grf, RK3126_GRF_LVDS_CON0,
+		     RK3126_LVDS_P2S_EN(0) | RK3126_LVDS_MODE_EN(0));
+}
+
+static const struct rockchip_lvds_funcs rk3126_lvds_funcs = {
+	.enable = rk3126_lvds_enable,
+	.disable = rk3126_lvds_disable,
+};
+
+static void rk3288_lvds_enable(struct rockchip_lvds *lvds)
+{
+	struct drm_display_mode *mode = &lvds->mode;
+	int pipe;
+	u32 val;
+
+	pipe = drm_of_encoder_active_endpoint_id(lvds->dev->of_node,
+						 &lvds->encoder);
+	regmap_write(lvds->grf, RK3288_GRF_SOC_CON6,
+		     RK3288_LVDS_LCDC_SEL(pipe));
+
+	val = RK3288_LVDS_PWRDWN(0) | RK3288_LVDS_CON_CLKINV(0) |
+	      RK3288_LVDS_CON_CHASEL(lvds->dual_channel) |
+	      RK3288_LVDS_CON_SELECT(lvds->format);
+
+	if (lvds->dual_channel) {
+		u32 h_bp = mode->htotal - mode->hsync_start;
+
+		val |= RK3288_LVDS_CON_ENABLE_2(1) |
+		       RK3288_LVDS_CON_ENABLE_1(1) |
+		       RK3288_LVDS_CON_STARTSEL(lvds->data_swap);
+
+		if (h_bp % 2)
+			val |= RK3288_LVDS_CON_STARTPHASE(1);
+		else
+			val |= RK3288_LVDS_CON_STARTPHASE(0);
+
+	} else {
+		val |= RK3288_LVDS_CON_ENABLE_2(0) |
+		       RK3288_LVDS_CON_ENABLE_1(1);
 	}
 
-	return ret;
+	regmap_write(lvds->grf, RK3288_GRF_SOC_CON7, val);
+
+	phy_set_bus_width(lvds->phy, lvds->dual_channel ? 2 : 1);
 }
 
-static int rockchip_lvds_remove(struct platform_device *pdev)
+static void rk3288_lvds_disable(struct rockchip_lvds *lvds)
 {
-	struct rockchip_lvds *lvds = dev_get_drvdata(&pdev->dev);
+	regmap_write(lvds->grf, RK3288_GRF_SOC_CON7, RK3288_LVDS_PWRDWN(1));
+}
 
-	component_del(&pdev->dev, &rockchip_lvds_component_ops);
-	clk_unprepare(lvds->pclk);
+static const struct rockchip_lvds_funcs rk3288_lvds_funcs = {
+	.enable = rk3288_lvds_enable,
+	.disable = rk3288_lvds_disable,
+};
+
+static void rk3368_lvds_enable(struct rockchip_lvds *lvds)
+{
+	regmap_write(lvds->grf, RK3368_GRF_SOC_CON7,
+		     RK3368_LVDS_SELECT(lvds->format) |
+		     RK3368_LVDS_MODE_EN(1) | RK3368_LVDS_MSBSEL(1) |
+		     RK3368_LVDS_P2S_EN(1));
+}
+
+static void rk3368_lvds_disable(struct rockchip_lvds *lvds)
+{
+	regmap_write(lvds->grf, RK3368_GRF_SOC_CON7,
+		     RK3368_LVDS_MODE_EN(0) | RK3368_LVDS_P2S_EN(0));
+}
+
+static const struct rockchip_lvds_funcs rk3368_lvds_funcs = {
+	.enable = rk3368_lvds_enable,
+	.disable = rk3368_lvds_disable,
+};
+
+static int __maybe_unused rockchip_secondary_lvds_probe(struct rockchip_lvds *lvds)
+{
+	if (lvds->dual_channel) {
+		struct rockchip_lvds *secondary = NULL;
+		struct device_node *port0, *port1;
+		int pixel_order;
+
+		secondary = rockchip_lvds_find_by_id(lvds->dev->driver, 1);
+		if (!secondary)
+			return -EPROBE_DEFER;
+
+		port0 = of_graph_get_port_by_id(lvds->dev->of_node, 1);
+		port1 = of_graph_get_port_by_id(secondary->dev->of_node, 1);
+		pixel_order = drm_of_lvds_get_dual_link_pixel_order(port0, port1);
+		of_node_put(port1);
+		of_node_put(port0);
+
+		secondary->primary = lvds;
+		lvds->secondary = secondary;
+		lvds->pixel_order = pixel_order >= 0 ? pixel_order : 0;
+	}
 
 	return 0;
 }
 
+static void rk3568_lvds_enable(struct rockchip_lvds *lvds)
+{
+	regmap_write(lvds->grf, RK3568_GRF_VO_CON2,
+		     RK3568_LVDS0_MODE_EN(1) | RK3568_LVDS0_P2S_EN(1) |
+		     RK3568_LVDS0_DCLK_INV_SEL(1));
+	regmap_write(lvds->grf, RK3568_GRF_VO_CON0,
+		     RK3568_LVDS0_SELECT(lvds->format) | RK3568_LVDS0_MSBSEL(1));
+}
+
+static void rk3568_lvds_disable(struct rockchip_lvds *lvds)
+{
+	regmap_write(lvds->grf, RK3568_GRF_VO_CON2, RK3568_LVDS0_MODE_EN(0));
+}
+
+static const struct rockchip_lvds_funcs rk3568_lvds_funcs = {
+	.enable = rk3568_lvds_enable,
+	.disable = rk3568_lvds_disable,
+};
+
+static const struct of_device_id rockchip_lvds_dt_ids[] = {
+	{ .compatible = "rockchip,px30-lvds", .data = &px30_lvds_funcs },
+	{ .compatible = "rockchip,rk3126-lvds", .data = &rk3126_lvds_funcs },
+	{ .compatible = "rockchip,rk3288-lvds", .data = &rk3288_lvds_funcs },
+	{ .compatible = "rockchip,rk3368-lvds", .data = &rk3368_lvds_funcs },
+	{ .compatible = "rockchip,rk3568-lvds", .data = &rk3568_lvds_funcs },
+	{}
+};
+MODULE_DEVICE_TABLE(of, rockchip_lvds_dt_ids);
+
 struct platform_driver rockchip_lvds_driver = {
 	.probe = rockchip_lvds_probe,
 	.remove = rockchip_lvds_remove,
diff --git a/drivers/gpu/drm/rockchip/rockchip_rgb.c b/drivers/gpu/drm/rockchip/rockchip_rgb.c
index 9a771af5d..654de19f7 100644
--- a/drivers/gpu/drm/rockchip/rockchip_rgb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_rgb.c
@@ -6,29 +6,172 @@
  */
 
 #include <linux/component.h>
+#include <linux/of_device.h>
 #include <linux/of_graph.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+#include <linux/pinctrl/consumer.h>
 
 #include <drm/drm_atomic_helper.h>
-#include <drm/drm_bridge.h>
+#include <drm/drm_crtc_helper.h>
 #include <drm/drm_dp_helper.h>
 #include <drm/drm_of.h>
 #include <drm/drm_panel.h>
 #include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
+
+#include <uapi/linux/videodev2.h>
 
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_vop.h"
 
-#define encoder_to_rgb(c) container_of(c, struct rockchip_rgb, encoder)
+#define HIWORD_UPDATE(v, l, h)	(((v) << (l)) | (GENMASK(h, l) << 16))
+
+#define PX30_GRF_PD_VO_CON1		0x0438
+#define PX30_RGB_DATA_SYNC_BYPASS(v)	HIWORD_UPDATE(v, 3, 3)
+#define PX30_RGB_VOP_SEL(v)		HIWORD_UPDATE(v, 2, 2)
+
+#define RK1808_GRF_PD_VO_CON1		0x0444
+#define RK1808_RGB_DATA_SYNC_BYPASS(v)	HIWORD_UPDATE(v, 3, 3)
+
+#define RV1126_GRF_IOFUNC_CON3		0x1026c
+#define RV1126_LCDC_IO_BYPASS(v)	HIWORD_UPDATE(v, 0, 0)
+
+#define RK3288_GRF_SOC_CON6		0x025c
+#define RK3288_LVDS_LCDC_SEL(x)		HIWORD_UPDATE(x,  3,  3)
+#define RK3288_GRF_SOC_CON7		0x0260
+#define RK3288_LVDS_PWRDWN(x)		HIWORD_UPDATE(x, 15, 15)
+#define RK3288_LVDS_CON_ENABLE_2(x)	HIWORD_UPDATE(x, 12, 12)
+#define RK3288_LVDS_CON_ENABLE_1(x)	HIWORD_UPDATE(x, 11, 11)
+#define RK3288_LVDS_CON_CLKINV(x)	HIWORD_UPDATE(x,  8,  8)
+#define RK3288_LVDS_CON_TTL_EN(x)	HIWORD_UPDATE(x,  6,  6)
+
+#define RK3568_GRF_VO_CON1		0X0364
+#define RK3568_RGB_DATA_BYPASS(v)	HIWORD_UPDATE(v, 6, 6)
+
+struct rockchip_rgb;
+
+struct rockchip_rgb_funcs {
+	void (*enable)(struct rockchip_rgb *rgb);
+	void (*disable)(struct rockchip_rgb *rgb);
+};
 
 struct rockchip_rgb {
+	u8 id;
 	struct device *dev;
-	struct drm_device *drm_dev;
+	struct drm_panel *panel;
 	struct drm_bridge *bridge;
+	struct drm_connector connector;
 	struct drm_encoder encoder;
-	int output_mode;
+	struct phy *phy;
+	struct regmap *grf;
+	bool data_sync_bypass;
+	const struct rockchip_rgb_funcs *funcs;
+	struct rockchip_drm_sub_dev sub_dev;
 };
 
+static inline struct rockchip_rgb *connector_to_rgb(struct drm_connector *c)
+{
+	return container_of(c, struct rockchip_rgb, connector);
+}
+
+static inline struct rockchip_rgb *encoder_to_rgb(struct drm_encoder *e)
+{
+	return container_of(e, struct rockchip_rgb, encoder);
+}
+
+static enum drm_connector_status
+rockchip_rgb_connector_detect(struct drm_connector *connector, bool force)
+{
+	return connector_status_connected;
+}
+
+static int
+rockchip_rgb_atomic_connector_get_property(struct drm_connector *connector,
+					   const struct drm_connector_state *state,
+					   struct drm_property *property,
+					   uint64_t *val)
+{
+	struct rockchip_rgb *rgb = connector_to_rgb(connector);
+	struct rockchip_drm_private *private = connector->dev->dev_private;
+
+	if (property == private->connector_id_prop) {
+		*val = rgb->id;
+		return 0;
+	}
+
+	DRM_ERROR("failed to get rockchip RGB property\n");
+	return -EINVAL;
+}
+
+static const struct drm_connector_funcs rockchip_rgb_connector_funcs = {
+	.detect = rockchip_rgb_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = drm_connector_cleanup,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+	.atomic_get_property = rockchip_rgb_atomic_connector_get_property,
+};
+
+static int rockchip_rgb_connector_get_modes(struct drm_connector *connector)
+{
+	struct rockchip_rgb *rgb = connector_to_rgb(connector);
+	struct drm_panel *panel = rgb->panel;
+
+	return drm_panel_get_modes(panel, connector);
+}
+
+static struct drm_encoder *
+rockchip_rgb_connector_best_encoder(struct drm_connector *connector)
+{
+	struct rockchip_rgb *rgb = connector_to_rgb(connector);
+
+	return &rgb->encoder;
+}
+
+static const
+struct drm_connector_helper_funcs rockchip_rgb_connector_helper_funcs = {
+	.get_modes = rockchip_rgb_connector_get_modes,
+	.best_encoder = rockchip_rgb_connector_best_encoder,
+};
+
+static void rockchip_rgb_encoder_enable(struct drm_encoder *encoder)
+{
+	struct rockchip_rgb *rgb = encoder_to_rgb(encoder);
+
+	pinctrl_pm_select_default_state(rgb->dev);
+
+	if (rgb->funcs && rgb->funcs->enable)
+		rgb->funcs->enable(rgb);
+
+	if (rgb->phy)
+		phy_power_on(rgb->phy);
+
+	if (rgb->panel) {
+		drm_panel_prepare(rgb->panel);
+		drm_panel_enable(rgb->panel);
+	}
+}
+
+static void rockchip_rgb_encoder_disable(struct drm_encoder *encoder)
+{
+	struct rockchip_rgb *rgb = encoder_to_rgb(encoder);
+
+	if (rgb->panel) {
+		drm_panel_disable(rgb->panel);
+		drm_panel_unprepare(rgb->panel);
+	}
+
+	if (rgb->phy)
+		phy_power_off(rgb->phy);
+
+	if (rgb->funcs && rgb->funcs->disable)
+		rgb->funcs->disable(rgb);
+
+	pinctrl_pm_select_sleep_state(rgb->dev);
+}
+
 static int
 rockchip_rgb_encoder_atomic_check(struct drm_encoder *encoder,
 				   struct drm_crtc_state *crtc_state,
@@ -37,128 +180,310 @@ rockchip_rgb_encoder_atomic_check(struct drm_encoder *encoder,
 	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
 	struct drm_connector *connector = conn_state->connector;
 	struct drm_display_info *info = &connector->display_info;
-	u32 bus_format;
 
 	if (info->num_bus_formats)
-		bus_format = info->bus_formats[0];
+		s->bus_format = info->bus_formats[0];
 	else
-		bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+		s->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
 
-	switch (bus_format) {
+	switch (s->bus_format) {
 	case MEDIA_BUS_FMT_RGB666_1X18:
 		s->output_mode = ROCKCHIP_OUT_MODE_P666;
+		s->output_if = VOP_OUTPUT_IF_RGB;
 		break;
 	case MEDIA_BUS_FMT_RGB565_1X16:
 		s->output_mode = ROCKCHIP_OUT_MODE_P565;
+		s->output_if = VOP_OUTPUT_IF_RGB;
+		break;
+	case MEDIA_BUS_FMT_RGB888_3X8:
+		s->output_mode = ROCKCHIP_OUT_MODE_S888;
+		s->output_if = VOP_OUTPUT_IF_RGB;
+		break;
+	case MEDIA_BUS_FMT_RGB888_DUMMY_4X8:
+		s->output_mode = ROCKCHIP_OUT_MODE_S888_DUMMY;
+		s->output_if = VOP_OUTPUT_IF_RGB;
+		break;
+	case MEDIA_BUS_FMT_YUYV8_2X8:
+	case MEDIA_BUS_FMT_YVYU8_2X8:
+	case MEDIA_BUS_FMT_UYVY8_2X8:
+	case MEDIA_BUS_FMT_VYUY8_2X8:
+		s->output_mode = ROCKCHIP_OUT_MODE_BT656;
+		s->output_if = VOP_OUTPUT_IF_BT656;
+		break;
+	case MEDIA_BUS_FMT_YUYV8_1X16:
+	case MEDIA_BUS_FMT_YVYU8_1X16:
+	case MEDIA_BUS_FMT_UYVY8_1X16:
+	case MEDIA_BUS_FMT_VYUY8_1X16:
+		s->output_mode = ROCKCHIP_OUT_MODE_BT1120;
+		s->output_if = VOP_OUTPUT_IF_BT1120;
 		break;
 	case MEDIA_BUS_FMT_RGB888_1X24:
 	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
 	default:
 		s->output_mode = ROCKCHIP_OUT_MODE_P888;
+		s->output_if = VOP_OUTPUT_IF_RGB;
 		break;
 	}
 
-	s->output_type = DRM_MODE_CONNECTOR_LVDS;
+	s->output_type = DRM_MODE_CONNECTOR_DPI;
+	s->bus_flags = info->bus_flags;
+	s->tv_state = &conn_state->tv;
+	s->eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR;
+	s->color_space = V4L2_COLORSPACE_DEFAULT;
 
 	return 0;
 }
 
+static void rockchip_rgb_encoder_loader_protect(struct drm_encoder *encoder,
+						bool on)
+{
+	struct rockchip_rgb *rgb = encoder_to_rgb(encoder);
+
+	if (rgb->panel)
+		panel_simple_loader_protect(rgb->panel);
+}
+
 static const
 struct drm_encoder_helper_funcs rockchip_rgb_encoder_helper_funcs = {
+	.enable = rockchip_rgb_encoder_enable,
+	.disable = rockchip_rgb_encoder_disable,
 	.atomic_check = rockchip_rgb_encoder_atomic_check,
 };
 
-struct rockchip_rgb *rockchip_rgb_init(struct device *dev,
-				       struct drm_crtc *crtc,
-				       struct drm_device *drm_dev)
+static const struct drm_encoder_funcs rockchip_rgb_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static int rockchip_rgb_bind(struct device *dev, struct device *master,
+			     void *data)
+{
+	struct rockchip_rgb *rgb = dev_get_drvdata(dev);
+	struct drm_device *drm_dev = data;
+	struct drm_encoder *encoder = &rgb->encoder;
+	struct drm_connector *connector;
+	int ret;
+
+	ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1,
+					  &rgb->panel, &rgb->bridge);
+	if (ret) {
+		DRM_DEV_ERROR(dev, "failed to find panel or bridge: %d\n", ret);
+		return ret;
+	}
+
+	encoder->possible_crtcs = rockchip_drm_of_find_possible_crtcs(drm_dev,
+								      dev->of_node);
+
+	ret = drm_encoder_init(drm_dev, encoder, &rockchip_rgb_encoder_funcs,
+			       DRM_MODE_ENCODER_DPI, NULL);
+	if (ret < 0) {
+		DRM_DEV_ERROR(dev, "failed to initialize encoder: %d\n", ret);
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &rockchip_rgb_encoder_helper_funcs);
+
+	if (rgb->panel) {
+		struct rockchip_drm_private *private = drm_dev->dev_private;
+
+		connector = &rgb->connector;
+		connector->interlace_allowed = true;
+		ret = drm_connector_init(drm_dev, connector,
+					 &rockchip_rgb_connector_funcs,
+					 DRM_MODE_CONNECTOR_DPI);
+		if (ret < 0) {
+			DRM_DEV_ERROR(dev,
+				      "failed to initialize connector: %d\n",
+				      ret);
+			goto err_free_encoder;
+		}
+
+		drm_connector_helper_add(connector,
+					 &rockchip_rgb_connector_helper_funcs);
+
+		ret = drm_connector_attach_encoder(connector, encoder);
+		if (ret < 0) {
+			DRM_DEV_ERROR(dev,
+				      "failed to attach encoder: %d\n", ret);
+			goto err_free_connector;
+		}
+		rgb->sub_dev.connector = &rgb->connector;
+		rgb->sub_dev.of_node = rgb->dev->of_node;
+		rgb->sub_dev.loader_protect = rockchip_rgb_encoder_loader_protect;
+		drm_object_attach_property(&connector->base, private->connector_id_prop, 0);
+		rockchip_drm_register_sub_dev(&rgb->sub_dev);
+	} else {
+		rgb->bridge->encoder = encoder;
+		ret = drm_bridge_attach(encoder, rgb->bridge, NULL, 0);
+		if (ret) {
+			DRM_DEV_ERROR(dev,
+				      "failed to attach bridge: %d\n", ret);
+			goto err_free_encoder;
+		}
+	}
+
+	return 0;
+
+err_free_connector:
+	drm_connector_cleanup(connector);
+err_free_encoder:
+	drm_encoder_cleanup(encoder);
+	return ret;
+}
+
+static void rockchip_rgb_unbind(struct device *dev, struct device *master,
+				void *data)
+{
+	struct rockchip_rgb *rgb = dev_get_drvdata(dev);
+
+	if (rgb->sub_dev.connector)
+		rockchip_drm_register_sub_dev(&rgb->sub_dev);
+	if (rgb->panel)
+		drm_connector_cleanup(&rgb->connector);
+
+	drm_encoder_cleanup(&rgb->encoder);
+}
+
+static const struct component_ops rockchip_rgb_component_ops = {
+	.bind = rockchip_rgb_bind,
+	.unbind = rockchip_rgb_unbind,
+};
+
+static int rockchip_rgb_probe(struct platform_device *pdev)
 {
+	struct device *dev = &pdev->dev;
 	struct rockchip_rgb *rgb;
-	struct drm_encoder *encoder;
-	struct device_node *port, *endpoint;
-	u32 endpoint_id;
-	int ret = 0, child_count = 0;
-	struct drm_panel *panel;
-	struct drm_bridge *bridge;
+	int ret, id;
 
-	rgb = devm_kzalloc(dev, sizeof(*rgb), GFP_KERNEL);
+	rgb = devm_kzalloc(&pdev->dev, sizeof(*rgb), GFP_KERNEL);
 	if (!rgb)
-		return ERR_PTR(-ENOMEM);
+		return -ENOMEM;
+
+	id = of_alias_get_id(dev->of_node, "rgb");
+	if (id < 0)
+		id = 0;
 
+	rgb->id = id;
 	rgb->dev = dev;
-	rgb->drm_dev = drm_dev;
-
-	port = of_graph_get_port_by_id(dev->of_node, 0);
-	if (!port)
-		return ERR_PTR(-EINVAL);
-
-	for_each_child_of_node(port, endpoint) {
-		if (of_property_read_u32(endpoint, "reg", &endpoint_id))
-			endpoint_id = 0;
-
-		/* if subdriver (> 0) or error case (< 0), ignore entry */
-		if (rockchip_drm_endpoint_is_subdriver(endpoint) != 0)
-			continue;
-
-		child_count++;
-		ret = drm_of_find_panel_or_bridge(dev->of_node, 0, endpoint_id,
-						  &panel, &bridge);
-		if (!ret) {
-			of_node_put(endpoint);
-			break;
+	rgb->funcs = of_device_get_match_data(dev);
+	platform_set_drvdata(pdev, rgb);
+
+	rgb->data_sync_bypass =
+	    of_property_read_bool(dev->of_node, "rockchip,data-sync-bypass");
+
+	if (dev->parent && dev->parent->of_node) {
+		rgb->grf = syscon_node_to_regmap(dev->parent->of_node);
+		if (IS_ERR(rgb->grf)) {
+			ret = PTR_ERR(rgb->grf);
+			dev_err(dev, "Unable to get grf: %d\n", ret);
+			return ret;
 		}
 	}
 
-	of_node_put(port);
+	rgb->phy = devm_phy_optional_get(dev, "phy");
+	if (IS_ERR(rgb->phy)) {
+		ret = PTR_ERR(rgb->phy);
+		dev_err(dev, "failed to get phy: %d\n", ret);
+		return ret;
+	}
 
-	/* if the rgb output is not connected to anything, just return */
-	if (!child_count)
-		return NULL;
+	return component_add(dev, &rockchip_rgb_component_ops);
+}
 
-	if (ret < 0) {
-		if (ret != -EPROBE_DEFER)
-			DRM_DEV_ERROR(dev, "failed to find panel or bridge %d\n", ret);
-		return ERR_PTR(ret);
-	}
+static int rockchip_rgb_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &rockchip_rgb_component_ops);
 
-	encoder = &rgb->encoder;
-	encoder->possible_crtcs = drm_crtc_mask(crtc);
+	return 0;
+}
 
-	ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_NONE);
-	if (ret < 0) {
-		DRM_DEV_ERROR(drm_dev->dev,
-			      "failed to initialize encoder: %d\n", ret);
-		return ERR_PTR(ret);
-	}
+static void px30_rgb_enable(struct rockchip_rgb *rgb)
+{
+	int pipe = drm_of_encoder_active_endpoint_id(rgb->dev->of_node,
+						     &rgb->encoder);
 
-	drm_encoder_helper_add(encoder, &rockchip_rgb_encoder_helper_funcs);
+	regmap_write(rgb->grf, PX30_GRF_PD_VO_CON1, PX30_RGB_VOP_SEL(pipe) |
+		     PX30_RGB_DATA_SYNC_BYPASS(rgb->data_sync_bypass));
+}
 
-	if (panel) {
-		bridge = drm_panel_bridge_add_typed(panel,
-						    DRM_MODE_CONNECTOR_LVDS);
-		if (IS_ERR(bridge))
-			return ERR_CAST(bridge);
-	}
+static const struct rockchip_rgb_funcs px30_rgb_funcs = {
+	.enable = px30_rgb_enable,
+};
 
-	rgb->bridge = bridge;
+static void rk1808_rgb_enable(struct rockchip_rgb *rgb)
+{
+	regmap_write(rgb->grf, RK1808_GRF_PD_VO_CON1,
+		     RK1808_RGB_DATA_SYNC_BYPASS(rgb->data_sync_bypass));
+}
 
-	ret = drm_bridge_attach(encoder, rgb->bridge, NULL, 0);
-	if (ret) {
-		DRM_DEV_ERROR(drm_dev->dev,
-			      "failed to attach bridge: %d\n", ret);
-		goto err_free_encoder;
-	}
+static const struct rockchip_rgb_funcs rk1808_rgb_funcs = {
+	.enable = rk1808_rgb_enable,
+};
 
-	return rgb;
+static void rk3288_rgb_enable(struct rockchip_rgb *rgb)
+{
+	int pipe = drm_of_encoder_active_endpoint_id(rgb->dev->of_node,
+						     &rgb->encoder);
 
-err_free_encoder:
-	drm_encoder_cleanup(encoder);
-	return ERR_PTR(ret);
+	regmap_write(rgb->grf, RK3288_GRF_SOC_CON6, RK3288_LVDS_LCDC_SEL(pipe));
+	regmap_write(rgb->grf, RK3288_GRF_SOC_CON7,
+		     RK3288_LVDS_PWRDWN(0) | RK3288_LVDS_CON_ENABLE_2(1) |
+		     RK3288_LVDS_CON_ENABLE_1(1) | RK3288_LVDS_CON_CLKINV(0) |
+		     RK3288_LVDS_CON_TTL_EN(1));
 }
-EXPORT_SYMBOL_GPL(rockchip_rgb_init);
 
-void rockchip_rgb_fini(struct rockchip_rgb *rgb)
+static void rk3288_rgb_disable(struct rockchip_rgb *rgb)
 {
-	drm_panel_bridge_remove(rgb->bridge);
-	drm_encoder_cleanup(&rgb->encoder);
+	regmap_write(rgb->grf, RK3288_GRF_SOC_CON7,
+		     RK3288_LVDS_PWRDWN(1) | RK3288_LVDS_CON_ENABLE_2(0) |
+		     RK3288_LVDS_CON_ENABLE_1(0) | RK3288_LVDS_CON_TTL_EN(0));
 }
-EXPORT_SYMBOL_GPL(rockchip_rgb_fini);
+
+static const struct rockchip_rgb_funcs rk3288_rgb_funcs = {
+	.enable = rk3288_rgb_enable,
+	.disable = rk3288_rgb_disable,
+};
+
+static void rk3568_rgb_enable(struct rockchip_rgb *rgb)
+{
+	regmap_write(rgb->grf, RK3568_GRF_VO_CON1,
+		     RK3568_RGB_DATA_BYPASS(rgb->data_sync_bypass));
+}
+
+static const struct rockchip_rgb_funcs rk3568_rgb_funcs = {
+	.enable = rk3568_rgb_enable,
+};
+
+static void rv1126_rgb_enable(struct rockchip_rgb *rgb)
+{
+	regmap_write(rgb->grf, RV1126_GRF_IOFUNC_CON3,
+		     RV1126_LCDC_IO_BYPASS(rgb->data_sync_bypass));
+}
+
+static const struct rockchip_rgb_funcs rv1126_rgb_funcs = {
+	.enable = rv1126_rgb_enable,
+};
+
+static const struct of_device_id rockchip_rgb_dt_ids[] = {
+	{ .compatible = "rockchip,px30-rgb", .data = &px30_rgb_funcs },
+	{ .compatible = "rockchip,rk1808-rgb", .data = &rk1808_rgb_funcs },
+	{ .compatible = "rockchip,rk3066-rgb", },
+	{ .compatible = "rockchip,rk3128-rgb", },
+	{ .compatible = "rockchip,rk3288-rgb", .data = &rk3288_rgb_funcs },
+	{ .compatible = "rockchip,rk3308-rgb", },
+	{ .compatible = "rockchip,rk3368-rgb", },
+	{ .compatible = "rockchip,rk3568-rgb", .data = &rk3568_rgb_funcs },
+	{ .compatible = "rockchip,rk3588-rgb", },
+	{ .compatible = "rockchip,rv1108-rgb", },
+	{ .compatible = "rockchip,rv1126-rgb", .data = &rv1126_rgb_funcs},
+	{}
+};
+MODULE_DEVICE_TABLE(of, rockchip_rgb_dt_ids);
+
+struct platform_driver rockchip_rgb_driver = {
+	.probe = rockchip_rgb_probe,
+	.remove = rockchip_rgb_remove,
+	.driver = {
+		.name = "rockchip-rgb",
+		.of_match_table = of_match_ptr(rockchip_rgb_dt_ids),
+	},
+};
diff --git a/drivers/gpu/drm/rockchip/rockchip_rgb.h b/drivers/gpu/drm/rockchip/rockchip_rgb.h
index 27b963512..8973232e0 100644
--- a/drivers/gpu/drm/rockchip/rockchip_rgb.h
+++ b/drivers/gpu/drm/rockchip/rockchip_rgb.h
@@ -8,12 +8,14 @@
 #ifdef CONFIG_ROCKCHIP_RGB
 struct rockchip_rgb *rockchip_rgb_init(struct device *dev,
 				       struct drm_crtc *crtc,
-				       struct drm_device *drm_dev);
+				       struct drm_device *drm_dev,
+				       u32 port_id);
 void rockchip_rgb_fini(struct rockchip_rgb *rgb);
 #else
 static inline struct rockchip_rgb *rockchip_rgb_init(struct device *dev,
 						     struct drm_crtc *crtc,
-						     struct drm_device *drm_dev)
+						     struct drm_device *drm_dev,
+						     u32 port_id)
 {
 	return NULL;
 }
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 39e1e1ebe..f33e7d1be 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -5,36 +5,36 @@
  */
 
 #include <linux/component.h>
-#include <linux/mod_devicetable.h>
-#include <linux/module.h>
+#include <linux/kernel.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
 
 #include <drm/drm_fourcc.h>
-#include <drm/drm_plane.h>
 #include <drm/drm_print.h>
 
 #include "rockchip_drm_vop.h"
 #include "rockchip_vop_reg.h"
-#include "rockchip_drm_drv.h"
 
-#define _VOP_REG(off, _mask, _shift, _write_mask, _relaxed) \
-		{ \
-		 .offset = off, \
+#define VOP_REG_VER_MASK(off, _mask, s, _write_mask, _major, \
+			 _begin_minor, _end_minor) \
+		{.offset = off, \
 		 .mask = _mask, \
-		 .shift = _shift, \
+		 .shift = s, \
 		 .write_mask = _write_mask, \
-		 .relaxed = _relaxed, \
-		}
+		 .major = _major, \
+		 .begin_minor = _begin_minor, \
+		 .end_minor = _end_minor,}
 
-#define VOP_REG(off, _mask, _shift) \
-		_VOP_REG(off, _mask, _shift, false, true)
+#define VOP_REG(off, _mask, s) \
+		VOP_REG_VER_MASK(off, _mask, s, false, 0, 0, -1)
 
-#define VOP_REG_SYNC(off, _mask, _shift) \
-		_VOP_REG(off, _mask, _shift, false, false)
+#define VOP_REG_MASK(off, _mask, s) \
+		VOP_REG_VER_MASK(off, _mask, s, true, 0, 0, -1)
+
+#define VOP_REG_VER(off, _mask, s, _major, _begin_minor, _end_minor) \
+		VOP_REG_VER_MASK(off, _mask, s, false, \
+				 _major, _begin_minor, _end_minor)
 
-#define VOP_REG_MASK_SYNC(off, _mask, _shift) \
-		_VOP_REG(off, _mask, _shift, true, false)
 
 static const uint32_t formats_win_full[] = {
 	DRM_FORMAT_XRGB8888,
@@ -50,15 +50,46 @@ static const uint32_t formats_win_full[] = {
 	DRM_FORMAT_NV24,
 };
 
-static const uint64_t format_modifiers_win_full[] = {
-	DRM_FORMAT_MOD_LINEAR,
-	DRM_FORMAT_MOD_INVALID,
+static const uint32_t formats_win_full_10bit[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565,
+	DRM_FORMAT_NV12,
+	DRM_FORMAT_NV16,
+	DRM_FORMAT_NV24,
+	DRM_FORMAT_NV15, /* yuv420_10bit linear mode, 2 plane, no padding */
+#ifdef CONFIG_NO_GKI
+	DRM_FORMAT_NV20, /* yuv422_10bit linear mode, 2 plane, no padding */
+	DRM_FORMAT_NV30, /* yuv444_10bit linear mode, 2 plane, no padding */
+#endif
 };
 
-static const uint64_t format_modifiers_win_full_afbc[] = {
-	ROCKCHIP_AFBC_MOD,
-	DRM_FORMAT_MOD_LINEAR,
-	DRM_FORMAT_MOD_INVALID,
+static const uint32_t formats_win_full_10bit_yuyv[] = {
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_ABGR8888,
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565,
+	DRM_FORMAT_NV12, /* yuv420_8bit linear mode, 2 plane */
+	DRM_FORMAT_NV16, /* yuv422_8bit linear mode, 2 plane */
+	DRM_FORMAT_NV24, /* yuv444_8bit linear mode, 2 plane */
+	DRM_FORMAT_NV15, /* yuv420_10bit linear mode, 2 plane, no padding */
+#ifdef CONFIG_NO_GKI
+	DRM_FORMAT_NV20, /* yuv422_10bit linear mode, 2 plane, no padding */
+	DRM_FORMAT_NV30, /* yuv444_10bit linear mode, 2 plane, no padding */
+#endif
+	DRM_FORMAT_YVYU, /* yuv422_8bit[YVYU] linear mode or non-Linear mode */
+	DRM_FORMAT_VYUY, /* yuv422_8bit[VYUY] linear mode or non-Linear mode */
+	DRM_FORMAT_YUYV, /* yuv422_8bit[YUYV] linear mode or non-Linear mode */
+	DRM_FORMAT_UYVY, /* yuv422_8bit[UYVY] linear mode or non-Linear mode */
 };
 
 static const uint32_t formats_win_lite[] = {
@@ -72,524 +103,571 @@ static const uint32_t formats_win_lite[] = {
 	DRM_FORMAT_BGR565,
 };
 
-static const uint64_t format_modifiers_win_lite[] = {
+static const uint64_t format_modifiers[] = {
 	DRM_FORMAT_MOD_LINEAR,
 	DRM_FORMAT_MOD_INVALID,
 };
 
-static const struct vop_scl_regs rk3036_win_scl = {
-	.scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
-	.scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
-	.scale_cbcr_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
-	.scale_cbcr_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
+static const uint64_t format_modifiers_afbc[] = {
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_SPARSE),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_YTR),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_CBR),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_YTR |
+				AFBC_FORMAT_MOD_SPARSE),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_CBR |
+				AFBC_FORMAT_MOD_SPARSE),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_YTR |
+				AFBC_FORMAT_MOD_CBR),
+
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_YTR |
+				AFBC_FORMAT_MOD_CBR |
+				AFBC_FORMAT_MOD_SPARSE),
+
+	/* SPLIT mandates SPARSE, RGB modes mandates YTR */
+	DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 |
+				AFBC_FORMAT_MOD_YTR |
+				AFBC_FORMAT_MOD_SPARSE |
+				AFBC_FORMAT_MOD_SPLIT),
+
+	DRM_FORMAT_MOD_LINEAR,
+	DRM_FORMAT_MOD_INVALID,
 };
 
-static const struct vop_win_phy rk3036_win0_data = {
-	.scl = &rk3036_win_scl,
-	.data_formats = formats_win_full,
-	.nformats = ARRAY_SIZE(formats_win_full),
-	.format_modifiers = format_modifiers_win_full,
-	.enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 0),
-	.format = VOP_REG(RK3036_SYS_CTRL, 0x7, 3),
-	.rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 15),
-	.act_info = VOP_REG(RK3036_WIN0_ACT_INFO, 0x1fff1fff, 0),
-	.dsp_info = VOP_REG(RK3036_WIN0_DSP_INFO, 0x0fff0fff, 0),
-	.dsp_st = VOP_REG(RK3036_WIN0_DSP_ST, 0x1fff1fff, 0),
-	.yrgb_mst = VOP_REG(RK3036_WIN0_YRGB_MST, 0xffffffff, 0),
-	.uv_mst = VOP_REG(RK3036_WIN0_CBR_MST, 0xffffffff, 0),
-	.yrgb_vir = VOP_REG(RK3036_WIN0_VIR, 0xffff, 0),
-	.uv_vir = VOP_REG(RK3036_WIN0_VIR, 0x1fff, 16),
+static const struct vop_scl_extension rk3288_win_full_scl_ext = {
+	.cbcr_vsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 31),
+	.cbcr_vsu_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 30),
+	.cbcr_hsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 28),
+	.cbcr_ver_scl_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 26),
+	.cbcr_hor_scl_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 24),
+	.yrgb_vsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 23),
+	.yrgb_vsu_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 22),
+	.yrgb_hsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 20),
+	.yrgb_ver_scl_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 18),
+	.yrgb_hor_scl_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 16),
+	.line_load_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 15),
+	.cbcr_axi_gather_num = VOP_REG(RK3288_WIN0_CTRL1, 0x7, 12),
+	.yrgb_axi_gather_num = VOP_REG(RK3288_WIN0_CTRL1, 0xf, 8),
+	.vsd_cbcr_gt2 = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 7),
+	.vsd_cbcr_gt4 = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 6),
+	.vsd_yrgb_gt2 = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 5),
+	.vsd_yrgb_gt4 = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 4),
+	.bic_coe_sel = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 2),
+	.cbcr_axi_gather_en = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 1),
+	.yrgb_axi_gather_en = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 0),
+	.lb_mode = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 5),
 };
 
-static const struct vop_win_phy rk3036_win1_data = {
+static const struct vop_scl_regs rk3288_win_full_scl = {
+	.ext = &rk3288_win_full_scl_ext,
+	.scale_yrgb_x = VOP_REG(RK3288_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
+	.scale_yrgb_y = VOP_REG(RK3288_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
+	.scale_cbcr_x = VOP_REG(RK3288_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
+	.scale_cbcr_y = VOP_REG(RK3288_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
+};
+
+static const struct vop_win_phy rk3288_win01_data = {
+	.scl = &rk3288_win_full_scl,
+	.data_formats = formats_win_full_10bit,
+	.nformats = ARRAY_SIZE(formats_win_full_10bit),
+	.enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0),
+	.format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1),
+	.fmt_10 = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 4),
+	.csc_mode = VOP_REG_VER(RK3288_WIN0_CTRL0, 0x3, 10, 3, 2, -1),
+	.rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12),
+	.xmirror = VOP_REG_VER(RK3368_WIN0_CTRL0, 0x1, 21, 3, 2, -1),
+	.ymirror = VOP_REG_VER(RK3368_WIN0_CTRL0, 0x1, 22, 3, 2, -1),
+	.act_info = VOP_REG(RK3288_WIN0_ACT_INFO, 0x1fff1fff, 0),
+	.dsp_info = VOP_REG(RK3288_WIN0_DSP_INFO, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3288_WIN0_DSP_ST, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3288_WIN0_YRGB_MST, 0xffffffff, 0),
+	.uv_mst = VOP_REG(RK3288_WIN0_CBR_MST, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3288_WIN0_VIR, 0x3fff, 0),
+	.uv_vir = VOP_REG(RK3288_WIN0_VIR, 0x3fff, 16),
+	.src_alpha_ctl = VOP_REG(RK3288_WIN0_SRC_ALPHA_CTRL, 0xffff, 0),
+	.global_alpha_val = VOP_REG(RK3288_WIN0_SRC_ALPHA_CTRL, 0xff, 16),
+	.dst_alpha_ctl = VOP_REG(RK3288_WIN0_DST_ALPHA_CTRL, 0xffffffff, 0),
+	.channel = VOP_REG_VER(RK3288_WIN0_CTRL2, 0xff, 0, 3, 8, 8),
+};
+
+static const struct vop_win_phy rk3288_win23_data = {
 	.data_formats = formats_win_lite,
 	.nformats = ARRAY_SIZE(formats_win_lite),
-	.format_modifiers = format_modifiers_win_lite,
-	.enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1),
-	.format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6),
-	.rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19),
-	.act_info = VOP_REG(RK3036_WIN1_ACT_INFO, 0x1fff1fff, 0),
-	.dsp_info = VOP_REG(RK3036_WIN1_DSP_INFO, 0x0fff0fff, 0),
-	.dsp_st = VOP_REG(RK3036_WIN1_DSP_ST, 0x1fff1fff, 0),
-	.yrgb_mst = VOP_REG(RK3036_WIN1_MST, 0xffffffff, 0),
-	.yrgb_vir = VOP_REG(RK3036_WIN1_VIR, 0xffff, 0),
+	.gate = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 0),
+	.enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 4),
+	.format = VOP_REG(RK3288_WIN2_CTRL0, 0x7, 1),
+	.rb_swap = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 12),
+	.dsp_info = VOP_REG(RK3288_WIN2_DSP_INFO0, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3288_WIN2_DSP_ST0, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3288_WIN2_MST0, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3288_WIN2_VIR0_1, 0x1fff, 0),
+	.src_alpha_ctl = VOP_REG(RK3288_WIN2_SRC_ALPHA_CTRL, 0xffff, 0),
+	.global_alpha_val = VOP_REG(RK3288_WIN2_SRC_ALPHA_CTRL, 0xff, 16),
+	.dst_alpha_ctl = VOP_REG(RK3288_WIN2_DST_ALPHA_CTRL, 0xffffffff, 0),
 };
 
-static const struct vop_win_data rk3036_vop_win_data[] = {
-	{ .base = 0x00, .phy = &rk3036_win0_data,
-	  .type = DRM_PLANE_TYPE_PRIMARY },
-	{ .base = 0x00, .phy = &rk3036_win1_data,
-	  .type = DRM_PLANE_TYPE_CURSOR },
+static const struct vop_win_phy rk3288_area1_data = {
+	.enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 5),
+	.dsp_info = VOP_REG(RK3288_WIN2_DSP_INFO1, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3288_WIN2_DSP_ST1, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3288_WIN2_MST1, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3288_WIN2_VIR0_1, 0x1fff, 16),
 };
 
-static const int rk3036_vop_intrs[] = {
-	DSP_HOLD_VALID_INTR,
-	FS_INTR,
-	LINE_FLAG_INTR,
-	BUS_ERROR_INTR,
+static const struct vop_win_phy rk3288_area2_data = {
+	.enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 6),
+	.dsp_info = VOP_REG(RK3288_WIN2_DSP_INFO2, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3288_WIN2_DSP_ST2, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3288_WIN2_MST2, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3288_WIN2_VIR2_3, 0x1fff, 0),
 };
 
-static const struct vop_intr rk3036_intr = {
-	.intrs = rk3036_vop_intrs,
-	.nintrs = ARRAY_SIZE(rk3036_vop_intrs),
-	.line_flag_num[0] = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
-	.status = VOP_REG_SYNC(RK3036_INT_STATUS, 0xf, 0),
-	.enable = VOP_REG_SYNC(RK3036_INT_STATUS, 0xf, 4),
-	.clear = VOP_REG_SYNC(RK3036_INT_STATUS, 0xf, 8),
+static const struct vop_win_phy rk3288_area3_data = {
+	.enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 7),
+	.dsp_info = VOP_REG(RK3288_WIN2_DSP_INFO3, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3288_WIN2_DSP_ST3, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3288_WIN2_MST3, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3288_WIN2_VIR2_3, 0x1fff, 16),
 };
 
-static const struct vop_modeset rk3036_modeset = {
-	.htotal_pw = VOP_REG(RK3036_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
-	.hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
-	.vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
-	.vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
+static const struct vop_win_phy *rk3288_area_data[] = {
+	&rk3288_area1_data,
+	&rk3288_area2_data,
+	&rk3288_area3_data
 };
 
-static const struct vop_output rk3036_output = {
-	.pin_pol = VOP_REG(RK3036_DSP_CTRL0, 0xf, 4),
+static const struct vop_ctrl rk3288_ctrl_data = {
+	.version = VOP_REG(RK3288_VERSION_INFO, 0xffff, 16),
+	.standby = VOP_REG(RK3288_SYS_CTRL, 0x1, 22),
+	.dma_stop = VOP_REG(RK3288_SYS_CTRL, 0x1, 21),
+	.axi_outstanding_max_num = VOP_REG(RK3288_SYS_CTRL1, 0x1f, 13),
+	.axi_max_outstanding_en = VOP_REG(RK3288_SYS_CTRL1, 0x1, 12),
+	.reg_done_frm = VOP_REG_VER(RK3288_SYS_CTRL1, 0x1, 24, 3, 5, -1),
+	.htotal_pw = VOP_REG(RK3288_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
+	.hact_st_end = VOP_REG(RK3288_DSP_HACT_ST_END, 0x1fff1fff, 0),
+	.vtotal_pw = VOP_REG(RK3288_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
+	.vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
+	.vact_st_end_f1 = VOP_REG(RK3288_DSP_VACT_ST_END_F1, 0x1fff1fff, 0),
+	.vs_st_end_f1 = VOP_REG(RK3288_DSP_VS_ST_END_F1, 0x1fff1fff, 0),
+	.hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
+	.vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+	.vpost_st_end_f1 = VOP_REG(RK3288_POST_DSP_VACT_INFO_F1, 0x1fff1fff, 0),
+	.post_scl_factor = VOP_REG(RK3288_POST_SCL_FACTOR_YRGB, 0xffffffff, 0),
+	.post_scl_ctrl = VOP_REG(RK3288_POST_SCL_CTRL, 0x3, 0),
+
+	.dsp_interlace = VOP_REG(RK3288_DSP_CTRL0, 0x1, 10),
+	.auto_gate_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 23),
+	.dsp_layer_sel = VOP_REG(RK3288_DSP_CTRL1, 0xff, 8),
+	.post_lb_mode = VOP_REG_VER(RK3288_SYS_CTRL, 0x1, 18, 3, 2, -1),
+	.global_regdone_en = VOP_REG_VER(RK3288_SYS_CTRL, 0x1, 11, 3, 2, -1),
+	.overlay_mode = VOP_REG_VER(RK3288_SYS_CTRL, 0x1, 16, 3, 2, -1),
+	.core_dclk_div = VOP_REG_VER(RK3366_DSP_CTRL0, 0x1, 4, 3, 4, -1),
+	.p2i_en = VOP_REG_VER(RK3366_DSP_CTRL0, 0x1, 5, 3, 4, -1),
+	.dclk_ddr = VOP_REG_VER(RK3288_DSP_CTRL0, 0x1, 8, 3, 1, -1),
+	.dp_en = VOP_REG_VER(RK3399_SYS_CTRL, 0x1, 11, 3, 5, -1),
+	.hdmi_dclk_out_en = VOP_REG_VER(RK3288_SYS_CTRL, 0x1, 11, 3, 1, 1),
+	.rgb_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 12),
+	.hdmi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 13),
+	.edp_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 14),
+	.mipi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 15),
+	.mipi_dual_channel_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 3),
+	.data01_swap = VOP_REG_VER(RK3288_SYS_CTRL, 0x1, 17, 3, 5, -1),
+	.dclk_pol = VOP_REG_VER(RK3288_DSP_CTRL0, 0x1, 7, 3, 0, 1),
+	.pin_pol = VOP_REG_VER(RK3288_DSP_CTRL0, 0x7, 4, 3, 0, 1),
+	.dp_dclk_pol = VOP_REG_VER(RK3399_DSP_CTRL1, 0x1, 19, 3, 5, -1),
+	.dp_pin_pol = VOP_REG_VER(RK3399_DSP_CTRL1, 0x7, 16, 3, 5, -1),
+	.rgb_dclk_pol = VOP_REG_VER(RK3368_DSP_CTRL1, 0x1, 19, 3, 2, -1),
+	.rgb_pin_pol = VOP_REG_VER(RK3368_DSP_CTRL1, 0x7, 16, 3, 2, -1),
+	.tve_dclk_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 24),
+	.tve_dclk_pol = VOP_REG(RK3288_SYS_CTRL, 0x1, 25),
+	.tve_sw_mode = VOP_REG(RK3288_SYS_CTRL, 0x1, 26),
+	.sw_uv_offset_en  = VOP_REG(RK3288_SYS_CTRL, 0x1, 27),
+	.sw_genlock   = VOP_REG(RK3288_SYS_CTRL, 0x1, 28),
+	.hdmi_dclk_pol = VOP_REG_VER(RK3368_DSP_CTRL1, 0x1, 23, 3, 2, -1),
+	.hdmi_pin_pol = VOP_REG_VER(RK3368_DSP_CTRL1, 0x7, 20, 3, 2, -1),
+	.edp_dclk_pol = VOP_REG_VER(RK3368_DSP_CTRL1, 0x1, 27, 3, 2, -1),
+	.edp_pin_pol = VOP_REG_VER(RK3368_DSP_CTRL1, 0x7, 24, 3, 2, -1),
+	.mipi_dclk_pol = VOP_REG_VER(RK3368_DSP_CTRL1, 0x1, 31, 3, 2, -1),
+	.mipi_pin_pol = VOP_REG_VER(RK3368_DSP_CTRL1, 0x7, 28, 3, 2, -1),
+
+	.dither_down_sel = VOP_REG(RK3288_DSP_CTRL1, 0x1, 4),
+	.dither_down_mode = VOP_REG(RK3288_DSP_CTRL1, 0x1, 3),
+	.dither_down_en = VOP_REG(RK3288_DSP_CTRL1, 0x1, 2),
+	.pre_dither_down_en = VOP_REG(RK3288_DSP_CTRL1, 0x1, 1),
+	.dither_up_en = VOP_REG(RK3288_DSP_CTRL1, 0x1, 6),
+
+	.dsp_out_yuv = VOP_REG_VER(RK3399_POST_SCL_CTRL, 0x1, 2, 3, 5, -1),
+	.dsp_data_swap = VOP_REG(RK3288_DSP_CTRL0, 0x1f, 12),
+	.dsp_ccir656_avg = VOP_REG(RK3288_DSP_CTRL0, 0x1, 20),
+	.dsp_blank = VOP_REG(RK3288_DSP_CTRL0, 0x3, 18),
+	.update_gamma_lut = VOP_REG_VER(RK3288_DSP_CTRL1, 0x1, 7, 3, 5, -1),
+	.lut_buffer_index = VOP_REG_VER(RK3399_DBG_POST_REG1, 0x1, 1, 3, 5, -1),
+	.dsp_lut_en = VOP_REG(RK3288_DSP_CTRL1, 0x1, 0),
+	.out_mode = VOP_REG(RK3288_DSP_CTRL0, 0xf, 0),
+
+	.afbdc_rstn = VOP_REG_VER(RK3399_AFBCD0_CTRL, 0x1, 3, 3, 5, -1),
+	.afbdc_en = VOP_REG_VER(RK3399_AFBCD0_CTRL, 0x1, 0, 3, 5, -1),
+	.afbdc_sel = VOP_REG_VER(RK3399_AFBCD0_CTRL, 0x3, 1, 3, 5, -1),
+	.afbdc_format = VOP_REG_VER(RK3399_AFBCD0_CTRL, 0x1f, 16, 3, 5, -1),
+	.afbdc_hreg_block_split = VOP_REG_VER(RK3399_AFBCD0_CTRL,
+					      0x1, 21, 3, 5, -1),
+	.afbdc_hdr_ptr = VOP_REG_VER(RK3399_AFBCD0_HDR_PTR, 0xffffffff,
+				     0, 3, 5, -1),
+	.afbdc_pic_size = VOP_REG_VER(RK3399_AFBCD0_PIC_SIZE, 0xffffffff,
+				      0, 3, 5, -1),
+	.bcsh_brightness = VOP_REG(RK3288_BCSH_BCS, 0xff, 0),
+	.bcsh_contrast = VOP_REG(RK3288_BCSH_BCS, 0x1ff, 8),
+	.bcsh_sat_con = VOP_REG(RK3288_BCSH_BCS, 0x3ff, 20),
+	.bcsh_out_mode = VOP_REG(RK3288_BCSH_BCS, 0x3, 30),
+	.bcsh_sin_hue = VOP_REG(RK3288_BCSH_H, 0x1ff, 0),
+	.bcsh_cos_hue = VOP_REG(RK3288_BCSH_H, 0x1ff, 16),
+	.bcsh_r2y_csc_mode = VOP_REG_VER(RK3368_BCSH_CTRL, 0x1, 6, 3, 1, -1),
+	.bcsh_r2y_en = VOP_REG_VER(RK3368_BCSH_CTRL, 0x1, 4, 3, 1, -1),
+	.bcsh_y2r_csc_mode = VOP_REG_VER(RK3368_BCSH_CTRL, 0x3, 2, 3, 1, -1),
+	.bcsh_y2r_en = VOP_REG_VER(RK3368_BCSH_CTRL, 0x1, 0, 3, 1, -1),
+	.bcsh_color_bar = VOP_REG(RK3288_BCSH_COLOR_BAR, 0xffffff, 8),
+	.bcsh_en = VOP_REG(RK3288_BCSH_COLOR_BAR, 0x1, 0),
+
+	.xmirror = VOP_REG(RK3288_DSP_CTRL0, 0x1, 22),
+	.ymirror = VOP_REG(RK3288_DSP_CTRL0, 0x1, 23),
+
+	.dsp_background = VOP_REG(RK3288_DSP_BG, 0xffffffff, 0),
+
+	.cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
 };
 
-static const struct vop_common rk3036_common = {
-	.standby = VOP_REG_SYNC(RK3036_SYS_CTRL, 0x1, 30),
-	.out_mode = VOP_REG(RK3036_DSP_CTRL0, 0xf, 0),
-	.dsp_blank = VOP_REG(RK3036_DSP_CTRL1, 0x1, 24),
-	.dither_down_sel = VOP_REG(RK3036_DSP_CTRL0, 0x1, 27),
-	.dither_down_en = VOP_REG(RK3036_DSP_CTRL0, 0x1, 11),
-	.dither_down_mode = VOP_REG(RK3036_DSP_CTRL0, 0x1, 10),
-	.cfg_done = VOP_REG_SYNC(RK3036_REG_CFG_DONE, 0x1, 0),
+/*
+ * Note: rk3288 has a dedicated 'cursor' window, however, that window requires
+ * special support to get alpha blending working.  For now, just use overlay
+ * window 3 for the drm cursor.
+ *
+ */
+static const struct vop_win_data rk3288_vop_win_data[] = {
+	{ .base = 0x00, .phy = &rk3288_win01_data,
+	  .type = DRM_PLANE_TYPE_PRIMARY },
+	{ .base = 0x40, .phy = &rk3288_win01_data,
+	  .type = DRM_PLANE_TYPE_OVERLAY },
+	{ .base = 0x00, .phy = &rk3288_win23_data,
+	  .type = DRM_PLANE_TYPE_OVERLAY,
+	  .area = rk3288_area_data,
+	  .area_size = ARRAY_SIZE(rk3288_area_data), },
+	{ .base = 0x50, .phy = &rk3288_win23_data,
+	  .type = DRM_PLANE_TYPE_CURSOR,
+	  .area = rk3288_area_data,
+	  .area_size = ARRAY_SIZE(rk3288_area_data), },
 };
 
-static const struct vop_data rk3036_vop = {
-	.intr = &rk3036_intr,
-	.common = &rk3036_common,
-	.modeset = &rk3036_modeset,
-	.output = &rk3036_output,
-	.win = rk3036_vop_win_data,
-	.win_size = ARRAY_SIZE(rk3036_vop_win_data),
+static const int rk3288_vop_intrs[] = {
+	DSP_HOLD_VALID_INTR,
+	FS_INTR,
+	LINE_FLAG_INTR,
+	BUS_ERROR_INTR,
 };
 
-static const struct vop_win_phy rk3126_win1_data = {
-	.data_formats = formats_win_lite,
-	.nformats = ARRAY_SIZE(formats_win_lite),
-	.format_modifiers = format_modifiers_win_lite,
-	.enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1),
-	.format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6),
-	.rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19),
-	.dsp_info = VOP_REG(RK3126_WIN1_DSP_INFO, 0x0fff0fff, 0),
-	.dsp_st = VOP_REG(RK3126_WIN1_DSP_ST, 0x1fff1fff, 0),
-	.yrgb_mst = VOP_REG(RK3126_WIN1_MST, 0xffffffff, 0),
-	.yrgb_vir = VOP_REG(RK3036_WIN1_VIR, 0xffff, 0),
+static const struct vop_intr rk3288_vop_intr = {
+	.intrs = rk3288_vop_intrs,
+	.nintrs = ARRAY_SIZE(rk3288_vop_intrs),
+	.line_flag_num[0] = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
+	.status = VOP_REG(RK3288_INTR_CTRL0, 0xf, 0),
+	.enable = VOP_REG(RK3288_INTR_CTRL0, 0xf, 4),
+	.clear = VOP_REG(RK3288_INTR_CTRL0, 0xf, 8),
 };
 
-static const struct vop_win_data rk3126_vop_win_data[] = {
-	{ .base = 0x00, .phy = &rk3036_win0_data,
-	  .type = DRM_PLANE_TYPE_PRIMARY },
-	{ .base = 0x00, .phy = &rk3126_win1_data,
-	  .type = DRM_PLANE_TYPE_CURSOR },
+static const struct vop_grf_ctrl rk3288_vop_big_grf_ctrl = {
+	.grf_dclk_inv = VOP_REG(RK3288_GRF_SOC_CON15, 0x1, 13),
 };
 
-static const struct vop_data rk3126_vop = {
-	.intr = &rk3036_intr,
-	.common = &rk3036_common,
-	.modeset = &rk3036_modeset,
-	.output = &rk3036_output,
-	.win = rk3126_vop_win_data,
-	.win_size = ARRAY_SIZE(rk3126_vop_win_data),
+static const struct vop_grf_ctrl rk3288_vop_lit_grf_ctrl = {
+	.grf_dclk_inv = VOP_REG(RK3288_GRF_SOC_CON15, 0x1, 15),
+};
+
+static const struct vop_data rk3288_vop_big = {
+	.soc_id = 0x3288,
+	.vop_id = 0,
+	.version = VOP_VERSION(3, 0),
+	.feature = VOP_FEATURE_OUTPUT_10BIT | VOP_FEATURE_ALPHA_SCALE | VOP_FEATURE_OVERSCAN,
+	.max_input = {4096, 8192},
+	.max_output = {3840, 2160},
+	.intr = &rk3288_vop_intr,
+	.grf_ctrl = &rk3288_vop_big_grf_ctrl,
+	.ctrl = &rk3288_ctrl_data,
+	.win = rk3288_vop_win_data,
+	.win_size = ARRAY_SIZE(rk3288_vop_win_data),
+};
+
+static const struct vop_data rk3288_vop_lit = {
+	.soc_id = 0x3288,
+	.vop_id = 1,
+	.version = VOP_VERSION(3, 0),
+	.feature = VOP_FEATURE_OUTPUT_10BIT | VOP_FEATURE_ALPHA_SCALE | VOP_FEATURE_OVERSCAN,
+	.max_input = {4096, 8192},
+	.max_output = {2560, 1600},
+	.intr = &rk3288_vop_intr,
+	.grf_ctrl = &rk3288_vop_lit_grf_ctrl,
+	.ctrl = &rk3288_ctrl_data,
+	.win = rk3288_vop_win_data,
+	.win_size = ARRAY_SIZE(rk3288_vop_win_data),
 };
 
-static const int px30_vop_intrs[] = {
+static const int rk3368_vop_intrs[] = {
 	FS_INTR,
-	0, 0,
+	FS_NEW_INTR,
+	ADDR_SAME_INTR,
 	LINE_FLAG_INTR,
-	0,
+	LINE_FLAG1_INTR,
 	BUS_ERROR_INTR,
-	0, 0,
+	WIN0_EMPTY_INTR,
+	WIN1_EMPTY_INTR,
+	WIN2_EMPTY_INTR,
+	WIN3_EMPTY_INTR,
+	HWC_EMPTY_INTR,
+	POST_BUF_EMPTY_INTR,
+	FS_FIELD_INTR,
 	DSP_HOLD_VALID_INTR,
 };
 
-static const struct vop_intr px30_intr = {
-	.intrs = px30_vop_intrs,
-	.nintrs = ARRAY_SIZE(px30_vop_intrs),
-	.line_flag_num[0] = VOP_REG(PX30_LINE_FLAG, 0xfff, 0),
-	.status = VOP_REG_MASK_SYNC(PX30_INTR_STATUS, 0xffff, 0),
-	.enable = VOP_REG_MASK_SYNC(PX30_INTR_EN, 0xffff, 0),
-	.clear = VOP_REG_MASK_SYNC(PX30_INTR_CLEAR, 0xffff, 0),
+static const struct vop_intr rk3368_vop_intr = {
+	.intrs = rk3368_vop_intrs,
+	.nintrs = ARRAY_SIZE(rk3368_vop_intrs),
+	.line_flag_num[0] = VOP_REG(RK3368_LINE_FLAG, 0xffff, 0),
+	.line_flag_num[1] = VOP_REG(RK3368_LINE_FLAG, 0xffff, 16),
+	.status = VOP_REG_MASK(RK3368_INTR_STATUS, 0x3fff, 0),
+	.enable = VOP_REG_MASK(RK3368_INTR_EN, 0x3fff, 0),
+	.clear = VOP_REG_MASK(RK3368_INTR_CLEAR, 0x3fff, 0),
 };
 
-static const struct vop_common px30_common = {
-	.standby = VOP_REG_SYNC(PX30_SYS_CTRL2, 0x1, 1),
-	.out_mode = VOP_REG(PX30_DSP_CTRL2, 0xf, 16),
-	.dsp_blank = VOP_REG(PX30_DSP_CTRL2, 0x1, 14),
-	.dither_down_en = VOP_REG(PX30_DSP_CTRL2, 0x1, 8),
-	.dither_down_sel = VOP_REG(PX30_DSP_CTRL2, 0x1, 7),
-	.dither_down_mode = VOP_REG(PX30_DSP_CTRL2, 0x1, 6),
-	.cfg_done = VOP_REG_SYNC(PX30_REG_CFG_DONE, 0x1, 0),
+static const struct vop_win_phy rk3368_win23_data = {
+	.data_formats = formats_win_lite,
+	.nformats = ARRAY_SIZE(formats_win_lite),
+	.gate = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 0),
+	.enable = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 4),
+	.format = VOP_REG(RK3368_WIN2_CTRL0, 0x3, 5),
+	.ymirror = VOP_REG(RK3368_WIN2_CTRL1, 0x1, 15),
+	.rb_swap = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 20),
+	.dsp_info = VOP_REG(RK3368_WIN2_DSP_INFO0, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3368_WIN2_DSP_ST0, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3368_WIN2_MST0, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3368_WIN2_VIR0_1, 0x1fff, 0),
+	.src_alpha_ctl = VOP_REG(RK3368_WIN2_SRC_ALPHA_CTRL, 0xffff, 0),
+	.global_alpha_val = VOP_REG(RK3368_WIN2_SRC_ALPHA_CTRL, 0xff, 16),
+	.dst_alpha_ctl = VOP_REG(RK3368_WIN2_DST_ALPHA_CTRL, 0xffffffff, 0),
 };
 
-static const struct vop_modeset px30_modeset = {
-	.htotal_pw = VOP_REG(PX30_DSP_HTOTAL_HS_END, 0x0fff0fff, 0),
-	.hact_st_end = VOP_REG(PX30_DSP_HACT_ST_END, 0x0fff0fff, 0),
-	.vtotal_pw = VOP_REG(PX30_DSP_VTOTAL_VS_END, 0x0fff0fff, 0),
-	.vact_st_end = VOP_REG(PX30_DSP_VACT_ST_END, 0x0fff0fff, 0),
+static const struct vop_win_phy rk3368_area1_data = {
+	.enable = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 8),
+	.format = VOP_REG(RK3368_WIN2_CTRL0, 0x3, 9),
+	.rb_swap = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 23),
+	.dsp_info = VOP_REG(RK3368_WIN2_DSP_INFO1, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3368_WIN2_DSP_ST1, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3368_WIN2_MST1, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3368_WIN2_VIR0_1, 0x1fff, 16),
 };
 
-static const struct vop_output px30_output = {
-	.rgb_dclk_pol = VOP_REG(PX30_DSP_CTRL0, 0x1, 1),
-	.rgb_pin_pol = VOP_REG(PX30_DSP_CTRL0, 0x7, 2),
-	.rgb_en = VOP_REG(PX30_DSP_CTRL0, 0x1, 0),
-	.mipi_dclk_pol = VOP_REG(PX30_DSP_CTRL0, 0x1, 25),
-	.mipi_pin_pol = VOP_REG(PX30_DSP_CTRL0, 0x7, 26),
-	.mipi_en = VOP_REG(PX30_DSP_CTRL0, 0x1, 24),
+static const struct vop_win_phy rk3368_area2_data = {
+	.enable = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 12),
+	.format = VOP_REG(RK3368_WIN2_CTRL0, 0x3, 13),
+	.rb_swap = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 26),
+	.dsp_info = VOP_REG(RK3368_WIN2_DSP_INFO2, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3368_WIN2_DSP_ST2, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3368_WIN2_MST2, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3368_WIN2_VIR2_3, 0x1fff, 0),
 };
 
-static const struct vop_scl_regs px30_win_scl = {
-	.scale_yrgb_x = VOP_REG(PX30_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
-	.scale_yrgb_y = VOP_REG(PX30_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
-	.scale_cbcr_x = VOP_REG(PX30_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
-	.scale_cbcr_y = VOP_REG(PX30_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
+static const struct vop_win_phy rk3368_area3_data = {
+	.enable = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 16),
+	.format = VOP_REG(RK3368_WIN2_CTRL0, 0x3, 17),
+	.rb_swap = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 29),
+	.dsp_info = VOP_REG(RK3368_WIN2_DSP_INFO3, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3368_WIN2_DSP_ST3, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3368_WIN2_MST3, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3368_WIN2_VIR2_3, 0x1fff, 16),
 };
 
-static const struct vop_win_phy px30_win0_data = {
-	.scl = &px30_win_scl,
-	.data_formats = formats_win_full,
-	.nformats = ARRAY_SIZE(formats_win_full),
-	.format_modifiers = format_modifiers_win_full,
-	.enable = VOP_REG(PX30_WIN0_CTRL0, 0x1, 0),
-	.format = VOP_REG(PX30_WIN0_CTRL0, 0x7, 1),
-	.rb_swap = VOP_REG(PX30_WIN0_CTRL0, 0x1, 12),
-	.act_info = VOP_REG(PX30_WIN0_ACT_INFO, 0xffffffff, 0),
-	.dsp_info = VOP_REG(PX30_WIN0_DSP_INFO, 0xffffffff, 0),
-	.dsp_st = VOP_REG(PX30_WIN0_DSP_ST, 0xffffffff, 0),
-	.yrgb_mst = VOP_REG(PX30_WIN0_YRGB_MST0, 0xffffffff, 0),
-	.uv_mst = VOP_REG(PX30_WIN0_CBR_MST0, 0xffffffff, 0),
-	.yrgb_vir = VOP_REG(PX30_WIN0_VIR, 0x1fff, 0),
-	.uv_vir = VOP_REG(PX30_WIN0_VIR, 0x1fff, 16),
-	.alpha_pre_mul = VOP_REG(PX30_WIN0_ALPHA_CTRL, 0x1, 2),
-	.alpha_mode = VOP_REG(PX30_WIN0_ALPHA_CTRL, 0x1, 1),
-	.alpha_en = VOP_REG(PX30_WIN0_ALPHA_CTRL, 0x1, 0),
-};
-
-static const struct vop_win_phy px30_win1_data = {
-	.data_formats = formats_win_lite,
-	.nformats = ARRAY_SIZE(formats_win_lite),
-	.format_modifiers = format_modifiers_win_lite,
-	.enable = VOP_REG(PX30_WIN1_CTRL0, 0x1, 0),
-	.format = VOP_REG(PX30_WIN1_CTRL0, 0x7, 4),
-	.rb_swap = VOP_REG(PX30_WIN1_CTRL0, 0x1, 12),
-	.dsp_info = VOP_REG(PX30_WIN1_DSP_INFO, 0xffffffff, 0),
-	.dsp_st = VOP_REG(PX30_WIN1_DSP_ST, 0xffffffff, 0),
-	.yrgb_mst = VOP_REG(PX30_WIN1_MST, 0xffffffff, 0),
-	.yrgb_vir = VOP_REG(PX30_WIN1_VIR, 0x1fff, 0),
-	.alpha_pre_mul = VOP_REG(PX30_WIN1_ALPHA_CTRL, 0x1, 2),
-	.alpha_mode = VOP_REG(PX30_WIN1_ALPHA_CTRL, 0x1, 1),
-	.alpha_en = VOP_REG(PX30_WIN1_ALPHA_CTRL, 0x1, 0),
-};
-
-static const struct vop_win_phy px30_win2_data = {
-	.data_formats = formats_win_lite,
-	.nformats = ARRAY_SIZE(formats_win_lite),
-	.format_modifiers = format_modifiers_win_lite,
-	.gate = VOP_REG(PX30_WIN2_CTRL0, 0x1, 4),
-	.enable = VOP_REG(PX30_WIN2_CTRL0, 0x1, 0),
-	.format = VOP_REG(PX30_WIN2_CTRL0, 0x3, 5),
-	.rb_swap = VOP_REG(PX30_WIN2_CTRL0, 0x1, 20),
-	.dsp_info = VOP_REG(PX30_WIN2_DSP_INFO0, 0x0fff0fff, 0),
-	.dsp_st = VOP_REG(PX30_WIN2_DSP_ST0, 0x1fff1fff, 0),
-	.yrgb_mst = VOP_REG(PX30_WIN2_MST0, 0xffffffff, 0),
-	.yrgb_vir = VOP_REG(PX30_WIN2_VIR0_1, 0x1fff, 0),
-	.alpha_pre_mul = VOP_REG(PX30_WIN2_ALPHA_CTRL, 0x1, 2),
-	.alpha_mode = VOP_REG(PX30_WIN2_ALPHA_CTRL, 0x1, 1),
-	.alpha_en = VOP_REG(PX30_WIN2_ALPHA_CTRL, 0x1, 0),
+static const struct vop_win_phy *rk3368_area_data[] = {
+	&rk3368_area1_data,
+	&rk3368_area2_data,
+	&rk3368_area3_data
 };
 
-static const struct vop_win_data px30_vop_big_win_data[] = {
-	{ .base = 0x00, .phy = &px30_win0_data,
+static const struct vop_win_data rk3368_vop_win_data[] = {
+	{ .base = 0x00, .phy = &rk3288_win01_data,
 	  .type = DRM_PLANE_TYPE_PRIMARY },
-	{ .base = 0x00, .phy = &px30_win1_data,
+	{ .base = 0x40, .phy = &rk3288_win01_data,
 	  .type = DRM_PLANE_TYPE_OVERLAY },
-	{ .base = 0x00, .phy = &px30_win2_data,
-	  .type = DRM_PLANE_TYPE_CURSOR },
+	{ .base = 0x00, .phy = &rk3368_win23_data,
+	  .type = DRM_PLANE_TYPE_OVERLAY,
+	  .area = rk3368_area_data,
+	  .area_size = ARRAY_SIZE(rk3368_area_data), },
+	{ .base = 0x50, .phy = &rk3368_win23_data,
+	  .type = DRM_PLANE_TYPE_CURSOR,
+	  .area = rk3368_area_data,
+	  .area_size = ARRAY_SIZE(rk3368_area_data), },
 };
 
-static const struct vop_data px30_vop_big = {
-	.intr = &px30_intr,
-	.feature = VOP_FEATURE_INTERNAL_RGB,
-	.common = &px30_common,
-	.modeset = &px30_modeset,
-	.output = &px30_output,
-	.win = px30_vop_big_win_data,
-	.win_size = ARRAY_SIZE(px30_vop_big_win_data),
+static const struct vop_data rk3368_vop = {
+	.soc_id = 0x3368,
+	.vop_id = 0,
+	.version = VOP_VERSION(3, 2),
+	.feature = VOP_FEATURE_ALPHA_SCALE | VOP_FEATURE_OVERSCAN,
+	.max_input = {4096, 8192},
+	.max_output = {4096, 2160},
+	.intr = &rk3368_vop_intr,
+	.ctrl = &rk3288_ctrl_data,
+	.win = rk3368_vop_win_data,
+	.win_size = ARRAY_SIZE(rk3368_vop_win_data),
 };
 
-static const struct vop_win_data px30_vop_lit_win_data[] = {
-	{ .base = 0x00, .phy = &px30_win1_data,
-	  .type = DRM_PLANE_TYPE_PRIMARY },
+static const struct vop_intr rk3366_vop_intr = {
+	.intrs = rk3368_vop_intrs,
+	.nintrs = ARRAY_SIZE(rk3368_vop_intrs),
+	.line_flag_num[0] = VOP_REG(RK3366_LINE_FLAG, 0xffff, 0),
+	.line_flag_num[1] = VOP_REG(RK3366_LINE_FLAG, 0xffff, 16),
+	.status = VOP_REG_MASK(RK3366_INTR_STATUS0, 0xffff, 0),
+	.enable = VOP_REG_MASK(RK3366_INTR_EN0, 0xffff, 0),
+	.clear = VOP_REG_MASK(RK3366_INTR_CLEAR0, 0xffff, 0),
 };
 
-static const struct vop_data px30_vop_lit = {
-	.intr = &px30_intr,
-	.feature = VOP_FEATURE_INTERNAL_RGB,
-	.common = &px30_common,
-	.modeset = &px30_modeset,
-	.output = &px30_output,
-	.win = px30_vop_lit_win_data,
-	.win_size = ARRAY_SIZE(px30_vop_lit_win_data),
+static const struct vop_grf_ctrl rk3368_vop_grf_ctrl = {
+	.grf_dclk_inv = VOP_REG(RK3368_GRF_SOC_CON6, 0x1, 5),
 };
 
-static const struct vop_scl_regs rk3066_win_scl = {
-	.scale_yrgb_x = VOP_REG(RK3066_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
-	.scale_yrgb_y = VOP_REG(RK3066_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
-	.scale_cbcr_x = VOP_REG(RK3066_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
-	.scale_cbcr_y = VOP_REG(RK3066_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
+static const struct vop_data rk3366_vop = {
+	.soc_id = 0x3366,
+	.vop_id = 0,
+	.version = VOP_VERSION(3, 4),
+	.feature = VOP_FEATURE_ALPHA_SCALE | VOP_FEATURE_OVERSCAN,
+	.max_input = {4096, 8192},
+	.max_output = {4096, 2160},
+	.intr = &rk3366_vop_intr,
+	.grf_ctrl = &rk3368_vop_grf_ctrl,
+	.ctrl = &rk3288_ctrl_data,
+	.win = rk3368_vop_win_data,
+	.win_size = ARRAY_SIZE(rk3368_vop_win_data),
 };
 
-static const struct vop_win_phy rk3066_win0_data = {
-	.scl = &rk3066_win_scl,
-	.data_formats = formats_win_full,
-	.nformats = ARRAY_SIZE(formats_win_full),
-	.format_modifiers = format_modifiers_win_full,
-	.enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 0),
-	.format = VOP_REG(RK3066_SYS_CTRL1, 0x7, 4),
-	.rb_swap = VOP_REG(RK3066_SYS_CTRL1, 0x1, 19),
-	.act_info = VOP_REG(RK3066_WIN0_ACT_INFO, 0x1fff1fff, 0),
-	.dsp_info = VOP_REG(RK3066_WIN0_DSP_INFO, 0x0fff0fff, 0),
-	.dsp_st = VOP_REG(RK3066_WIN0_DSP_ST, 0x1fff1fff, 0),
-	.yrgb_mst = VOP_REG(RK3066_WIN0_YRGB_MST0, 0xffffffff, 0),
-	.uv_mst = VOP_REG(RK3066_WIN0_CBR_MST0, 0xffffffff, 0),
-	.yrgb_vir = VOP_REG(RK3066_WIN0_VIR, 0xffff, 0),
-	.uv_vir = VOP_REG(RK3066_WIN0_VIR, 0x1fff, 16),
+static const uint32_t vop_csc_y2r_bt601[] = {
+	0x00000400, 0x0400059c, 0xfd25fea0, 0x07170400,
+	0x00000000, 0xfff4cab4, 0x00087932, 0xfff1d4f2,
 };
 
-static const struct vop_win_phy rk3066_win1_data = {
-	.data_formats = formats_win_full,
-	.nformats = ARRAY_SIZE(formats_win_full),
-	.format_modifiers = format_modifiers_win_full,
-	.enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 1),
-	.format = VOP_REG(RK3066_SYS_CTRL1, 0x7, 7),
-	.rb_swap = VOP_REG(RK3066_SYS_CTRL1, 0x1, 23),
-	.act_info = VOP_REG(RK3066_WIN1_ACT_INFO, 0x1fff1fff, 0),
-	.dsp_info = VOP_REG(RK3066_WIN1_DSP_INFO, 0x0fff0fff, 0),
-	.dsp_st = VOP_REG(RK3066_WIN1_DSP_ST, 0x1fff1fff, 0),
-	.yrgb_mst = VOP_REG(RK3066_WIN1_YRGB_MST, 0xffffffff, 0),
-	.uv_mst = VOP_REG(RK3066_WIN1_CBR_MST, 0xffffffff, 0),
-	.yrgb_vir = VOP_REG(RK3066_WIN1_VIR, 0xffff, 0),
-	.uv_vir = VOP_REG(RK3066_WIN1_VIR, 0x1fff, 16),
+static const uint32_t vop_csc_y2r_bt601_12_235[] = {
+	0x000004a8, 0x04a80662, 0xfcbffe6f, 0x081204a8,
+	0x00000000, 0xfff2134e, 0x00087b58, 0xffeeb4b0,
 };
 
-static const struct vop_win_phy rk3066_win2_data = {
-	.data_formats = formats_win_lite,
-	.nformats = ARRAY_SIZE(formats_win_lite),
-	.format_modifiers = format_modifiers_win_lite,
-	.enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 2),
-	.format = VOP_REG(RK3066_SYS_CTRL1, 0x7, 10),
-	.rb_swap = VOP_REG(RK3066_SYS_CTRL1, 0x1, 27),
-	.dsp_info = VOP_REG(RK3066_WIN2_DSP_INFO, 0x0fff0fff, 0),
-	.dsp_st = VOP_REG(RK3066_WIN2_DSP_ST, 0x1fff1fff, 0),
-	.yrgb_mst = VOP_REG(RK3066_WIN2_MST, 0xffffffff, 0),
-	.yrgb_vir = VOP_REG(RK3066_WIN2_VIR, 0xffff, 0),
+static const uint32_t vop_csc_r2y_bt601[] = {
+	0x02590132, 0xff530075, 0x0200fead, 0xfe530200,
+	0x0000ffad, 0x00000200, 0x00080200, 0x00080200,
 };
 
-static const struct vop_modeset rk3066_modeset = {
-	.htotal_pw = VOP_REG(RK3066_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
-	.hact_st_end = VOP_REG(RK3066_DSP_HACT_ST_END, 0x1fff1fff, 0),
-	.vtotal_pw = VOP_REG(RK3066_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
-	.vact_st_end = VOP_REG(RK3066_DSP_VACT_ST_END, 0x1fff1fff, 0),
+static const uint32_t vop_csc_r2y_bt601_12_235[] = {
+	0x02040107, 0xff680064, 0x01c2fed6, 0xfe8701c2,
+	0x0000ffb7, 0x00010200, 0x00080200, 0x00080200,
 };
 
-static const struct vop_output rk3066_output = {
-	.pin_pol = VOP_REG(RK3066_DSP_CTRL0, 0x7, 4),
+static const uint32_t vop_csc_y2r_bt709[] = {
+	0x000004a8, 0x04a8072c, 0xfddeff26, 0x087304a8,
+	0x00000000, 0xfff08077, 0x0004cfed, 0xffedf1b8,
 };
 
-static const struct vop_common rk3066_common = {
-	.standby = VOP_REG(RK3066_SYS_CTRL0, 0x1, 1),
-	.out_mode = VOP_REG(RK3066_DSP_CTRL0, 0xf, 0),
-	.cfg_done = VOP_REG(RK3066_REG_CFG_DONE, 0x1, 0),
-	.dither_down_en = VOP_REG(RK3066_DSP_CTRL0, 0x1, 11),
-	.dither_down_mode = VOP_REG(RK3066_DSP_CTRL0, 0x1, 10),
-	.dsp_blank = VOP_REG(RK3066_DSP_CTRL1, 0x1, 24),
-	.dither_up = VOP_REG(RK3066_DSP_CTRL0, 0x1, 9),
-	.dsp_lut_en = VOP_REG(RK3066_SYS_CTRL1, 0x1, 31),
-	.data_blank = VOP_REG(RK3066_DSP_CTRL1, 0x1, 25),
+static const uint32_t vop_csc_r2y_bt709[] = {
+	0x027500bb, 0xff99003f, 0x01c2fea5, 0xfe6801c2,
+	0x0000ffd7, 0x00010200, 0x00080200, 0x00080200,
 };
 
-static const struct vop_win_data rk3066_vop_win_data[] = {
-	{ .base = 0x00, .phy = &rk3066_win0_data,
-	  .type = DRM_PLANE_TYPE_PRIMARY },
-	{ .base = 0x00, .phy = &rk3066_win1_data,
-	  .type = DRM_PLANE_TYPE_OVERLAY },
-	{ .base = 0x00, .phy = &rk3066_win2_data,
-	  .type = DRM_PLANE_TYPE_CURSOR },
+static const uint32_t vop_csc_y2r_bt2020[] = {
+	0x000004a8, 0x04a806b6, 0xfd66ff40, 0x089004a8,
+	0x00000000, 0xfff16bfc, 0x00058ae9, 0xffedb828,
 };
 
-static const int rk3066_vop_intrs[] = {
-	/*
-	 * hs_start interrupt fires at frame-start, so serves
-	 * the same purpose as dsp_hold in the driver.
-	 */
-	DSP_HOLD_VALID_INTR,
-	FS_INTR,
-	LINE_FLAG_INTR,
-	BUS_ERROR_INTR,
+static const uint32_t vop_csc_r2y_bt2020[] = {
+	0x025300e6, 0xff830034, 0x01c1febd, 0xfe6401c1,
+	0x0000ffdc, 0x00010200, 0x00080200, 0x00080200,
 };
 
-static const struct vop_intr rk3066_intr = {
-	.intrs = rk3066_vop_intrs,
-	.nintrs = ARRAY_SIZE(rk3066_vop_intrs),
-	.line_flag_num[0] = VOP_REG(RK3066_INT_STATUS, 0xfff, 12),
-	.status = VOP_REG(RK3066_INT_STATUS, 0xf, 0),
-	.enable = VOP_REG(RK3066_INT_STATUS, 0xf, 4),
-	.clear = VOP_REG(RK3066_INT_STATUS, 0xf, 8),
+static const uint32_t vop_csc_r2r_bt709_to_bt2020[] = {
+	0xfda606a4, 0xff80ffb5, 0xfff80488, 0xff99ffed,
+	0x0000047a, 0x00000200, 0x00000200, 0x00000200,
 };
 
-static const struct vop_data rk3066_vop = {
-	.version = VOP_VERSION(2, 1),
-	.intr = &rk3066_intr,
-	.common = &rk3066_common,
-	.modeset = &rk3066_modeset,
-	.output = &rk3066_output,
-	.win = rk3066_vop_win_data,
-	.win_size = ARRAY_SIZE(rk3066_vop_win_data),
+static const uint32_t vop_csc_r2r_bt2020_to_bt709[] = {
+	0x01510282, 0x0047002c, 0x000c03ae, 0x005a0011,
+	0x00000394, 0x00000200, 0x00000200, 0x00000200,
 };
 
-static const struct vop_scl_regs rk3188_win_scl = {
-	.scale_yrgb_x = VOP_REG(RK3188_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
-	.scale_yrgb_y = VOP_REG(RK3188_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
-	.scale_cbcr_x = VOP_REG(RK3188_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
-	.scale_cbcr_y = VOP_REG(RK3188_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
-};
+static const struct vop_csc_table rk3399_csc_table = {
+	.y2r_bt601		= vop_csc_y2r_bt601,
+	.y2r_bt601_12_235	= vop_csc_y2r_bt601_12_235,
+	.r2y_bt601		= vop_csc_r2y_bt601,
+	.r2y_bt601_12_235	= vop_csc_r2y_bt601_12_235,
 
-static const struct vop_win_phy rk3188_win0_data = {
-	.scl = &rk3188_win_scl,
-	.data_formats = formats_win_full,
-	.nformats = ARRAY_SIZE(formats_win_full),
-	.format_modifiers = format_modifiers_win_full,
-	.enable = VOP_REG(RK3188_SYS_CTRL, 0x1, 0),
-	.format = VOP_REG(RK3188_SYS_CTRL, 0x7, 3),
-	.rb_swap = VOP_REG(RK3188_SYS_CTRL, 0x1, 15),
-	.act_info = VOP_REG(RK3188_WIN0_ACT_INFO, 0x1fff1fff, 0),
-	.dsp_info = VOP_REG(RK3188_WIN0_DSP_INFO, 0x0fff0fff, 0),
-	.dsp_st = VOP_REG(RK3188_WIN0_DSP_ST, 0x1fff1fff, 0),
-	.yrgb_mst = VOP_REG(RK3188_WIN0_YRGB_MST0, 0xffffffff, 0),
-	.uv_mst = VOP_REG(RK3188_WIN0_CBR_MST0, 0xffffffff, 0),
-	.yrgb_vir = VOP_REG(RK3188_WIN_VIR, 0x1fff, 0),
-};
-
-static const struct vop_win_phy rk3188_win1_data = {
-	.data_formats = formats_win_lite,
-	.nformats = ARRAY_SIZE(formats_win_lite),
-	.format_modifiers = format_modifiers_win_lite,
-	.enable = VOP_REG(RK3188_SYS_CTRL, 0x1, 1),
-	.format = VOP_REG(RK3188_SYS_CTRL, 0x7, 6),
-	.rb_swap = VOP_REG(RK3188_SYS_CTRL, 0x1, 19),
-	/* no act_info on window1 */
-	.dsp_info = VOP_REG(RK3188_WIN1_DSP_INFO, 0x07ff07ff, 0),
-	.dsp_st = VOP_REG(RK3188_WIN1_DSP_ST, 0x0fff0fff, 0),
-	.yrgb_mst = VOP_REG(RK3188_WIN1_MST, 0xffffffff, 0),
-	.yrgb_vir = VOP_REG(RK3188_WIN_VIR, 0x1fff, 16),
-};
-
-static const struct vop_modeset rk3188_modeset = {
-	.htotal_pw = VOP_REG(RK3188_DSP_HTOTAL_HS_END, 0x0fff0fff, 0),
-	.hact_st_end = VOP_REG(RK3188_DSP_HACT_ST_END, 0x0fff0fff, 0),
-	.vtotal_pw = VOP_REG(RK3188_DSP_VTOTAL_VS_END, 0x0fff0fff, 0),
-	.vact_st_end = VOP_REG(RK3188_DSP_VACT_ST_END, 0x0fff0fff, 0),
-};
-
-static const struct vop_output rk3188_output = {
-	.pin_pol = VOP_REG(RK3188_DSP_CTRL0, 0xf, 4),
-};
-
-static const struct vop_common rk3188_common = {
-	.gate_en = VOP_REG(RK3188_SYS_CTRL, 0x1, 31),
-	.standby = VOP_REG(RK3188_SYS_CTRL, 0x1, 30),
-	.out_mode = VOP_REG(RK3188_DSP_CTRL0, 0xf, 0),
-	.cfg_done = VOP_REG(RK3188_REG_CFG_DONE, 0x1, 0),
-	.dither_down_sel = VOP_REG(RK3188_DSP_CTRL0, 0x1, 27),
-	.dither_down_en = VOP_REG(RK3188_DSP_CTRL0, 0x1, 11),
-	.dither_down_mode = VOP_REG(RK3188_DSP_CTRL0, 0x1, 10),
-	.dsp_blank = VOP_REG(RK3188_DSP_CTRL1, 0x1, 24),
-	.dither_up = VOP_REG(RK3188_DSP_CTRL0, 0x1, 9),
-	.dsp_lut_en = VOP_REG(RK3188_SYS_CTRL, 0x1, 28),
-	.data_blank = VOP_REG(RK3188_DSP_CTRL1, 0x1, 25),
-};
-
-static const struct vop_win_data rk3188_vop_win_data[] = {
-	{ .base = 0x00, .phy = &rk3188_win0_data,
-	  .type = DRM_PLANE_TYPE_PRIMARY },
-	{ .base = 0x00, .phy = &rk3188_win1_data,
-	  .type = DRM_PLANE_TYPE_CURSOR },
-};
+	.y2r_bt709		= vop_csc_y2r_bt709,
+	.r2y_bt709		= vop_csc_r2y_bt709,
 
-static const int rk3188_vop_intrs[] = {
-	/*
-	 * hs_start interrupt fires at frame-start, so serves
-	 * the same purpose as dsp_hold in the driver.
-	 */
-	DSP_HOLD_VALID_INTR,
-	FS_INTR,
-	LINE_FLAG_INTR,
-	BUS_ERROR_INTR,
+	.y2r_bt2020		= vop_csc_y2r_bt2020,
+	.r2y_bt2020		= vop_csc_r2y_bt2020,
+
+	.r2r_bt709_to_bt2020	= vop_csc_r2r_bt709_to_bt2020,
+	.r2r_bt2020_to_bt709	= vop_csc_r2r_bt2020_to_bt709,
 };
 
-static const struct vop_intr rk3188_vop_intr = {
-	.intrs = rk3188_vop_intrs,
-	.nintrs = ARRAY_SIZE(rk3188_vop_intrs),
-	.line_flag_num[0] = VOP_REG(RK3188_INT_STATUS, 0xfff, 12),
-	.status = VOP_REG(RK3188_INT_STATUS, 0xf, 0),
-	.enable = VOP_REG(RK3188_INT_STATUS, 0xf, 4),
-	.clear = VOP_REG(RK3188_INT_STATUS, 0xf, 8),
+static const struct vop_csc rk3399_win0_csc = {
+	.r2r_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 0),
+	.y2r_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 1),
+	.r2y_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 2),
+	.y2r_offset = RK3399_WIN0_YUV2YUV_Y2R,
+	.r2r_offset = RK3399_WIN0_YUV2YUV_3X3,
+	.r2y_offset = RK3399_WIN0_YUV2YUV_R2Y,
 };
 
-static const struct vop_data rk3188_vop = {
-	.intr = &rk3188_vop_intr,
-	.common = &rk3188_common,
-	.modeset = &rk3188_modeset,
-	.output = &rk3188_output,
-	.win = rk3188_vop_win_data,
-	.win_size = ARRAY_SIZE(rk3188_vop_win_data),
-	.feature = VOP_FEATURE_INTERNAL_RGB,
+static const struct vop_csc rk3399_win1_csc = {
+	.r2r_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 8),
+	.y2r_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 9),
+	.r2y_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 10),
+	.y2r_offset = RK3399_WIN1_YUV2YUV_Y2R,
+	.r2r_offset = RK3399_WIN1_YUV2YUV_3X3,
+	.r2y_offset = RK3399_WIN1_YUV2YUV_R2Y,
 };
 
-static const struct vop_scl_extension rk3288_win_full_scl_ext = {
-	.cbcr_vsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 31),
-	.cbcr_vsu_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 30),
-	.cbcr_hsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 28),
-	.cbcr_ver_scl_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 26),
-	.cbcr_hor_scl_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 24),
-	.yrgb_vsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 23),
-	.yrgb_vsu_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 22),
-	.yrgb_hsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 20),
-	.yrgb_ver_scl_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 18),
-	.yrgb_hor_scl_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 16),
-	.line_load_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 15),
-	.cbcr_axi_gather_num = VOP_REG(RK3288_WIN0_CTRL1, 0x7, 12),
-	.yrgb_axi_gather_num = VOP_REG(RK3288_WIN0_CTRL1, 0xf, 8),
-	.vsd_cbcr_gt2 = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 7),
-	.vsd_cbcr_gt4 = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 6),
-	.vsd_yrgb_gt2 = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 5),
-	.vsd_yrgb_gt4 = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 4),
-	.bic_coe_sel = VOP_REG(RK3288_WIN0_CTRL1, 0x3, 2),
-	.cbcr_axi_gather_en = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 1),
-	.yrgb_axi_gather_en = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 0),
-	.lb_mode = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 5),
+static const struct vop_csc rk3399_win2_csc = {
+	.r2r_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 16),
+	.r2y_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 18),
+	.r2r_offset = RK3399_WIN2_YUV2YUV_3X3,
+	.csc_mode = VOP_REG(RK3399_YUV2YUV_WIN, 0x3, 22),
 };
 
-static const struct vop_scl_regs rk3288_win_full_scl = {
-	.ext = &rk3288_win_full_scl_ext,
-	.scale_yrgb_x = VOP_REG(RK3288_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
-	.scale_yrgb_y = VOP_REG(RK3288_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
-	.scale_cbcr_x = VOP_REG(RK3288_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
-	.scale_cbcr_y = VOP_REG(RK3288_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
+static const struct vop_csc rk3399_win3_csc = {
+	.r2r_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 24),
+	.r2y_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 26),
+	.r2r_offset = RK3399_WIN3_YUV2YUV_3X3,
+	.csc_mode = VOP_REG(RK3399_YUV2YUV_WIN, 0x3, 30),
 };
 
-static const struct vop_win_phy rk3288_win01_data = {
+static const struct vop_win_phy rk3399_win01_data = {
 	.scl = &rk3288_win_full_scl,
-	.data_formats = formats_win_full,
-	.nformats = ARRAY_SIZE(formats_win_full),
-	.format_modifiers = format_modifiers_win_full,
+	.data_formats = formats_win_full_10bit_yuyv,
+	.nformats = ARRAY_SIZE(formats_win_full_10bit_yuyv),
 	.enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0),
 	.format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1),
+	.fmt_10 = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 4),
+	.fmt_yuyv = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 17),
+	.csc_mode = VOP_REG_VER(RK3288_WIN0_CTRL0, 0x3, 10, 3, 2, -1),
 	.rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12),
+	.xmirror = VOP_REG_VER(RK3368_WIN0_CTRL0, 0x1, 21, 3, 2, -1),
+	.ymirror = VOP_REG_VER(RK3368_WIN0_CTRL0, 0x1, 22, 3, 2, -1),
 	.act_info = VOP_REG(RK3288_WIN0_ACT_INFO, 0x1fff1fff, 0),
 	.dsp_info = VOP_REG(RK3288_WIN0_DSP_INFO, 0x0fff0fff, 0),
 	.dsp_st = VOP_REG(RK3288_WIN0_DSP_ST, 0x1fff1fff, 0),
@@ -597,466 +675,1196 @@ static const struct vop_win_phy rk3288_win01_data = {
 	.uv_mst = VOP_REG(RK3288_WIN0_CBR_MST, 0xffffffff, 0),
 	.yrgb_vir = VOP_REG(RK3288_WIN0_VIR, 0x3fff, 0),
 	.uv_vir = VOP_REG(RK3288_WIN0_VIR, 0x3fff, 16),
-	.src_alpha_ctl = VOP_REG(RK3288_WIN0_SRC_ALPHA_CTRL, 0xff, 0),
-	.dst_alpha_ctl = VOP_REG(RK3288_WIN0_DST_ALPHA_CTRL, 0xff, 0),
-	.channel = VOP_REG(RK3288_WIN0_CTRL2, 0xff, 0),
+	.src_alpha_ctl = VOP_REG(RK3288_WIN0_SRC_ALPHA_CTRL, 0xffff, 0),
+	.global_alpha_val = VOP_REG(RK3288_WIN0_SRC_ALPHA_CTRL, 0xff, 16),
+	.dst_alpha_ctl = VOP_REG(RK3288_WIN0_DST_ALPHA_CTRL, 0xffffffff, 0),
+	.channel = VOP_REG_VER(RK3288_WIN0_CTRL2, 0xff, 0, 3, 8, 8),
 };
 
-static const struct vop_win_phy rk3288_win23_data = {
-	.data_formats = formats_win_lite,
-	.nformats = ARRAY_SIZE(formats_win_lite),
-	.format_modifiers = format_modifiers_win_lite,
-	.enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 4),
-	.gate = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 0),
-	.format = VOP_REG(RK3288_WIN2_CTRL0, 0x7, 1),
-	.rb_swap = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 12),
-	.dsp_info = VOP_REG(RK3288_WIN2_DSP_INFO0, 0x0fff0fff, 0),
-	.dsp_st = VOP_REG(RK3288_WIN2_DSP_ST0, 0x1fff1fff, 0),
-	.yrgb_mst = VOP_REG(RK3288_WIN2_MST0, 0xffffffff, 0),
-	.yrgb_vir = VOP_REG(RK3288_WIN2_VIR0_1, 0x1fff, 0),
-	.src_alpha_ctl = VOP_REG(RK3288_WIN2_SRC_ALPHA_CTRL, 0xff, 0),
-	.dst_alpha_ctl = VOP_REG(RK3288_WIN2_DST_ALPHA_CTRL, 0xff, 0),
+static const struct vop_win_data rk3399_vop_win_data[] = {
+	{ .base = 0x00, .phy = &rk3399_win01_data, .csc = &rk3399_win0_csc,
+	  .format_modifiers = format_modifiers_afbc,
+	  .type = DRM_PLANE_TYPE_PRIMARY,
+	  .feature = WIN_FEATURE_AFBDC },
+	{ .base = 0x40, .phy = &rk3399_win01_data, .csc = &rk3399_win1_csc,
+	  .format_modifiers = format_modifiers_afbc,
+	  .type = DRM_PLANE_TYPE_OVERLAY,
+	  .feature = WIN_FEATURE_AFBDC },
+	{ .base = 0x00, .phy = &rk3368_win23_data, .csc = &rk3399_win2_csc,
+	  .format_modifiers = format_modifiers_afbc,
+	  .type = DRM_PLANE_TYPE_OVERLAY,
+	  .feature = WIN_FEATURE_AFBDC,
+	  .area = rk3368_area_data,
+	  .area_size = ARRAY_SIZE(rk3368_area_data), },
+	{ .base = 0x50, .phy = &rk3368_win23_data, .csc = &rk3399_win3_csc,
+	  .format_modifiers = format_modifiers_afbc,
+	  .type = DRM_PLANE_TYPE_CURSOR,
+	  .feature = WIN_FEATURE_AFBDC,
+	  .area = rk3368_area_data,
+	  .area_size = ARRAY_SIZE(rk3368_area_data), },
 };
 
-static const struct vop_modeset rk3288_modeset = {
-	.htotal_pw = VOP_REG(RK3288_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
-	.hact_st_end = VOP_REG(RK3288_DSP_HACT_ST_END, 0x1fff1fff, 0),
-	.vtotal_pw = VOP_REG(RK3288_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
-	.vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
-	.hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
-	.vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+static const struct vop_data rk3399_vop_big = {
+	.soc_id = 0x3399,
+	.vop_id = 0,
+	.version = VOP_VERSION(3, 5),
+	.csc_table = &rk3399_csc_table,
+	.feature = VOP_FEATURE_OUTPUT_10BIT | VOP_FEATURE_ALPHA_SCALE | VOP_FEATURE_OVERSCAN,
+	.max_input = {4096, 8192},
+	.max_output = {4096, 2160},
+	.intr = &rk3366_vop_intr,
+	.ctrl = &rk3288_ctrl_data,
+	.win = rk3399_vop_win_data,
+	.win_size = ARRAY_SIZE(rk3399_vop_win_data),
 };
 
-static const struct vop_output rk3288_output = {
-	.pin_pol = VOP_REG(RK3288_DSP_CTRL0, 0xf, 4),
-	.rgb_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 12),
-	.hdmi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 13),
-	.edp_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 14),
-	.mipi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 15),
+static const struct vop_win_data rk3399_vop_lit_win_data[] = {
+	{ .base = 0x00, .phy = &rk3399_win01_data, .csc = &rk3399_win0_csc,
+	  .format_modifiers = format_modifiers,
+	  .type = DRM_PLANE_TYPE_OVERLAY,
+	  .feature = WIN_FEATURE_AFBDC },
+	{ .phy = NULL },
+	{ .base = 0x00, .phy = &rk3368_win23_data, .csc = &rk3399_win2_csc,
+	  .format_modifiers = format_modifiers,
+	  .type = DRM_PLANE_TYPE_PRIMARY,
+	  .feature = WIN_FEATURE_AFBDC,
+	  .area = rk3368_area_data,
+	  .area_size = ARRAY_SIZE(rk3368_area_data), },
+	{ .phy = NULL },
 };
 
-static const struct vop_common rk3288_common = {
-	.standby = VOP_REG_SYNC(RK3288_SYS_CTRL, 0x1, 22),
-	.gate_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 23),
-	.mmu_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 20),
-	.dither_down_sel = VOP_REG(RK3288_DSP_CTRL1, 0x1, 4),
-	.dither_down_mode = VOP_REG(RK3288_DSP_CTRL1, 0x1, 3),
-	.dither_down_en = VOP_REG(RK3288_DSP_CTRL1, 0x1, 2),
-	.pre_dither_down = VOP_REG(RK3288_DSP_CTRL1, 0x1, 1),
-	.dither_up = VOP_REG(RK3288_DSP_CTRL1, 0x1, 6),
-	.dsp_lut_en = VOP_REG(RK3288_DSP_CTRL1, 0x1, 0),
-	.data_blank = VOP_REG(RK3288_DSP_CTRL0, 0x1, 19),
-	.dsp_blank = VOP_REG(RK3288_DSP_CTRL0, 0x3, 18),
-	.out_mode = VOP_REG(RK3288_DSP_CTRL0, 0xf, 0),
-	.cfg_done = VOP_REG_SYNC(RK3288_REG_CFG_DONE, 0x1, 0),
+
+static const struct vop_data rk3399_vop_lit = {
+	.soc_id = 0x3399,
+	.vop_id = 1,
+	.version = VOP_VERSION(3, 6),
+	.feature = VOP_FEATURE_ALPHA_SCALE | VOP_FEATURE_OVERSCAN,
+	.csc_table = &rk3399_csc_table,
+	.max_input = {4096, 8192},
+	.max_output = {2560, 1600},
+	.intr = &rk3366_vop_intr,
+	.ctrl = &rk3288_ctrl_data,
+	.win = rk3399_vop_lit_win_data,
+	.win_size = ARRAY_SIZE(rk3399_vop_lit_win_data),
 };
 
-/*
- * Note: rk3288 has a dedicated 'cursor' window, however, that window requires
- * special support to get alpha blending working.  For now, just use overlay
- * window 3 for the drm cursor.
- *
- */
-static const struct vop_win_data rk3288_vop_win_data[] = {
+static const struct vop_win_data rk322x_vop_win_data[] = {
 	{ .base = 0x00, .phy = &rk3288_win01_data,
 	  .type = DRM_PLANE_TYPE_PRIMARY },
 	{ .base = 0x40, .phy = &rk3288_win01_data,
-	  .type = DRM_PLANE_TYPE_OVERLAY },
-	{ .base = 0x00, .phy = &rk3288_win23_data,
-	  .type = DRM_PLANE_TYPE_OVERLAY },
-	{ .base = 0x50, .phy = &rk3288_win23_data,
 	  .type = DRM_PLANE_TYPE_CURSOR },
 };
 
-static const int rk3288_vop_intrs[] = {
-	DSP_HOLD_VALID_INTR,
-	FS_INTR,
-	LINE_FLAG_INTR,
-	BUS_ERROR_INTR,
-};
-
-static const struct vop_intr rk3288_vop_intr = {
-	.intrs = rk3288_vop_intrs,
-	.nintrs = ARRAY_SIZE(rk3288_vop_intrs),
-	.line_flag_num[0] = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
-	.status = VOP_REG(RK3288_INTR_CTRL0, 0xf, 0),
-	.enable = VOP_REG(RK3288_INTR_CTRL0, 0xf, 4),
-	.clear = VOP_REG(RK3288_INTR_CTRL0, 0xf, 8),
+static const struct vop_data rk3228_vop = {
+	.soc_id = 0x3228,
+	.vop_id = 0,
+	.version = VOP_VERSION(3, 7),
+	.feature = VOP_FEATURE_OUTPUT_10BIT | VOP_FEATURE_ALPHA_SCALE | VOP_FEATURE_OVERSCAN,
+	.max_input = {4096, 8192},
+	.max_output = {4096, 2160},
+	.intr = &rk3366_vop_intr,
+	.ctrl = &rk3288_ctrl_data,
+	.win = rk322x_vop_win_data,
+	.win_size = ARRAY_SIZE(rk322x_vop_win_data),
 };
 
-static const struct vop_data rk3288_vop = {
-	.version = VOP_VERSION(3, 1),
-	.feature = VOP_FEATURE_OUTPUT_RGB10,
-	.intr = &rk3288_vop_intr,
-	.common = &rk3288_common,
-	.modeset = &rk3288_modeset,
-	.output = &rk3288_output,
-	.win = rk3288_vop_win_data,
-	.win_size = ARRAY_SIZE(rk3288_vop_win_data),
-	.lut_size = 1024,
+static const u32 sdr2hdr_bt1886eotf_yn_for_hlg_hdr[65] = {
+	0,
+	1,	7,	17,	35,
+	60,	92,	134,	184,
+	244,	315,	396,	487,
+	591,	706,	833,	915,
+	1129,	1392,	1717,	2118,
+	2352,	2612,	2900,	3221,
+	3577,	3972,	4411,	4899,
+	5441,	6042,	6710,	7452,
+	7853,	8276,	8721,	9191,
+	9685,	10207,	10756,	11335,
+	11945,	12588,	13266,	13980,
+	14732,	15525,	16361,	17241,
+	17699,	18169,	18652,	19147,
+	19656,	20178,	20714,	21264,
+	21829,	22408,	23004,	23615,
+	24242,	24886,	25547,	26214,
+};
+
+static const u32 sdr2hdr_bt1886eotf_yn_for_bt2020[65] = {
+	0,
+	1820,   3640,   5498,   7674,
+	10256,  13253,  16678,  20539,
+	24847,  29609,  34833,  40527,
+	46699,  53354,  60499,  68141,
+	76285,  84937,  94103,  103787,
+	108825, 113995, 119296, 124731,
+	130299, 136001, 141837, 147808,
+	153915, 160158, 166538, 173055,
+	176365, 179709, 183089, 186502,
+	189951, 193434, 196952, 200505,
+	204093, 207715, 211373, 215066,
+	218795, 222558, 226357, 230191,
+	232121, 234060, 236008, 237965,
+	239931, 241906, 243889, 245882,
+	247883, 249894, 251913, 253941,
+	255978, 258024, 260079, 262143,
+};
+
+static u32 sdr2hdr_bt1886eotf_yn_for_hdr[65] = {
+	/* dst_range 425int */
+	0,
+	5,     21,    49,     91,
+	150,   225,   320,   434,
+	569,   726,   905,   1108,
+	1336,  1588,  1866,  2171,
+	2502,  2862,  3250,  3667,
+	3887,  4114,  4349,  4591,
+	4841,  5099,  5364,  5638,
+	5920,  6209,  6507,  6812,
+	6968,  7126,  7287,  7449,
+	7613,  7779,  7948,  8118,
+	8291,  8466,  8643,  8822,
+	9003,  9187,  9372,  9560,
+	9655,  9750,  9846,  9942,
+	10039, 10136, 10234, 10333,
+	10432, 10531, 10631, 10732,
+	10833, 10935, 11038, 11141,
+};
+
+static const u32 sdr2hdr_st2084oetf_yn_for_hlg_hdr[65] = {
+	0,
+	668,	910,	1217,	1600,
+	2068,	2384,	2627,	3282,
+	3710,	4033,	4879,	5416,
+	5815,	6135,	6401,	6631,
+	6833,	7176,	7462,	7707,
+	7921,	8113,	8285,	8442,
+	8586,	8843,	9068,	9268,
+	9447,	9760,	10027,	10259,
+	10465,	10650,	10817,	10971,
+	11243,	11480,	11689,	11877,
+	12047,	12202,	12345,	12477,
+	12601,	12716,	12926,	13115,
+	13285,	13441,	13583,	13716,
+	13839,	13953,	14163,	14350,
+	14519,	14673,	14945,	15180,
+	15570,	15887,	16153,	16383,
+};
+
+static const u32 sdr2hdr_st2084oetf_yn_for_bt2020[65] = {
+	0,
+	0,     0,     1,     2,
+	4,     6,     9,     18,
+	27,    36,    72,    108,
+	144,   180,   216,   252,
+	288,   360,   432,   504,
+	576,   648,   720,   792,
+	864,   1008,  1152,  1296,
+	1444,  1706,  1945,  2166,
+	2372,  2566,  2750,  2924,
+	3251,  3553,  3834,  4099,
+	4350,  4588,  4816,  5035,
+	5245,  5447,  5832,  6194,
+	6536,  6862,  7173,  7471,
+	7758,  8035,  8560,  9055,
+	9523,  9968,  10800, 11569,
+	12963, 14210, 15347, 16383,
+};
+
+static u32 sdr2hdr_st2084oetf_yn_for_hdr[65] = {
+	0,
+	281,   418,   610,   871,
+	1217,  1464,  1662,  2218,
+	2599,  2896,  3699,  4228,
+	4628,  4953,  5227,  5466,
+	5676,  6038,  6341,  6602,
+	6833,  7039,  7226,  7396,
+	7554,  7835,  8082,  8302,
+	8501,  8848,  9145,  9405,
+	9635,  9842,  10031, 10204,
+	10512, 10779, 11017, 11230,
+	11423, 11599, 11762, 11913,
+	12054, 12185, 12426, 12641,
+	12835, 13013, 13177, 13328,
+	13469, 13600, 13840, 14055,
+	14248, 14425, 14737, 15006,
+	15453, 15816, 16121, 16383,
+};
+
+static const u32 sdr2hdr_st2084oetf_dxn_pow2[64] = {
+	0,  0,  1,  2,
+	3,  3,  3,  5,
+	5,  5,  7,  7,
+	7,  7,  7,  7,
+	7,  8,  8,  8,
+	8,  8,  8,  8,
+	8,  9,  9,  9,
+	9,  10, 10, 10,
+	10, 10, 10, 10,
+	11, 11, 11, 11,
+	11, 11, 11, 11,
+	11, 11, 12, 12,
+	12, 12, 12, 12,
+	12, 12, 13, 13,
+	13, 13, 14, 14,
+	15, 15, 15, 15,
+};
+
+static const u32 sdr2hdr_st2084oetf_dxn[64] = {
+	1,     1,     2,     4,
+	8,     8,     8,     32,
+	32,    32,    128,   128,
+	128,   128,   128,   128,
+	128,   256,   256,   256,
+	256,   256,   256,   256,
+	256,   512,   512,   512,
+	512,   1024,  1024,  1024,
+	1024,  1024,  1024,  1024,
+	2048,  2048,  2048,  2048,
+	2048,  2048,  2048,  2048,
+	2048,  2048,  4096,  4096,
+	4096,  4096,  4096,  4096,
+	4096,  4096,  8192,  8192,
+	8192,  8192,  16384, 16384,
+	32768, 32768, 32768, 32768,
+};
+
+static const u32 sdr2hdr_st2084oetf_xn[63] = {
+	1,      2,      4,      8,
+	16,     24,     32,     64,
+	96,     128,    256,    384,
+	512,    640,    768,    896,
+	1024,   1280,   1536,   1792,
+	2048,   2304,   2560,   2816,
+	3072,   3584,   4096,   4608,
+	5120,   6144,   7168,   8192,
+	9216,   10240,  11264,  12288,
+	14336,  16384,  18432,  20480,
+	22528,  24576,  26624,  28672,
+	30720,  32768,  36864,  40960,
+	45056,  49152,  53248,  57344,
+	61440,  65536,  73728,  81920,
+	90112,  98304,  114688, 131072,
+	163840, 196608, 229376,
+};
+
+static u32 hdr2sdr_eetf_yn[33] = {
+	1716,
+	1880,	2067,	2277,	2508,
+	2758,	3026,	3310,	3609,
+	3921,	4246,	4581,	4925,
+	5279,	5640,	6007,	6380,
+	6758,	7140,	7526,	7914,
+	8304,	8694,	9074,	9438,
+	9779,	10093,	10373,	10615,
+	10812,	10960,	11053,	11084,
+};
+
+static u32 hdr2sdr_bt1886oetf_yn[33] = {
+	0,
+	0,	0,	0,	0,
+	0,	0,	0,	314,
+	746,	1323,	2093,	2657,
+	3120,	3519,	3874,	4196,
+	4492,	5024,	5498,	5928,
+	6323,	7034,	7666,	8239,
+	8766,	9716,	10560,	11325,
+	12029,	13296,	14422,	16383,
 };
 
-static const int rk3368_vop_intrs[] = {
-	FS_INTR,
-	0, 0,
-	LINE_FLAG_INTR,
+static const u32 hdr2sdr_sat_yn[9] = {
 	0,
-	BUS_ERROR_INTR,
-	0, 0, 0, 0, 0, 0, 0,
-	DSP_HOLD_VALID_INTR,
+	1792, 3584, 3472, 2778,
+	2083, 1389, 694,  0,
+};
+
+static const struct vop_hdr_table rk3328_hdr_table = {
+	.hdr2sdr_eetf_oetf_y0_offset = RK3328_HDR2SDR_EETF_OETF_Y0,
+	.hdr2sdr_eetf_oetf_y1_offset = RK3328_HDR2SDR_EETF_OETF_Y1,
+	.hdr2sdr_eetf_yn	= hdr2sdr_eetf_yn,
+	.hdr2sdr_bt1886oetf_yn	= hdr2sdr_bt1886oetf_yn,
+	.hdr2sdr_sat_y0_offset = RK3328_HDR2DR_SAT_Y0,
+	.hdr2sdr_sat_y1_offset = RK3328_HDR2DR_SAT_Y1,
+	.hdr2sdr_sat_yn = hdr2sdr_sat_yn,
+
+	.hdr2sdr_src_range_min = 494,
+	.hdr2sdr_src_range_max = 12642,
+	.hdr2sdr_normfaceetf = 1327,
+	.hdr2sdr_dst_range_min = 4,
+	.hdr2sdr_dst_range_max = 3276,
+	.hdr2sdr_normfacgamma = 5120,
+
+	.sdr2hdr_eotf_oetf_y0_offset = RK3328_SDR2HDR_EOTF_OETF_Y0,
+	.sdr2hdr_eotf_oetf_y1_offset = RK3328_SDR2HDR_EOTF_OETF_Y1,
+	.sdr2hdr_bt1886eotf_yn_for_hlg_hdr = sdr2hdr_bt1886eotf_yn_for_hlg_hdr,
+	.sdr2hdr_bt1886eotf_yn_for_bt2020 = sdr2hdr_bt1886eotf_yn_for_bt2020,
+	.sdr2hdr_bt1886eotf_yn_for_hdr = sdr2hdr_bt1886eotf_yn_for_hdr,
+	.sdr2hdr_st2084oetf_yn_for_hlg_hdr = sdr2hdr_st2084oetf_yn_for_hlg_hdr,
+	.sdr2hdr_st2084oetf_yn_for_bt2020 = sdr2hdr_st2084oetf_yn_for_bt2020,
+	.sdr2hdr_st2084oetf_yn_for_hdr = sdr2hdr_st2084oetf_yn_for_hdr,
+	.sdr2hdr_oetf_dx_dxpow1_offset = RK3328_SDR2HDR_OETF_DX_DXPOW1,
+	.sdr2hdr_oetf_xn1_offset = RK3328_SDR2HDR_OETF_XN1,
+	.sdr2hdr_st2084oetf_dxn_pow2 = sdr2hdr_st2084oetf_dxn_pow2,
+	.sdr2hdr_st2084oetf_dxn = sdr2hdr_st2084oetf_dxn,
+	.sdr2hdr_st2084oetf_xn = sdr2hdr_st2084oetf_xn,
+};
+
+static const struct vop_ctrl rk3328_ctrl_data = {
+	.standby = VOP_REG(RK3328_SYS_CTRL, 0x1, 22),
+	.dma_stop = VOP_REG(RK3328_SYS_CTRL, 0x1, 21),
+	.axi_outstanding_max_num = VOP_REG(RK3328_SYS_CTRL1, 0x1f, 13),
+	.axi_max_outstanding_en = VOP_REG(RK3328_SYS_CTRL1, 0x1, 12),
+	.reg_done_frm = VOP_REG(RK3328_SYS_CTRL1, 0x1, 24),
+	.auto_gate_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 23),
+	.htotal_pw = VOP_REG(RK3328_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
+	.hact_st_end = VOP_REG(RK3328_DSP_HACT_ST_END, 0x1fff1fff, 0),
+	.vtotal_pw = VOP_REG(RK3328_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
+	.vact_st_end = VOP_REG(RK3328_DSP_VACT_ST_END, 0x1fff1fff, 0),
+	.vact_st_end_f1 = VOP_REG(RK3328_DSP_VACT_ST_END_F1, 0x1fff1fff, 0),
+	.vs_st_end_f1 = VOP_REG(RK3328_DSP_VS_ST_END_F1, 0x1fff1fff, 0),
+	.hpost_st_end = VOP_REG(RK3328_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
+	.vpost_st_end = VOP_REG(RK3328_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+	.vpost_st_end_f1 = VOP_REG(RK3328_POST_DSP_VACT_INFO_F1, 0x1fff1fff, 0),
+	.post_scl_factor = VOP_REG(RK3328_POST_SCL_FACTOR_YRGB, 0xffffffff, 0),
+	.post_scl_ctrl = VOP_REG(RK3328_POST_SCL_CTRL, 0x3, 0),
+	.dsp_out_yuv = VOP_REG(RK3328_POST_SCL_CTRL, 0x1, 2),
+	.dsp_interlace = VOP_REG(RK3328_DSP_CTRL0, 0x1, 10),
+	.dsp_layer_sel = VOP_REG(RK3328_DSP_CTRL1, 0xff, 8),
+	.post_lb_mode = VOP_REG(RK3328_SYS_CTRL, 0x1, 18),
+	.global_regdone_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 11),
+	.overlay_mode = VOP_REG(RK3328_SYS_CTRL, 0x1, 16),
+	.core_dclk_div = VOP_REG(RK3328_DSP_CTRL0, 0x1, 4),
+	.dclk_ddr = VOP_REG(RK3328_DSP_CTRL0, 0x1, 8),
+	.p2i_en = VOP_REG(RK3328_DSP_CTRL0, 0x1, 5),
+	.rgb_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 12),
+	.hdmi_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 13),
+	.edp_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 14),
+	.mipi_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 15),
+	.tve_dclk_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 24),
+	.tve_dclk_pol = VOP_REG(RK3328_SYS_CTRL, 0x1, 25),
+	.tve_sw_mode = VOP_REG(RK3328_SYS_CTRL, 0x1, 26),
+	.sw_uv_offset_en  = VOP_REG(RK3328_SYS_CTRL, 0x1, 27),
+	.sw_genlock   = VOP_REG(RK3328_SYS_CTRL, 0x1, 28),
+	.sw_dac_sel = VOP_REG(RK3328_SYS_CTRL, 0x1, 29),
+	.rgb_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0x7, 16),
+	.hdmi_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0x7, 20),
+	.edp_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0x7, 24),
+	.mipi_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0x7, 28),
+	.rgb_dclk_pol = VOP_REG(RK3328_DSP_CTRL1, 0x1, 19),
+	.hdmi_dclk_pol = VOP_REG(RK3328_DSP_CTRL1, 0x1, 23),
+	.edp_dclk_pol = VOP_REG(RK3328_DSP_CTRL1, 0x1, 27),
+	.mipi_dclk_pol = VOP_REG(RK3328_DSP_CTRL1, 0x1, 31),
+
+	.dither_down_sel = VOP_REG(RK3328_DSP_CTRL1, 0x1, 4),
+	.dither_down_mode = VOP_REG(RK3328_DSP_CTRL1, 0x1, 3),
+	.dither_down_en = VOP_REG(RK3328_DSP_CTRL1, 0x1, 2),
+	.pre_dither_down_en = VOP_REG(RK3328_DSP_CTRL1, 0x1, 1),
+	.dither_up_en = VOP_REG(RK3328_DSP_CTRL1, 0x1, 6),
+
+	.dsp_data_swap = VOP_REG(RK3328_DSP_CTRL0, 0x1f, 12),
+	.dsp_ccir656_avg = VOP_REG(RK3328_DSP_CTRL0, 0x1, 20),
+	.dsp_blank = VOP_REG(RK3328_DSP_CTRL0, 0x3, 18),
+	.dsp_lut_en = VOP_REG(RK3328_DSP_CTRL1, 0x1, 0),
+	.out_mode = VOP_REG(RK3328_DSP_CTRL0, 0xf, 0),
+
+	.xmirror = VOP_REG(RK3328_DSP_CTRL0, 0x1, 22),
+	.ymirror = VOP_REG(RK3328_DSP_CTRL0, 0x1, 23),
+
+	.dsp_background = VOP_REG(RK3328_DSP_BG, 0xffffffff, 0),
+
+	.alpha_hard_calc = VOP_REG(RK3328_SYS_CTRL1, 0x1, 27),
+	.level2_overlay_en = VOP_REG(RK3328_SYS_CTRL1, 0x1, 28),
+
+	.hdr2sdr_en = VOP_REG(RK3328_HDR2DR_CTRL, 0x1, 0),
+	.hdr2sdr_en_win0_csc = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 9),
+	.hdr2sdr_src_min = VOP_REG(RK3328_HDR2DR_SRC_RANGE, 0x3fff, 0),
+	.hdr2sdr_src_max = VOP_REG(RK3328_HDR2DR_SRC_RANGE, 0x3fff, 16),
+	.hdr2sdr_normfaceetf = VOP_REG(RK3328_HDR2DR_NORMFACEETF, 0x7ff, 0),
+	.hdr2sdr_dst_min = VOP_REG(RK3328_HDR2DR_DST_RANGE, 0x3fff, 0),
+	.hdr2sdr_dst_max = VOP_REG(RK3328_HDR2DR_DST_RANGE, 0x3fff, 16),
+	.hdr2sdr_normfacgamma = VOP_REG(RK3328_HDR2DR_NORMFACGAMMA, 0xffff, 0),
+
+	.bt1886eotf_pre_conv_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 0),
+	.rgb2rgb_pre_conv_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 1),
+	.rgb2rgb_pre_conv_mode = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 2),
+	.st2084oetf_pre_conv_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 3),
+	.bt1886eotf_post_conv_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 4),
+	.rgb2rgb_post_conv_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 5),
+	.rgb2rgb_post_conv_mode = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 6),
+	.st2084oetf_post_conv_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 7),
+	.win_csc_mode_sel = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 31),
+
+	.bcsh_brightness = VOP_REG(RK3328_BCSH_BCS, 0xff, 0),
+	.bcsh_contrast = VOP_REG(RK3328_BCSH_BCS, 0x1ff, 8),
+	.bcsh_sat_con = VOP_REG(RK3328_BCSH_BCS, 0x3ff, 20),
+	.bcsh_out_mode = VOP_REG(RK3328_BCSH_BCS, 0x3, 30),
+	.bcsh_sin_hue = VOP_REG(RK3328_BCSH_H, 0x1ff, 0),
+	.bcsh_cos_hue = VOP_REG(RK3328_BCSH_H, 0x1ff, 16),
+	.bcsh_r2y_csc_mode = VOP_REG(RK3328_BCSH_CTRL, 0x3, 6),
+	.bcsh_r2y_en = VOP_REG(RK3328_BCSH_CTRL, 0x1, 4),
+	.bcsh_y2r_csc_mode = VOP_REG(RK3328_BCSH_CTRL, 0x3, 2),
+	.bcsh_y2r_en = VOP_REG(RK3328_BCSH_CTRL, 0x1, 0),
+	.bcsh_color_bar = VOP_REG(RK3328_BCSH_COLOR_BAR, 0xffffff, 8),
+	.bcsh_en = VOP_REG(RK3328_BCSH_COLOR_BAR, 0x1, 0),
+
+	.cfg_done = VOP_REG(RK3328_REG_CFG_DONE, 0x1, 0),
 };
 
-static const struct vop_intr rk3368_vop_intr = {
+static const struct vop_intr rk3328_vop_intr = {
 	.intrs = rk3368_vop_intrs,
 	.nintrs = ARRAY_SIZE(rk3368_vop_intrs),
-	.line_flag_num[0] = VOP_REG(RK3368_LINE_FLAG, 0xffff, 0),
-	.line_flag_num[1] = VOP_REG(RK3368_LINE_FLAG, 0xffff, 16),
-	.status = VOP_REG_MASK_SYNC(RK3368_INTR_STATUS, 0x3fff, 0),
-	.enable = VOP_REG_MASK_SYNC(RK3368_INTR_EN, 0x3fff, 0),
-	.clear = VOP_REG_MASK_SYNC(RK3368_INTR_CLEAR, 0x3fff, 0),
+	.line_flag_num[0] = VOP_REG(RK3328_LINE_FLAG, 0xffff, 0),
+	.line_flag_num[1] = VOP_REG(RK3328_LINE_FLAG, 0xffff, 16),
+	.status = VOP_REG_MASK(RK3328_INTR_STATUS0, 0xffff, 0),
+	.enable = VOP_REG_MASK(RK3328_INTR_EN0, 0xffff, 0),
+	.clear = VOP_REG_MASK(RK3328_INTR_CLEAR0, 0xffff, 0),
 };
 
-static const struct vop_win_phy rk3368_win01_data = {
-	.scl = &rk3288_win_full_scl,
-	.data_formats = formats_win_full,
-	.nformats = ARRAY_SIZE(formats_win_full),
-	.format_modifiers = format_modifiers_win_full,
-	.enable = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 0),
-	.format = VOP_REG(RK3368_WIN0_CTRL0, 0x7, 1),
-	.rb_swap = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 12),
-	.x_mir_en = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 21),
-	.y_mir_en = VOP_REG(RK3368_WIN0_CTRL0, 0x1, 22),
-	.act_info = VOP_REG(RK3368_WIN0_ACT_INFO, 0x1fff1fff, 0),
-	.dsp_info = VOP_REG(RK3368_WIN0_DSP_INFO, 0x0fff0fff, 0),
-	.dsp_st = VOP_REG(RK3368_WIN0_DSP_ST, 0x1fff1fff, 0),
-	.yrgb_mst = VOP_REG(RK3368_WIN0_YRGB_MST, 0xffffffff, 0),
-	.uv_mst = VOP_REG(RK3368_WIN0_CBR_MST, 0xffffffff, 0),
-	.yrgb_vir = VOP_REG(RK3368_WIN0_VIR, 0x3fff, 0),
-	.uv_vir = VOP_REG(RK3368_WIN0_VIR, 0x3fff, 16),
-	.src_alpha_ctl = VOP_REG(RK3368_WIN0_SRC_ALPHA_CTRL, 0xff, 0),
-	.dst_alpha_ctl = VOP_REG(RK3368_WIN0_DST_ALPHA_CTRL, 0xff, 0),
-	.channel = VOP_REG(RK3368_WIN0_CTRL2, 0xff, 0),
+static const struct vop_csc rk3328_win0_csc = {
+	.r2y_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 8),
+	.r2r_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 5),
+	.y2r_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 9),
 };
 
-static const struct vop_win_phy rk3368_win23_data = {
-	.data_formats = formats_win_lite,
-	.nformats = ARRAY_SIZE(formats_win_lite),
-	.format_modifiers = format_modifiers_win_lite,
-	.gate = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 0),
-	.enable = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 4),
-	.format = VOP_REG(RK3368_WIN2_CTRL0, 0x3, 5),
-	.rb_swap = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 20),
-	.y_mir_en = VOP_REG(RK3368_WIN2_CTRL1, 0x1, 15),
-	.dsp_info = VOP_REG(RK3368_WIN2_DSP_INFO0, 0x0fff0fff, 0),
-	.dsp_st = VOP_REG(RK3368_WIN2_DSP_ST0, 0x1fff1fff, 0),
-	.yrgb_mst = VOP_REG(RK3368_WIN2_MST0, 0xffffffff, 0),
-	.yrgb_vir = VOP_REG(RK3368_WIN2_VIR0_1, 0x1fff, 0),
-	.src_alpha_ctl = VOP_REG(RK3368_WIN2_SRC_ALPHA_CTRL, 0xff, 0),
-	.dst_alpha_ctl = VOP_REG(RK3368_WIN2_DST_ALPHA_CTRL, 0xff, 0),
+static const struct vop_csc rk3328_win1_csc = {
+	.r2y_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 10),
+	.r2r_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 1),
+	.y2r_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 11),
 };
 
-static const struct vop_win_data rk3368_vop_win_data[] = {
-	{ .base = 0x00, .phy = &rk3368_win01_data,
-	  .type = DRM_PLANE_TYPE_PRIMARY },
-	{ .base = 0x40, .phy = &rk3368_win01_data,
-	  .type = DRM_PLANE_TYPE_OVERLAY },
-	{ .base = 0x00, .phy = &rk3368_win23_data,
-	  .type = DRM_PLANE_TYPE_OVERLAY },
-	{ .base = 0x50, .phy = &rk3368_win23_data,
-	  .type = DRM_PLANE_TYPE_CURSOR },
+static const struct vop_csc rk3328_win2_csc = {
+	.r2y_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 12),
+	.r2r_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 1),
+	.y2r_en = VOP_REG(RK3328_SDR2HDR_CTRL, 0x1, 13),
 };
 
-static const struct vop_output rk3368_output = {
-	.rgb_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 19),
-	.hdmi_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 23),
-	.edp_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 27),
-	.mipi_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 31),
-	.rgb_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 16),
-	.hdmi_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 20),
-	.edp_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 24),
-	.mipi_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 28),
-	.rgb_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 12),
-	.hdmi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 13),
-	.edp_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 14),
-	.mipi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 15),
+static const struct vop_win_data rk3328_vop_win_data[] = {
+	{ .base = 0xd0, .phy = &rk3288_win01_data,  .csc = &rk3328_win0_csc,
+	  .type = DRM_PLANE_TYPE_PRIMARY,
+	  .feature = WIN_FEATURE_HDR2SDR | WIN_FEATURE_SDR2HDR },
+	{ .base = 0x1d0, .phy = &rk3288_win01_data, .csc = &rk3328_win1_csc,
+	  .type = DRM_PLANE_TYPE_OVERLAY,
+	  .feature = WIN_FEATURE_SDR2HDR | WIN_FEATURE_PRE_OVERLAY },
+	{ .base = 0x2d0, .phy = &rk3288_win01_data, .csc = &rk3328_win2_csc,
+	  .type = DRM_PLANE_TYPE_CURSOR,
+	  .feature = WIN_FEATURE_SDR2HDR | WIN_FEATURE_PRE_OVERLAY },
 };
 
-static const struct vop_misc rk3368_misc = {
-	.global_regdone_en = VOP_REG(RK3368_SYS_CTRL, 0x1, 11),
+static const struct vop_data rk3328_vop = {
+	.soc_id = 0x3328,
+	.vop_id = 0,
+	.version = VOP_VERSION(3, 8),
+	.feature = VOP_FEATURE_OUTPUT_10BIT | VOP_FEATURE_HDR10 |
+			VOP_FEATURE_ALPHA_SCALE | VOP_FEATURE_OVERSCAN,
+	.hdr_table = &rk3328_hdr_table,
+	.max_input = {4096, 8192},
+	.max_output = {4096, 2160},
+	.intr = &rk3328_vop_intr,
+	.ctrl = &rk3328_ctrl_data,
+	.win = rk3328_vop_win_data,
+	.win_size = ARRAY_SIZE(rk3328_vop_win_data),
 };
 
-static const struct vop_data rk3368_vop = {
-	.version = VOP_VERSION(3, 2),
-	.intr = &rk3368_vop_intr,
-	.common = &rk3288_common,
-	.modeset = &rk3288_modeset,
-	.output = &rk3368_output,
-	.misc = &rk3368_misc,
-	.win = rk3368_vop_win_data,
-	.win_size = ARRAY_SIZE(rk3368_vop_win_data),
+static const struct vop_scl_regs rk3036_win0_scl = {
+	.scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
+	.scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
+	.scale_cbcr_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
+	.scale_cbcr_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
 };
 
-static const struct vop_intr rk3366_vop_intr = {
-	.intrs = rk3368_vop_intrs,
-	.nintrs = ARRAY_SIZE(rk3368_vop_intrs),
-	.line_flag_num[0] = VOP_REG(RK3366_LINE_FLAG, 0xffff, 0),
-	.line_flag_num[1] = VOP_REG(RK3366_LINE_FLAG, 0xffff, 16),
-	.status = VOP_REG_MASK_SYNC(RK3366_INTR_STATUS0, 0xffff, 0),
-	.enable = VOP_REG_MASK_SYNC(RK3366_INTR_EN0, 0xffff, 0),
-	.clear = VOP_REG_MASK_SYNC(RK3366_INTR_CLEAR0, 0xffff, 0),
+static const struct vop_scl_regs rk3036_win1_scl = {
+	.scale_yrgb_x = VOP_REG(RK3036_WIN1_SCL_FACTOR_YRGB, 0xffff, 0x0),
+	.scale_yrgb_y = VOP_REG(RK3036_WIN1_SCL_FACTOR_YRGB, 0xffff, 16),
 };
 
-static const struct vop_data rk3366_vop = {
-	.version = VOP_VERSION(3, 4),
-	.intr = &rk3366_vop_intr,
-	.common = &rk3288_common,
-	.modeset = &rk3288_modeset,
-	.output = &rk3368_output,
-	.misc = &rk3368_misc,
-	.win = rk3368_vop_win_data,
-	.win_size = ARRAY_SIZE(rk3368_vop_win_data),
+static const struct vop_win_phy rk3036_win0_data = {
+	.scl = &rk3036_win0_scl,
+	.data_formats = formats_win_full,
+	.nformats = ARRAY_SIZE(formats_win_full),
+	.enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 0),
+	.format = VOP_REG(RK3036_SYS_CTRL, 0x7, 3),
+	.rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 15),
+	.act_info = VOP_REG(RK3036_WIN0_ACT_INFO, 0x1fff1fff, 0),
+	.dsp_info = VOP_REG(RK3036_WIN0_DSP_INFO, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3036_WIN0_DSP_ST, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3036_WIN0_YRGB_MST, 0xffffffff, 0),
+	.uv_mst = VOP_REG(RK3036_WIN0_CBR_MST, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3036_WIN0_VIR, 0xffff, 0),
+	.uv_vir = VOP_REG(RK3036_WIN0_VIR, 0x1fff, 16),
+	.alpha_mode = VOP_REG(RK3036_DSP_CTRL0, 0x1, 18),
+	.alpha_en = VOP_REG(RK3036_ALPHA_CTRL, 0x1, 0),
+	.alpha_pre_mul = VOP_REG(RK3036_DSP_CTRL0, 0x1, 29),
 };
 
-static const struct vop_output rk3399_output = {
-	.dp_dclk_pol = VOP_REG(RK3399_DSP_CTRL1, 0x1, 19),
-	.rgb_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 19),
-	.hdmi_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 23),
-	.edp_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 27),
-	.mipi_dclk_pol = VOP_REG(RK3368_DSP_CTRL1, 0x1, 31),
-	.dp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0x7, 16),
-	.rgb_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 16),
-	.hdmi_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 20),
-	.edp_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 24),
-	.mipi_pin_pol = VOP_REG(RK3368_DSP_CTRL1, 0x7, 28),
-	.dp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11),
-	.rgb_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 12),
-	.hdmi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 13),
-	.edp_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 14),
-	.mipi_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 15),
-	.mipi_dual_channel_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 3),
+static const struct vop_win_phy rk3036_win1_data = {
+	.scl = &rk3036_win1_scl,
+	.data_formats = formats_win_lite,
+	.nformats = ARRAY_SIZE(formats_win_lite),
+	.enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1),
+	.format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6),
+	.rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19),
+	.act_info = VOP_REG(RK3036_WIN1_ACT_INFO, 0x1fff1fff, 0),
+	.dsp_info = VOP_REG(RK3036_WIN1_DSP_INFO, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3036_WIN1_DSP_ST, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3036_WIN1_MST, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3036_WIN1_VIR, 0xffff, 0),
+	.alpha_mode = VOP_REG(RK3036_DSP_CTRL0, 0x1, 19),
+	.alpha_en = VOP_REG(RK3036_ALPHA_CTRL, 0x1, 1)
 };
 
-static const struct vop_yuv2yuv_phy rk3399_yuv2yuv_win01_data = {
-	.y2r_coefficients = {
-		VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 0, 0xffff, 0),
-		VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 0, 0xffff, 16),
-		VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 4, 0xffff, 0),
-		VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 4, 0xffff, 16),
-		VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 8, 0xffff, 0),
-		VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 8, 0xffff, 16),
-		VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 12, 0xffff, 0),
-		VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 12, 0xffff, 16),
-		VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 16, 0xffff, 0),
-		VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 20, 0xffffffff, 0),
-		VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 24, 0xffffffff, 0),
-		VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 28, 0xffffffff, 0),
-	},
+static const struct vop_win_data rk3036_vop_win_data[] = {
+	{ .base = 0x00, .phy = &rk3036_win0_data,
+	  .type = DRM_PLANE_TYPE_PRIMARY },
+	{ .base = 0x00, .phy = &rk3036_win1_data,
+	  .type = DRM_PLANE_TYPE_OVERLAY },
 };
 
-static const struct vop_yuv2yuv_phy rk3399_yuv2yuv_win23_data = { };
+static const int rk3036_vop_intrs[] = {
+	DSP_HOLD_VALID_INTR,
+	FS_INTR,
+	LINE_FLAG_INTR,
+	BUS_ERROR_INTR,
+};
 
-static const struct vop_win_yuv2yuv_data rk3399_vop_big_win_yuv2yuv_data[] = {
-	{ .base = 0x00, .phy = &rk3399_yuv2yuv_win01_data,
-	  .y2r_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 1) },
-	{ .base = 0x60, .phy = &rk3399_yuv2yuv_win01_data,
-	  .y2r_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 9) },
-	{ .base = 0xC0, .phy = &rk3399_yuv2yuv_win23_data },
-	{ .base = 0x120, .phy = &rk3399_yuv2yuv_win23_data },
+static const struct vop_intr rk3036_intr = {
+	.intrs = rk3036_vop_intrs,
+	.nintrs = ARRAY_SIZE(rk3036_vop_intrs),
+	.line_flag_num[0] = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
+	.status = VOP_REG(RK3036_INT_STATUS, 0xf, 0),
+	.enable = VOP_REG(RK3036_INT_STATUS, 0xf, 4),
+	.clear = VOP_REG(RK3036_INT_STATUS, 0xf, 8),
+};
+
+static const struct vop_ctrl rk3036_ctrl_data = {
+	.standby = VOP_REG(RK3036_SYS_CTRL, 0x1, 30),
+	.out_mode = VOP_REG(RK3036_DSP_CTRL0, 0xf, 0),
+	.dsp_blank = VOP_REG(RK3036_DSP_CTRL1, 0x1, 24),
+	.dclk_pol = VOP_REG(RK3036_DSP_CTRL0, 0x1, 7),
+	.pin_pol = VOP_REG(RK3036_DSP_CTRL0, 0x7, 4),
+	.dither_down_sel = VOP_REG(RK3036_DSP_CTRL0, 0x1, 27),
+	.dither_down_en = VOP_REG(RK3036_DSP_CTRL0, 0x1, 11),
+	.dither_down_mode = VOP_REG(RK3036_DSP_CTRL0, 0x1, 10),
+	.dither_up_en = VOP_REG(RK3036_DSP_CTRL0, 0x1, 9),
+	.dsp_layer_sel = VOP_REG(RK3036_DSP_CTRL0, 0x1, 8),
+	.htotal_pw = VOP_REG(RK3036_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
+	.hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
+	.hdmi_en = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 22),
+	.hdmi_dclk_pol = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 23),
+	.hdmi_pin_pol = VOP_REG(RK3036_INT_SCALER, 0x7, 4),
+	.rgb_en = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 24),
+	.rgb_dclk_pol = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 25),
+	.lvds_en = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 26),
+	.lvds_dclk_pol = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 27),
+	.mipi_en = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 28),
+	.mipi_dclk_pol = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 29),
+	.vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
+	.vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
+	.cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
+};
 
+static const struct vop_data rk3036_vop = {
+	.soc_id = 0x3036,
+	.vop_id = 0,
+	.version = VOP_VERSION(2, 2),
+	.max_input = {1920, 1080},
+	.max_output = {1920, 1080},
+	.ctrl = &rk3036_ctrl_data,
+	.intr = &rk3036_intr,
+	.win = rk3036_vop_win_data,
+	.win_size = ARRAY_SIZE(rk3036_vop_win_data),
 };
 
-static const struct vop_win_phy rk3399_win01_data = {
-	.scl = &rk3288_win_full_scl,
+static const struct vop_scl_regs rk3066_win_scl = {
+	.scale_yrgb_x = VOP_REG(RK3066_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
+	.scale_yrgb_y = VOP_REG(RK3066_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
+	.scale_cbcr_x = VOP_REG(RK3066_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
+	.scale_cbcr_y = VOP_REG(RK3066_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
+};
+
+static const struct vop_win_phy rk3066_win0_data = {
+	.scl = &rk3066_win_scl,
 	.data_formats = formats_win_full,
 	.nformats = ARRAY_SIZE(formats_win_full),
-	.format_modifiers = format_modifiers_win_full_afbc,
-	.enable = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 0),
-	.format = VOP_REG(RK3288_WIN0_CTRL0, 0x7, 1),
-	.rb_swap = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 12),
-	.x_mir_en = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 21),
-	.y_mir_en = VOP_REG(RK3288_WIN0_CTRL0, 0x1, 22),
-	.act_info = VOP_REG(RK3288_WIN0_ACT_INFO, 0x1fff1fff, 0),
-	.dsp_info = VOP_REG(RK3288_WIN0_DSP_INFO, 0x0fff0fff, 0),
-	.dsp_st = VOP_REG(RK3288_WIN0_DSP_ST, 0x1fff1fff, 0),
-	.yrgb_mst = VOP_REG(RK3288_WIN0_YRGB_MST, 0xffffffff, 0),
-	.uv_mst = VOP_REG(RK3288_WIN0_CBR_MST, 0xffffffff, 0),
-	.yrgb_vir = VOP_REG(RK3288_WIN0_VIR, 0x3fff, 0),
-	.uv_vir = VOP_REG(RK3288_WIN0_VIR, 0x3fff, 16),
-	.src_alpha_ctl = VOP_REG(RK3288_WIN0_SRC_ALPHA_CTRL, 0xff, 0),
-	.dst_alpha_ctl = VOP_REG(RK3288_WIN0_DST_ALPHA_CTRL, 0xff, 0),
-	.channel = VOP_REG(RK3288_WIN0_CTRL2, 0xff, 0),
+	.enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 0),
+	.format = VOP_REG(RK3066_SYS_CTRL0, 0x7, 4),
+	.rb_swap = VOP_REG(RK3066_SYS_CTRL0, 0x1, 19),
+	.act_info = VOP_REG(RK3066_WIN0_ACT_INFO, 0x1fff1fff, 0),
+	.dsp_info = VOP_REG(RK3066_WIN0_DSP_INFO, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3066_WIN0_DSP_ST, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3066_WIN0_YRGB_MST0, 0xffffffff, 0),
+	.uv_mst = VOP_REG(RK3066_WIN0_CBR_MST0, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3066_WIN0_VIR, 0xffff, 0),
+	.uv_vir = VOP_REG(RK3066_WIN0_VIR, 0x1fff, 16),
+	.alpha_mode = VOP_REG(RK3066_DSP_CTRL0, 0x1, 21),
+	.alpha_en = VOP_REG(RK3066_BLEND_CTRL, 0x1, 0)
 };
 
-/*
- * rk3399 vop big windows register layout is same as rk3288, but we
- * have a separate rk3399 win data array here so that we can advertise
- * AFBC on the primary plane.
- */
-static const struct vop_win_data rk3399_vop_win_data[] = {
-	{ .base = 0x00, .phy = &rk3399_win01_data,
+static const struct vop_win_phy rk3066_win1_data = {
+	.scl = &rk3066_win_scl,
+	.data_formats = formats_win_full,
+	.nformats = ARRAY_SIZE(formats_win_full),
+	.enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 1),
+	.format = VOP_REG(RK3066_SYS_CTRL0, 0x7, 7),
+	.rb_swap = VOP_REG(RK3066_SYS_CTRL0, 0x1, 23),
+	.act_info = VOP_REG(RK3066_WIN1_ACT_INFO, 0x1fff1fff, 0),
+	.dsp_info = VOP_REG(RK3066_WIN1_DSP_INFO, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3066_WIN1_DSP_ST, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3066_WIN1_YRGB_MST, 0xffffffff, 0),
+	.uv_mst = VOP_REG(RK3066_WIN1_CBR_MST, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3066_WIN1_VIR, 0xffff, 0),
+	.uv_vir = VOP_REG(RK3066_WIN1_VIR, 0x1fff, 16),
+	.alpha_mode = VOP_REG(RK3066_DSP_CTRL0, 0x1, 22),
+	.alpha_en = VOP_REG(RK3066_BLEND_CTRL, 0x1, 1)
+};
+
+static const struct vop_win_phy rk3066_win2_data = {
+	.data_formats = formats_win_lite,
+	.nformats = ARRAY_SIZE(formats_win_lite),
+	.enable = VOP_REG(RK3066_SYS_CTRL1, 0x1, 2),
+	.format = VOP_REG(RK3066_SYS_CTRL0, 0x7, 10),
+	.rb_swap = VOP_REG(RK3066_SYS_CTRL0, 0x1, 27),
+	.dsp_info = VOP_REG(RK3066_WIN2_DSP_INFO, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3066_WIN2_DSP_ST, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3066_WIN2_MST, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3066_WIN2_VIR, 0xffff, 0),
+	.alpha_mode = VOP_REG(RK3066_DSP_CTRL0, 0x1, 23),
+	.alpha_en = VOP_REG(RK3066_BLEND_CTRL, 0x1, 2)
+};
+
+static const struct vop_win_data rk3066_vop_win_data[] = {
+	{ .base = 0x00, .phy = &rk3066_win0_data,
 	  .type = DRM_PLANE_TYPE_PRIMARY },
-	{ .base = 0x40, .phy = &rk3368_win01_data,
-	  .type = DRM_PLANE_TYPE_OVERLAY },
-	{ .base = 0x00, .phy = &rk3368_win23_data,
+	{ .base = 0x00, .phy = &rk3066_win1_data,
 	  .type = DRM_PLANE_TYPE_OVERLAY },
-	{ .base = 0x50, .phy = &rk3368_win23_data,
+	{ .base = 0x00, .phy = &rk3066_win2_data,
 	  .type = DRM_PLANE_TYPE_CURSOR },
 };
 
-static const struct vop_afbc rk3399_vop_afbc = {
-	.rstn = VOP_REG(RK3399_AFBCD0_CTRL, 0x1, 3),
-	.enable = VOP_REG(RK3399_AFBCD0_CTRL, 0x1, 0),
-	.win_sel = VOP_REG(RK3399_AFBCD0_CTRL, 0x3, 1),
-	.format = VOP_REG(RK3399_AFBCD0_CTRL, 0x1f, 16),
-	.hreg_block_split = VOP_REG(RK3399_AFBCD0_CTRL, 0x1, 21),
-	.hdr_ptr = VOP_REG(RK3399_AFBCD0_HDR_PTR, 0xffffffff, 0),
-	.pic_size = VOP_REG(RK3399_AFBCD0_PIC_SIZE, 0xffffffff, 0),
+static const int rk3066_vop_intrs[] = {
+	0,
+	FS_INTR,
+	LINE_FLAG_INTR,
+	BUS_ERROR_INTR,
 };
 
-static const struct vop_data rk3399_vop_big = {
-	.version = VOP_VERSION(3, 5),
-	.feature = VOP_FEATURE_OUTPUT_RGB10,
-	.intr = &rk3366_vop_intr,
-	.common = &rk3288_common,
-	.modeset = &rk3288_modeset,
-	.output = &rk3399_output,
-	.afbc = &rk3399_vop_afbc,
-	.misc = &rk3368_misc,
-	.win = rk3399_vop_win_data,
-	.win_size = ARRAY_SIZE(rk3399_vop_win_data),
-	.win_yuv2yuv = rk3399_vop_big_win_yuv2yuv_data,
+static const struct vop_intr rk3066_intr = {
+	.intrs = rk3066_vop_intrs,
+	.nintrs = ARRAY_SIZE(rk3066_vop_intrs),
+	.line_flag_num[0] = VOP_REG(RK3066_INT_STATUS, 0xfff, 12),
+	.status = VOP_REG(RK3066_INT_STATUS, 0xf, 0),
+	.enable = VOP_REG(RK3066_INT_STATUS, 0xf, 4),
+	.clear = VOP_REG(RK3066_INT_STATUS, 0xf, 8),
 };
 
-static const struct vop_win_data rk3399_vop_lit_win_data[] = {
-	{ .base = 0x00, .phy = &rk3368_win01_data,
-	  .type = DRM_PLANE_TYPE_PRIMARY },
-	{ .base = 0x00, .phy = &rk3368_win23_data,
-	  .type = DRM_PLANE_TYPE_CURSOR},
+static const struct vop_ctrl rk3066_ctrl_data = {
+	.standby = VOP_REG(RK3066_SYS_CTRL0, 0x1, 1),
+	.out_mode = VOP_REG(RK3066_DSP_CTRL0, 0xf, 0),
+	.dsp_blank = VOP_REG(RK3066_DSP_CTRL1, 0x1, 24),
+	.dclk_pol = VOP_REG(RK3066_DSP_CTRL0, 0x1, 7),
+	.pin_pol = VOP_REG(RK3066_DSP_CTRL0, 0x7, 4),
+	.dsp_layer_sel = VOP_REG(RK3066_DSP_CTRL0, 0x1, 8),
+	.htotal_pw = VOP_REG(RK3066_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
+	.hact_st_end = VOP_REG(RK3066_DSP_HACT_ST_END, 0x1fff1fff, 0),
+	.vtotal_pw = VOP_REG(RK3066_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
+	.vact_st_end = VOP_REG(RK3066_DSP_VACT_ST_END, 0x1fff1fff, 0),
+	.cfg_done = VOP_REG(RK3066_REG_CFG_DONE, 0x1, 0),
 };
 
-static const struct vop_win_yuv2yuv_data rk3399_vop_lit_win_yuv2yuv_data[] = {
-	{ .base = 0x00, .phy = &rk3399_yuv2yuv_win01_data,
-	  .y2r_en = VOP_REG(RK3399_YUV2YUV_WIN, 0x1, 1)},
-	{ .base = 0x60, .phy = &rk3399_yuv2yuv_win23_data },
+static const struct vop_data rk3066_vop = {
+	.soc_id = 0x3066,
+	.vop_id = 0,
+	.version = VOP_VERSION(2, 1),
+	.max_input = {1920, 4096},
+	.max_output = {1920, 1080},
+	.ctrl = &rk3066_ctrl_data,
+	.intr = &rk3066_intr,
+	.win = rk3066_vop_win_data,
+	.win_size = ARRAY_SIZE(rk3066_vop_win_data),
 };
 
-static const struct vop_data rk3399_vop_lit = {
-	.version = VOP_VERSION(3, 6),
-	.intr = &rk3366_vop_intr,
-	.common = &rk3288_common,
-	.modeset = &rk3288_modeset,
-	.output = &rk3399_output,
-	.misc = &rk3368_misc,
-	.win = rk3399_vop_lit_win_data,
-	.win_size = ARRAY_SIZE(rk3399_vop_lit_win_data),
-	.win_yuv2yuv = rk3399_vop_lit_win_yuv2yuv_data,
+static const int rk3366_vop_lit_intrs[] = {
+	FS_INTR,
+	FS_NEW_INTR,
+	ADDR_SAME_INTR,
+	LINE_FLAG_INTR,
+	LINE_FLAG1_INTR,
+	BUS_ERROR_INTR,
+	WIN0_EMPTY_INTR,
+	WIN1_EMPTY_INTR,
+	DSP_HOLD_VALID_INTR,
+	DMA_FINISH_INTR,
+	WIN2_EMPTY_INTR,
+	POST_BUF_EMPTY_INTR
 };
 
-static const struct vop_win_data rk3228_vop_win_data[] = {
-	{ .base = 0x00, .phy = &rk3288_win01_data,
+static const struct vop_scl_regs rk3366_lit_win_scl = {
+	.scale_yrgb_x = VOP_REG(RK3366_LIT_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
+	.scale_yrgb_y = VOP_REG(RK3366_LIT_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
+	.scale_cbcr_x = VOP_REG(RK3366_LIT_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
+	.scale_cbcr_y = VOP_REG(RK3366_LIT_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
+};
+
+static const struct vop_win_phy rk3366_lit_win0_data = {
+	.scl = &rk3366_lit_win_scl,
+	.data_formats = formats_win_full,
+	.nformats = ARRAY_SIZE(formats_win_full),
+
+	.enable = VOP_REG(RK3366_LIT_WIN0_CTRL0, 0x1, 0),
+	.format = VOP_REG(RK3366_LIT_WIN0_CTRL0, 0x7, 1),
+	.rb_swap = VOP_REG(RK3366_LIT_WIN0_CTRL0, 0x1, 12),
+	.act_info = VOP_REG(RK3366_LIT_WIN0_ACT_INFO, 0xffffffff, 0),
+	.dsp_info = VOP_REG(RK3366_LIT_WIN0_DSP_INFO, 0xffffffff, 0),
+	.dsp_st = VOP_REG(RK3366_LIT_WIN0_DSP_ST, 0xffffffff, 0),
+	.yrgb_mst = VOP_REG(RK3366_LIT_WIN0_YRGB_MST0, 0xffffffff, 0),
+	.uv_mst = VOP_REG(RK3366_LIT_WIN0_CBR_MST0, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3366_LIT_WIN0_VIR, 0x1fff, 0),
+	.uv_vir = VOP_REG(RK3366_LIT_WIN0_VIR, 0x1fff, 16),
+
+	.alpha_pre_mul = VOP_REG(RK3366_LIT_WIN0_ALPHA_CTRL, 0x1, 2),
+	.alpha_mode = VOP_REG(RK3366_LIT_WIN0_ALPHA_CTRL, 0x1, 1),
+	.alpha_en = VOP_REG(RK3366_LIT_WIN0_ALPHA_CTRL, 0x1, 0),
+	.global_alpha_val = VOP_REG(RK3366_LIT_WIN0_ALPHA_CTRL, 0xff, 4),
+	.key_color = VOP_REG(RK3366_LIT_WIN0_COLOR_KEY, 0xffffff, 0),
+	.key_en = VOP_REG(RK3366_LIT_WIN0_COLOR_KEY, 0x1, 24),
+};
+
+static const struct vop_win_phy rk3366_lit_win1_data = {
+	.data_formats = formats_win_lite,
+	.nformats = ARRAY_SIZE(formats_win_lite),
+
+	.enable = VOP_REG(RK3366_LIT_WIN1_CTRL0, 0x1, 0),
+	.format = VOP_REG(RK3366_LIT_WIN1_CTRL0, 0x7, 4),
+	.rb_swap = VOP_REG(RK3366_LIT_WIN1_CTRL0, 0x1, 12),
+	.dsp_info = VOP_REG(RK3366_LIT_WIN1_DSP_INFO, 0xffffffff, 0),
+	.dsp_st = VOP_REG(RK3366_LIT_WIN1_DSP_ST, 0xffffffff, 0),
+	.yrgb_mst = VOP_REG(RK3366_LIT_WIN1_MST, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3366_LIT_WIN1_VIR, 0x1fff, 0),
+
+	.alpha_pre_mul = VOP_REG(RK3366_LIT_WIN1_ALPHA_CTRL, 0x1, 2),
+	.alpha_mode = VOP_REG(RK3366_LIT_WIN1_ALPHA_CTRL, 0x1, 1),
+	.alpha_en = VOP_REG(RK3366_LIT_WIN1_ALPHA_CTRL, 0x1, 0),
+	.global_alpha_val = VOP_REG(RK3366_LIT_WIN1_ALPHA_CTRL, 0xff, 4),
+	.key_color = VOP_REG(RK3366_LIT_WIN1_COLOR_KEY, 0xffffff, 0),
+	.key_en = VOP_REG(RK3366_LIT_WIN1_COLOR_KEY, 0x1, 24),
+};
+
+static const struct vop_win_data rk3366_vop_lit_win_data[] = {
+	{ .base = 0x00, .phy = &rk3366_lit_win0_data,
 	  .type = DRM_PLANE_TYPE_PRIMARY },
-	{ .base = 0x40, .phy = &rk3288_win01_data,
+	{ .base = 0x00, .phy = &rk3366_lit_win1_data,
 	  .type = DRM_PLANE_TYPE_CURSOR },
 };
 
-static const struct vop_data rk3228_vop = {
-	.version = VOP_VERSION(3, 7),
-	.feature = VOP_FEATURE_OUTPUT_RGB10,
-	.intr = &rk3366_vop_intr,
-	.common = &rk3288_common,
-	.modeset = &rk3288_modeset,
-	.output = &rk3399_output,
-	.misc = &rk3368_misc,
-	.win = rk3228_vop_win_data,
-	.win_size = ARRAY_SIZE(rk3228_vop_win_data),
+static const struct vop_intr rk3366_lit_intr = {
+	.intrs = rk3366_vop_lit_intrs,
+	.nintrs = ARRAY_SIZE(rk3366_vop_lit_intrs),
+	.line_flag_num[0] = VOP_REG(RK3366_LIT_LINE_FLAG, 0xfff, 0),
+	.line_flag_num[1] = VOP_REG(RK3366_LIT_LINE_FLAG, 0xfff, 16),
+	.status = VOP_REG_MASK(RK3366_LIT_INTR_STATUS, 0xffff, 0),
+	.enable = VOP_REG_MASK(RK3366_LIT_INTR_EN, 0xffff, 0),
+	.clear = VOP_REG_MASK(RK3366_LIT_INTR_CLEAR, 0xffff, 0),
 };
 
-static const struct vop_modeset rk3328_modeset = {
-	.htotal_pw = VOP_REG(RK3328_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
-	.hact_st_end = VOP_REG(RK3328_DSP_HACT_ST_END, 0x1fff1fff, 0),
-	.vtotal_pw = VOP_REG(RK3328_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
-	.vact_st_end = VOP_REG(RK3328_DSP_VACT_ST_END, 0x1fff1fff, 0),
-	.hpost_st_end = VOP_REG(RK3328_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
-	.vpost_st_end = VOP_REG(RK3328_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+static const struct vop_win_phy rk3126_win1_data = {
+	.data_formats = formats_win_lite,
+	.nformats = ARRAY_SIZE(formats_win_lite),
+	.enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1),
+	.format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6),
+	.rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19),
+	.dsp_info = VOP_REG(RK3126_WIN1_DSP_INFO, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3126_WIN1_DSP_ST, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3126_WIN1_MST, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3036_WIN1_VIR, 0xffff, 0),
+	.alpha_mode = VOP_REG(RK3036_DSP_CTRL0, 0x1, 19),
+	.alpha_en = VOP_REG(RK3036_ALPHA_CTRL, 0x1, 1),
+	.alpha_pre_mul = VOP_REG(RK3036_DSP_CTRL0, 0x1, 29),
 };
 
-static const struct vop_output rk3328_output = {
-	.rgb_dclk_pol = VOP_REG(RK3328_DSP_CTRL1, 0x1, 19),
-	.hdmi_dclk_pol = VOP_REG(RK3328_DSP_CTRL1, 0x1, 23),
-	.edp_dclk_pol = VOP_REG(RK3328_DSP_CTRL1, 0x1, 27),
-	.mipi_dclk_pol = VOP_REG(RK3328_DSP_CTRL1, 0x1, 31),
-	.rgb_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 12),
-	.hdmi_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 13),
-	.edp_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 14),
-	.mipi_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 15),
-	.rgb_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0x7, 16),
-	.hdmi_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0x7, 20),
-	.edp_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0x7, 24),
-	.mipi_pin_pol = VOP_REG(RK3328_DSP_CTRL1, 0x7, 28),
+static const struct vop_win_data rk3126_vop_win_data[] = {
+	{ .base = 0x00, .phy = &rk3036_win0_data,
+	  .type = DRM_PLANE_TYPE_OVERLAY },
+	{ .base = 0x00, .phy = &rk3126_win1_data,
+	  .type = DRM_PLANE_TYPE_PRIMARY },
 };
 
-static const struct vop_misc rk3328_misc = {
-	.global_regdone_en = VOP_REG(RK3328_SYS_CTRL, 0x1, 11),
+static const struct vop_data rk3126_vop = {
+	.soc_id = 0x3126,
+	.vop_id = 0,
+	.version = VOP_VERSION(2, 4),
+	.max_input = {1920, 8192},
+	.max_output = {1920, 1080},
+	.ctrl = &rk3036_ctrl_data,
+	.intr = &rk3036_intr,
+	.win = rk3126_vop_win_data,
+	.win_size = ARRAY_SIZE(rk3126_vop_win_data),
 };
 
-static const struct vop_common rk3328_common = {
-	.standby = VOP_REG_SYNC(RK3328_SYS_CTRL, 0x1, 22),
-	.dither_down_sel = VOP_REG(RK3328_DSP_CTRL1, 0x1, 4),
-	.dither_down_mode = VOP_REG(RK3328_DSP_CTRL1, 0x1, 3),
-	.dither_down_en = VOP_REG(RK3328_DSP_CTRL1, 0x1, 2),
-	.pre_dither_down = VOP_REG(RK3328_DSP_CTRL1, 0x1, 1),
-	.dither_up = VOP_REG(RK3328_DSP_CTRL1, 0x1, 6),
-	.dsp_blank = VOP_REG(RK3328_DSP_CTRL0, 0x3, 18),
-	.out_mode = VOP_REG(RK3328_DSP_CTRL0, 0xf, 0),
-	.cfg_done = VOP_REG_SYNC(RK3328_REG_CFG_DONE, 0x1, 0),
+/* PX30 VOPB win2 is same with RK3368,
+ * but RK3368 win2 register offset is 0xb0 and px30 is 0x190,
+ * so we set the PX30 VOPB win2 base = 0x190 - 0xb0 = 0xe0
+ */
+
+static const struct vop_ctrl px30_ctrl_data = {
+	.standby = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 1),
+	.axi_outstanding_max_num = VOP_REG(RK3366_LIT_SYS_CTRL1, 0x1f, 16),
+	.axi_max_outstanding_en = VOP_REG(RK3366_LIT_SYS_CTRL1, 0x1, 12),
+	.htotal_pw = VOP_REG(RK3366_LIT_DSP_HTOTAL_HS_END, 0x0fff0fff, 0),
+	.hact_st_end = VOP_REG(RK3366_LIT_DSP_HACT_ST_END, 0x0fff0fff, 0),
+	.vtotal_pw = VOP_REG(RK3366_LIT_DSP_VTOTAL_VS_END, 0x0fff0fff, 0),
+	.vact_st_end = VOP_REG(RK3366_LIT_DSP_VACT_ST_END, 0x0fff0fff, 0),
+	.vact_st_end_f1 = VOP_REG(RK3366_LIT_DSP_VACT_ST_END_F1, 0x0fff0fff, 0),
+	.vs_st_end_f1 = VOP_REG(RK3366_LIT_DSP_VS_ST_END_F1, 0x0fff0fff, 0),
+	.dsp_interlace = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 0),
+	.global_regdone_en = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 13),
+	.auto_gate_en = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 0),
+	.dsp_layer_sel = VOP_REG(RK3366_LIT_DSP_CTRL2, 0xff, 22),
+	.overlay_mode = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 4),
+	.core_dclk_div = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 13),
+	.dclk_ddr = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 14),
+	.rgb_en = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 0),
+	.rgb_pin_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x7, 2),
+	.hdmi_en = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 8),
+	.hdmi_pin_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x7, 10),
+	.lvds_en = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 16),
+	.lvds_pin_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x7, 18),
+	.mipi_en = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 24),
+	.mipi_pin_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x7, 26),
+	.mipi_dclk_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 25),
+	.lvds_dclk_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 17),
+	.hdmi_dclk_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 9),
+	.rgb_dclk_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 1),
+	.dither_down_en = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 8),
+	.dither_down_sel = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 7),
+	.dither_down_mode = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 6),
+	.dither_up_en = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 2),
+	.dsp_data_swap = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1f, 9),
+	.dsp_ccir656_avg = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 5),
+	.dsp_black = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 15),
+	.dsp_blank = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 14),
+	.dsp_outzero = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 3),
+	.dsp_lut_en = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 5),
+	.out_mode = VOP_REG(RK3366_LIT_DSP_CTRL2, 0xf, 16),
+	.dsp_background = VOP_REG(RK3366_LIT_DSP_BG, 0x00ffffff, 0),
+	.cfg_done = VOP_REG(RK3366_LIT_REG_CFG_DONE, 0x1, 0),
+
+	.bcsh_en = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 0),
+	.bcsh_r2y_csc_mode = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 1),
+	.bcsh_out_mode = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x3, 2),
+	.bcsh_y2r_csc_mode = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x3, 4),
+	.bcsh_y2r_en = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 6),
+	.bcsh_r2y_en = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 7),
+	.bcsh_color_bar = VOP_REG(RK3366_LIT_BCSH_COL_BAR, 0xffffff, 0),
+	.bcsh_brightness = VOP_REG(RK3366_LIT_BCSH_BCS, 0xff, 0),
+	.bcsh_contrast = VOP_REG(RK3366_LIT_BCSH_BCS, 0x1ff, 8),
+	.bcsh_sat_con = VOP_REG(RK3366_LIT_BCSH_BCS, 0x3ff, 20),
+	.bcsh_sin_hue = VOP_REG(RK3366_LIT_BCSH_H, 0x1ff, 0),
+	.bcsh_cos_hue = VOP_REG(RK3366_LIT_BCSH_H, 0x1ff, 16),
+
+	.afbdc_en = VOP_REG(PX30_AFBCD0_CTRL, 0x1, 0),
+	.afbdc_format = VOP_REG(PX30_AFBCD0_CTRL, 0x1f, 4),
+	.afbdc_pic_vir_width = VOP_REG(PX30_AFBCD0_CTRL, 0xffff, 16),
+	.afbdc_hdr_ptr = VOP_REG(PX30_AFBCD0_HDR_PTR, 0xffffffff, 0),
+	.afbdc_pic_size = VOP_REG(PX30_AFBCD0_PIC_SIZE, 0xffffffff, 0),
+	.afbdc_pic_offset = VOP_REG(PX30_AFBCD0_PIC_OFFSET, 0xffffffff, 0),
+	.afbdc_axi_ctrl =  VOP_REG(PX30_AFBCD0_AXI_CTRL, 0xffffffff, 0),
+
+	.mcu_pix_total = VOP_REG(RK3366_LIT_MCU_CTRL, 0x3f, 0),
+	.mcu_cs_pst = VOP_REG(RK3366_LIT_MCU_CTRL, 0xf, 6),
+	.mcu_cs_pend = VOP_REG(RK3366_LIT_MCU_CTRL, 0x3f, 10),
+	.mcu_rw_pst = VOP_REG(RK3366_LIT_MCU_CTRL, 0xf, 16),
+	.mcu_rw_pend = VOP_REG(RK3366_LIT_MCU_CTRL, 0x3f, 20),
+	.mcu_clk_sel = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 26),
+	.mcu_hold_mode = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 27),
+	.mcu_frame_st = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 28),
+	.mcu_rs = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 29),
+	.mcu_bypass = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 30),
+	.mcu_type = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 31),
+	.mcu_rw_bypass_port = VOP_REG(RK3366_LIT_MCU_RW_BYPASS_PORT,
+				      0xffffffff, 0),
+};
+
+static const struct vop_win_phy px30_win23_data = {
+	.data_formats = formats_win_lite,
+	.nformats = ARRAY_SIZE(formats_win_lite),
+	.gate = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 0),
+	.enable = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 4),
+	.format = VOP_REG(RK3368_WIN2_CTRL0, 0x3, 5),
+	.rb_swap = VOP_REG(RK3368_WIN2_CTRL0, 0x1, 20),
+	.dsp_info = VOP_REG(RK3368_WIN2_DSP_INFO0, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3368_WIN2_DSP_ST0, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3368_WIN2_MST0, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3368_WIN2_VIR0_1, 0x1fff, 0),
+	.alpha_pre_mul = VOP_REG(RK3368_WIN2_SRC_ALPHA_CTRL, 0x1, 2),
+	.alpha_mode = VOP_REG(RK3368_WIN2_SRC_ALPHA_CTRL, 0x1, 1),
+	.alpha_en = VOP_REG(RK3368_WIN2_SRC_ALPHA_CTRL, 0x1, 0),
+	.global_alpha_val = VOP_REG(RK3368_WIN2_SRC_ALPHA_CTRL, 0xff, 4),
+	.channel = VOP_REG(RK3368_WIN2_CTRL1, 0xf, 8),
 };
 
-static const struct vop_intr rk3328_vop_intr = {
-	.intrs = rk3368_vop_intrs,
-	.nintrs = ARRAY_SIZE(rk3368_vop_intrs),
-	.line_flag_num[0] = VOP_REG(RK3328_LINE_FLAG, 0xffff, 0),
-	.line_flag_num[1] = VOP_REG(RK3328_LINE_FLAG, 0xffff, 16),
-	.status = VOP_REG_MASK_SYNC(RK3328_INTR_STATUS0, 0xffff, 0),
-	.enable = VOP_REG_MASK_SYNC(RK3328_INTR_EN0, 0xffff, 0),
-	.clear = VOP_REG_MASK_SYNC(RK3328_INTR_CLEAR0, 0xffff, 0),
+static const struct vop_win_data px30_vop_big_win_data[] = {
+	{ .base = 0x00, .phy = &rk3366_lit_win0_data,
+	  .type = DRM_PLANE_TYPE_OVERLAY },
+	{ .base = 0x00, .phy = &rk3366_lit_win1_data,
+	  .type = DRM_PLANE_TYPE_PRIMARY,
+	  .feature = WIN_FEATURE_AFBDC },
+	{ .base = 0xe0, .phy = &px30_win23_data,
+	  .type = DRM_PLANE_TYPE_CURSOR,
+	  .area = rk3368_area_data,
+	  .area_size = ARRAY_SIZE(rk3368_area_data), },
 };
 
-static const struct vop_win_data rk3328_vop_win_data[] = {
-	{ .base = 0xd0, .phy = &rk3368_win01_data,
+static const struct vop_win_data px30_vop_lit_win_data[] = {
+	{ .phy = NULL },
+	{ .base = 0x00, .phy = &rk3366_lit_win1_data,
 	  .type = DRM_PLANE_TYPE_PRIMARY },
-	{ .base = 0x1d0, .phy = &rk3368_win01_data,
+	{ .phy = NULL },
+};
+
+static const struct vop_grf_ctrl px30_grf_ctrl = {
+	.grf_dclk_inv = VOP_REG(PX30_GRF_PD_VO_CON1, 0x1, 4),
+};
+
+static const struct vop_data px30_vop_lit = {
+	.soc_id = 0x3326,
+	.vop_id = 1,
+	.version = VOP_VERSION(2, 5),
+	.max_input = {1920, 8192},
+	.max_output = {1920, 1080},
+	.ctrl = &px30_ctrl_data,
+	.intr = &rk3366_lit_intr,
+	.grf_ctrl = &px30_grf_ctrl,
+	.win = px30_vop_lit_win_data,
+	.win_size = ARRAY_SIZE(px30_vop_lit_win_data),
+};
+
+static const struct vop_data px30_vop_big = {
+	.soc_id = 0x3326,
+	.vop_id = 0,
+	.version = VOP_VERSION(2, 6),
+	.max_input = {1920, 8192},
+	.max_output = {1920, 1080},
+	.ctrl = &px30_ctrl_data,
+	.intr = &rk3366_lit_intr,
+	.grf_ctrl = &px30_grf_ctrl,
+	.win = px30_vop_big_win_data,
+	.win_size = ARRAY_SIZE(px30_vop_big_win_data),
+};
+
+static const struct vop_ctrl rk3308_ctrl_data = {
+	.standby = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 1),
+	.axi_outstanding_max_num = VOP_REG(RK3366_LIT_SYS_CTRL1, 0x1f, 16),
+	.axi_max_outstanding_en = VOP_REG(RK3366_LIT_SYS_CTRL1, 0x1, 12),
+	.htotal_pw = VOP_REG(RK3366_LIT_DSP_HTOTAL_HS_END, 0x0fff0fff, 0),
+	.hact_st_end = VOP_REG(RK3366_LIT_DSP_HACT_ST_END, 0x0fff0fff, 0),
+	.vtotal_pw = VOP_REG(RK3366_LIT_DSP_VTOTAL_VS_END, 0x0fff0fff, 0),
+	.vact_st_end = VOP_REG(RK3366_LIT_DSP_VACT_ST_END, 0x0fff0fff, 0),
+	.vact_st_end_f1 = VOP_REG(RK3366_LIT_DSP_VACT_ST_END_F1, 0x0fff0fff, 0),
+	.vs_st_end_f1 = VOP_REG(RK3366_LIT_DSP_VS_ST_END_F1, 0x0fff0fff, 0),
+	.global_regdone_en = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 13),
+	.auto_gate_en = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 0),
+	.dsp_layer_sel = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 3),
+	.overlay_mode = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 4),
+	.dclk_ddr = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 14),
+	.rgb_en = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 0),
+	.rgb_pin_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x7, 2),
+	.rgb_dclk_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 1),
+	.dither_down_en = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 8),
+	.dither_down_sel = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 7),
+	.dither_down_mode = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 6),
+	.dither_up_en = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 2),
+	.dsp_data_swap = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1f, 9),
+	.dsp_ccir656_avg = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 5),
+	.dsp_black = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 15),
+	.dsp_blank = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 14),
+	.dsp_outzero = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 3),
+	.dsp_lut_en = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 5),
+	.out_mode = VOP_REG(RK3366_LIT_DSP_CTRL2, 0xf, 16),
+	.dsp_background = VOP_REG(RK3366_LIT_DSP_BG, 0x00ffffff, 0),
+	.cfg_done = VOP_REG(RK3366_LIT_REG_CFG_DONE, 0x1, 0),
+
+	.bcsh_en = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 0),
+	.bcsh_r2y_csc_mode = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 1),
+	.bcsh_out_mode = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x3, 2),
+	.bcsh_y2r_csc_mode = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x3, 4),
+	.bcsh_y2r_en = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 6),
+	.bcsh_r2y_en = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 7),
+	.bcsh_color_bar = VOP_REG(RK3366_LIT_BCSH_COL_BAR, 0xffffff, 0),
+	.bcsh_brightness = VOP_REG(RK3366_LIT_BCSH_BCS, 0x3f, 0),
+	.bcsh_contrast = VOP_REG(RK3366_LIT_BCSH_BCS, 0xff, 8),
+	.bcsh_sat_con = VOP_REG(RK3366_LIT_BCSH_BCS, 0x1ff, 16),
+	.bcsh_sin_hue = VOP_REG(RK3366_LIT_BCSH_H, 0xff, 0),
+	.bcsh_cos_hue = VOP_REG(RK3366_LIT_BCSH_H, 0xff, 8),
+
+	.mcu_pix_total = VOP_REG(RK3366_LIT_MCU_CTRL, 0x3f, 0),
+	.mcu_cs_pst = VOP_REG(RK3366_LIT_MCU_CTRL, 0xf, 6),
+	.mcu_cs_pend = VOP_REG(RK3366_LIT_MCU_CTRL, 0x3f, 10),
+	.mcu_rw_pst = VOP_REG(RK3366_LIT_MCU_CTRL, 0xf, 16),
+	.mcu_rw_pend = VOP_REG(RK3366_LIT_MCU_CTRL, 0x3f, 20),
+	.mcu_clk_sel = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 26),
+	.mcu_hold_mode = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 27),
+	.mcu_frame_st = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 28),
+	.mcu_rs = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 29),
+	.mcu_bypass = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 30),
+	.mcu_type = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 31),
+	.mcu_rw_bypass_port = VOP_REG(RK3366_LIT_MCU_RW_BYPASS_PORT,
+				      0xffffffff, 0),
+};
+
+static const int rk3308_vop_intrs[] = {
+	FS_INTR,
+	FS_NEW_INTR,
+	ADDR_SAME_INTR,
+	LINE_FLAG_INTR,
+	LINE_FLAG1_INTR,
+	BUS_ERROR_INTR,
+	0,
+	0,
+	DSP_HOLD_VALID_INTR,
+	DMA_FINISH_INTR,
+	0,
+	POST_BUF_EMPTY_INTR
+};
+
+static const struct vop_intr rk3308_vop_intr = {
+	.intrs = rk3308_vop_intrs,
+	.nintrs = ARRAY_SIZE(rk3308_vop_intrs),
+	.line_flag_num[0] = VOP_REG(RK3366_LIT_LINE_FLAG, 0xfff, 0),
+	.line_flag_num[1] = VOP_REG(RK3366_LIT_LINE_FLAG, 0xfff, 16),
+	.status = VOP_REG_MASK(RK3366_LIT_INTR_STATUS, 0xffff, 0),
+	.enable = VOP_REG_MASK(RK3366_LIT_INTR_EN, 0xffff, 0),
+	.clear = VOP_REG_MASK(RK3366_LIT_INTR_CLEAR, 0xffff, 0),
+};
+
+static const struct vop_data rk3308_vop = {
+	.soc_id = 0x3308,
+	.vop_id = 0,
+	.version = VOP_VERSION(2, 7),
+	.max_input = {1920, 8192},
+	.max_output = {1920, 1080},
+	.ctrl = &rk3308_ctrl_data,
+	.intr = &rk3308_vop_intr,
+	.win = rk3366_vop_lit_win_data,
+	.win_size = ARRAY_SIZE(rk3366_vop_lit_win_data),
+};
+
+static const struct vop_ctrl rv1126_ctrl_data = {
+	.standby = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 1),
+	.axi_outstanding_max_num = VOP_REG(RK3366_LIT_SYS_CTRL1, 0x1f, 16),
+	.axi_max_outstanding_en = VOP_REG(RK3366_LIT_SYS_CTRL1, 0x1, 12),
+	.htotal_pw = VOP_REG(RK3366_LIT_DSP_HTOTAL_HS_END, 0x0fff0fff, 0),
+	.hact_st_end = VOP_REG(RK3366_LIT_DSP_HACT_ST_END, 0x0fff0fff, 0),
+	.vtotal_pw = VOP_REG(RK3366_LIT_DSP_VTOTAL_VS_END, 0x0fff0fff, 0),
+	.vact_st_end = VOP_REG(RK3366_LIT_DSP_VACT_ST_END, 0x0fff0fff, 0),
+	.vact_st_end_f1 = VOP_REG(RK3366_LIT_DSP_VACT_ST_END_F1, 0x0fff0fff, 0),
+	.vs_st_end_f1 = VOP_REG(RK3366_LIT_DSP_VS_ST_END_F1, 0x0fff0fff, 0),
+	.dsp_interlace = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 0),
+	.global_regdone_en = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 13),
+	.auto_gate_en = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 0),
+	.dsp_layer_sel = VOP_REG(RK3366_LIT_DSP_CTRL2, 0xff, 22),
+	.overlay_mode = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 4),
+	.core_dclk_div = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 13),
+	.dclk_ddr = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 14),
+	.rgb_en = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 0),
+	.rgb_pin_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x7, 2),
+	.hdmi_en = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 8),
+	.hdmi_pin_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x7, 10),
+	.lvds_en = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 16),
+	.lvds_pin_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x7, 18),
+	.mipi_en = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 24),
+	.mipi_pin_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x7, 26),
+	.mipi_dclk_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 25),
+	.lvds_dclk_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 17),
+	.hdmi_dclk_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 9),
+	.rgb_dclk_pol = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 1),
+	.dither_down_en = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 8),
+	.dither_down_sel = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 7),
+	.dither_down_mode = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 6),
+	.dither_up_en = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 2),
+	.dsp_data_swap = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1f, 9),
+	.yuv_clip = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 4),
+	.dsp_ccir656_avg = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 5),
+	.dsp_black = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 15),
+	.dsp_blank = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 14),
+	.dsp_outzero = VOP_REG(RK3366_LIT_SYS_CTRL2, 0x1, 3),
+	.dsp_lut_en = VOP_REG(RK3366_LIT_DSP_CTRL2, 0x1, 5),
+	.out_mode = VOP_REG(RK3366_LIT_DSP_CTRL2, 0xf, 16),
+	.dsp_background = VOP_REG(RK3366_LIT_DSP_BG, 0x00ffffff, 0),
+	.cfg_done = VOP_REG(RK3366_LIT_REG_CFG_DONE, 0x1, 0),
+
+	.bcsh_en = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 0),
+	.bcsh_r2y_csc_mode = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 1),
+	.bcsh_out_mode = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x3, 2),
+	.bcsh_y2r_csc_mode = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x3, 4),
+	.bcsh_y2r_en = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 6),
+	.bcsh_r2y_en = VOP_REG(RK3366_LIT_BCSH_CTRL, 0x1, 7),
+	.bcsh_color_bar = VOP_REG(RK3366_LIT_BCSH_COL_BAR, 0xffffff, 0),
+	.bcsh_brightness = VOP_REG(RK3366_LIT_BCSH_BCS, 0xff, 0),
+	.bcsh_contrast = VOP_REG(RK3366_LIT_BCSH_BCS, 0x1ff, 8),
+	.bcsh_sat_con = VOP_REG(RK3366_LIT_BCSH_BCS, 0x3ff, 20),
+	.bcsh_sin_hue = VOP_REG(RK3366_LIT_BCSH_H, 0x1ff, 0),
+	.bcsh_cos_hue = VOP_REG(RK3366_LIT_BCSH_H, 0x1ff, 16),
+
+	.mcu_pix_total = VOP_REG(RK3366_LIT_MCU_CTRL, 0x3f, 0),
+	.mcu_cs_pst = VOP_REG(RK3366_LIT_MCU_CTRL, 0xf, 6),
+	.mcu_cs_pend = VOP_REG(RK3366_LIT_MCU_CTRL, 0x3f, 10),
+	.mcu_rw_pst = VOP_REG(RK3366_LIT_MCU_CTRL, 0xf, 16),
+	.mcu_rw_pend = VOP_REG(RK3366_LIT_MCU_CTRL, 0x3f, 20),
+	.mcu_clk_sel = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 26),
+	.mcu_hold_mode = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 27),
+	.mcu_frame_st = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 28),
+	.mcu_rs = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 29),
+	.mcu_bypass = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 30),
+	.mcu_type = VOP_REG(RK3366_LIT_MCU_CTRL, 0x1, 31),
+	.mcu_rw_bypass_port = VOP_REG(RK3366_LIT_MCU_RW_BYPASS_PORT,
+				      0xffffffff, 0),
+	.bt1120_yc_swap = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 30),
+	.bt1120_en = VOP_REG(RK3366_LIT_DSP_CTRL0, 0x1, 31),
+};
+
+static const struct vop_win_data rv1126_vop_win_data[] = {
+	{ .base = 0x00, .phy = &rk3366_lit_win0_data,
 	  .type = DRM_PLANE_TYPE_OVERLAY },
-	{ .base = 0x2d0, .phy = &rk3368_win01_data,
-	  .type = DRM_PLANE_TYPE_CURSOR },
+	{ .phy = NULL },
+	{ .base = 0xe0, .phy = &px30_win23_data,
+	  .type = DRM_PLANE_TYPE_PRIMARY,
+	  .area = rk3368_area_data,
+	  .area_size = ARRAY_SIZE(rk3368_area_data), },
 };
 
-static const struct vop_data rk3328_vop = {
-	.version = VOP_VERSION(3, 8),
-	.feature = VOP_FEATURE_OUTPUT_RGB10,
-	.intr = &rk3328_vop_intr,
-	.common = &rk3328_common,
-	.modeset = &rk3328_modeset,
-	.output = &rk3328_output,
-	.misc = &rk3328_misc,
-	.win = rk3328_vop_win_data,
-	.win_size = ARRAY_SIZE(rk3328_vop_win_data),
+static const struct vop_grf_ctrl rv1126_grf_ctrl = {
+	.grf_dclk_inv = VOP_REG(RV1126_GRF_IOFUNC_CON3, 0x1, 2),
+};
+
+static const struct vop_data rv1126_vop = {
+	.soc_id = 0x1126,
+	.vop_id = 0,
+	.version = VOP_VERSION(2, 0xb),
+	.max_input = {1920, 1920},
+	.max_output = {1920, 1080},
+	.ctrl = &rv1126_ctrl_data,
+	.intr = &rk3366_lit_intr,
+	.grf_ctrl = &rv1126_grf_ctrl,
+	.win = rv1126_vop_win_data,
+	.win_size = ARRAY_SIZE(rv1126_vop_win_data),
 };
 
 static const struct of_device_id vop_driver_dt_match[] = {
 	{ .compatible = "rockchip,rk3036-vop",
 	  .data = &rk3036_vop },
+	{ .compatible = "rockchip,rk3066-vop",
+	  .data = &rk3066_vop },
 	{ .compatible = "rockchip,rk3126-vop",
 	  .data = &rk3126_vop },
-	{ .compatible = "rockchip,px30-vop-big",
-	  .data = &px30_vop_big },
 	{ .compatible = "rockchip,px30-vop-lit",
 	  .data = &px30_vop_lit },
-	{ .compatible = "rockchip,rk3066-vop",
-	  .data = &rk3066_vop },
-	{ .compatible = "rockchip,rk3188-vop",
-	  .data = &rk3188_vop },
-	{ .compatible = "rockchip,rk3288-vop",
-	  .data = &rk3288_vop },
+	{ .compatible = "rockchip,px30-vop-big",
+	  .data = &px30_vop_big },
+	{ .compatible = "rockchip,rk3308-vop",
+	  .data = &rk3308_vop },
+	{ .compatible = "rockchip,rv1126-vop",
+	  .data = &rv1126_vop },
+	{ .compatible = "rockchip,rk3288-vop-big",
+	  .data = &rk3288_vop_big },
+	{ .compatible = "rockchip,rk3288-vop-lit",
+	  .data = &rk3288_vop_lit },
 	{ .compatible = "rockchip,rk3368-vop",
 	  .data = &rk3368_vop },
 	{ .compatible = "rockchip,rk3366-vop",
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
index 6e9fa5815..ffd49a862 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
@@ -113,6 +113,11 @@
 #define RK3288_DSP_VACT_ST_END			0x0194
 #define RK3288_DSP_VS_ST_END_F1			0x0198
 #define RK3288_DSP_VACT_ST_END_F1		0x019c
+
+#define RK3288_BCSH_COLOR_BAR			0x01b0
+#define RK3288_BCSH_BCS				0x01b4
+#define RK3288_BCSH_H				0x01b8
+#define RK3288_GRF_SOC_CON15			0x03a4
 /* register definition end */
 
 /* rk3368 register definition */
@@ -300,6 +305,7 @@
 #define RK3368_CABC_GAMMA_LUT_ADDR		0x1800
 #define RK3368_MCU_BYPASS_WPORT			0x2200
 #define RK3368_MCU_BYPASS_RPORT			0x2300
+#define RK3368_GRF_SOC_CON6			0x0418
 /* rk3368 register definition end */
 
 #define RK3366_REG_CFG_DONE			0x0000
@@ -628,6 +634,7 @@
 #define RK3399_YUV2YUV_WIN			0x02c0
 #define RK3399_YUV2YUV_POST			0x02c4
 #define RK3399_AUTO_GATING_EN			0x02cc
+#define RK3399_DBG_POST_REG1			0x036c
 #define RK3399_WIN0_CSC_COE			0x03a0
 #define RK3399_WIN1_CSC_COE			0x03c0
 #define RK3399_WIN2_CSC_COE			0x03e0
@@ -798,6 +805,21 @@
 #define RK3328_DBG_POST_RESERVED		0x000006ec
 #define RK3328_DBG_DATAO			0x000006f0
 #define RK3328_DBG_DATAO_2			0x000006f4
+#define RK3328_SDR2HDR_CTRL			0x00000700
+#define RK3328_SDR2HDR_EOTF_OETF_Y0		0x00000704
+#define RK3328_SDR2HDR_EOTF_OETF_Y1		0x00000710
+#define RK3328_SDR2HDR_OETF_DX_DXPOW1		0x00000810
+#define RK3328_SDR2HDR_OETF_XN1			0x00000910
+
+#define RK3328_HDR2DR_CTRL			0x00000a10
+#define RK3328_HDR2DR_SRC_RANGE			0x00000a14
+#define RK3328_HDR2DR_NORMFACEETF		0x00000a18
+#define RK3328_HDR2DR_DST_RANGE			0x00000a20
+#define RK3328_HDR2DR_NORMFACGAMMA		0x00000a24
+#define RK3328_HDR2SDR_EETF_OETF_Y0		0x00000a28
+#define RK3328_HDR2DR_SAT_Y0			0x00000a2C
+#define RK3328_HDR2SDR_EETF_OETF_Y1		0x00000a30
+#define RK3328_HDR2DR_SAT_Y1			0x00000ab0
 
 /* sdr to hdr */
 #define RK3328_SDR2HDR_CTRL			0x00000700
@@ -830,6 +852,7 @@
 #define RK3036_SYS_CTRL			0x00
 #define RK3036_DSP_CTRL0		0x04
 #define RK3036_DSP_CTRL1		0x08
+#define RK3036_INT_SCALER		0x0c
 #define RK3036_INT_STATUS		0x10
 #define RK3036_ALPHA_CTRL		0x14
 #define RK3036_WIN0_COLOR_KEY		0x18
@@ -870,112 +893,6 @@
 #define RK3036_HWC_LUT_ADDR		0x800
 /* rk3036 register definition end */
 
-/* rk3126 register definition */
-#define RK3126_WIN1_MST			0x4c
-#define RK3126_WIN1_DSP_INFO		0x50
-#define RK3126_WIN1_DSP_ST		0x54
-/* rk3126 register definition end */
-
-/* px30 register definition */
-#define PX30_REG_CFG_DONE			0x00000
-#define PX30_VERSION				0x00004
-#define PX30_DSP_BG				0x00008
-#define PX30_MCU_CTRL				0x0000c
-#define PX30_SYS_CTRL0				0x00010
-#define PX30_SYS_CTRL1				0x00014
-#define PX30_SYS_CTRL2				0x00018
-#define PX30_DSP_CTRL0				0x00020
-#define PX30_DSP_CTRL2				0x00028
-#define PX30_VOP_STATUS				0x0002c
-#define PX30_LINE_FLAG				0x00030
-#define PX30_INTR_EN				0x00034
-#define PX30_INTR_CLEAR				0x00038
-#define PX30_INTR_STATUS			0x0003c
-#define PX30_WIN0_CTRL0				0x00050
-#define PX30_WIN0_CTRL1				0x00054
-#define PX30_WIN0_COLOR_KEY			0x00058
-#define PX30_WIN0_VIR				0x0005c
-#define PX30_WIN0_YRGB_MST0			0x00060
-#define PX30_WIN0_CBR_MST0			0x00064
-#define PX30_WIN0_ACT_INFO			0x00068
-#define PX30_WIN0_DSP_INFO			0x0006c
-#define PX30_WIN0_DSP_ST			0x00070
-#define PX30_WIN0_SCL_FACTOR_YRGB		0x00074
-#define PX30_WIN0_SCL_FACTOR_CBR		0x00078
-#define PX30_WIN0_SCL_OFFSET			0x0007c
-#define PX30_WIN0_ALPHA_CTRL			0x00080
-#define PX30_WIN1_CTRL0				0x00090
-#define PX30_WIN1_CTRL1				0x00094
-#define PX30_WIN1_VIR				0x00098
-#define PX30_WIN1_MST				0x000a0
-#define PX30_WIN1_DSP_INFO			0x000a4
-#define PX30_WIN1_DSP_ST			0x000a8
-#define PX30_WIN1_COLOR_KEY			0x000ac
-#define PX30_WIN1_ALPHA_CTRL			0x000bc
-#define PX30_HWC_CTRL0				0x000e0
-#define PX30_HWC_CTRL1				0x000e4
-#define PX30_HWC_MST				0x000e8
-#define PX30_HWC_DSP_ST				0x000ec
-#define PX30_HWC_ALPHA_CTRL			0x000f0
-#define PX30_DSP_HTOTAL_HS_END			0x00100
-#define PX30_DSP_HACT_ST_END			0x00104
-#define PX30_DSP_VTOTAL_VS_END			0x00108
-#define PX30_DSP_VACT_ST_END			0x0010c
-#define PX30_DSP_VS_ST_END_F1			0x00110
-#define PX30_DSP_VACT_ST_END_F1			0x00114
-#define PX30_BCSH_CTRL				0x00160
-#define PX30_BCSH_COL_BAR			0x00164
-#define PX30_BCSH_BCS				0x00168
-#define PX30_BCSH_H				0x0016c
-#define PX30_FRC_LOWER01_0			0x00170
-#define PX30_FRC_LOWER01_1			0x00174
-#define PX30_FRC_LOWER10_0			0x00178
-#define PX30_FRC_LOWER10_1			0x0017c
-#define PX30_FRC_LOWER11_0			0x00180
-#define PX30_FRC_LOWER11_1			0x00184
-#define PX30_MCU_RW_BYPASS_PORT			0x0018c
-#define PX30_WIN2_CTRL0				0x00190
-#define PX30_WIN2_CTRL1				0x00194
-#define PX30_WIN2_VIR0_1			0x00198
-#define PX30_WIN2_VIR2_3			0x0019c
-#define PX30_WIN2_MST0				0x001a0
-#define PX30_WIN2_DSP_INFO0			0x001a4
-#define PX30_WIN2_DSP_ST0			0x001a8
-#define PX30_WIN2_COLOR_KEY			0x001ac
-#define PX30_WIN2_ALPHA_CTRL			0x001bc
-#define PX30_BLANKING_VALUE			0x001f4
-#define PX30_FLAG_REG_FRM_VALID			0x001f8
-#define PX30_FLAG_REG				0x001fc
-#define PX30_HWC_LUT_ADDR			0x00600
-#define PX30_GAMMA_LUT_ADDR			0x00a00
-/* px30 register definition end */
-
-/* rk3188 register definition */
-#define RK3188_SYS_CTRL			0x00
-#define RK3188_DSP_CTRL0		0x04
-#define RK3188_DSP_CTRL1		0x08
-#define RK3188_INT_STATUS		0x10
-#define RK3188_WIN0_YRGB_MST0		0x20
-#define RK3188_WIN0_CBR_MST0		0x24
-#define RK3188_WIN0_YRGB_MST1		0x28
-#define RK3188_WIN0_CBR_MST1		0x2c
-#define RK3188_WIN_VIR			0x30
-#define RK3188_WIN0_ACT_INFO		0x34
-#define RK3188_WIN0_DSP_INFO		0x38
-#define RK3188_WIN0_DSP_ST		0x3c
-#define RK3188_WIN0_SCL_FACTOR_YRGB	0x40
-#define RK3188_WIN0_SCL_FACTOR_CBR	0x44
-#define RK3188_WIN1_MST			0x4c
-#define RK3188_WIN1_DSP_INFO		0x50
-#define RK3188_WIN1_DSP_ST		0x54
-#define RK3188_DSP_HTOTAL_HS_END	0x6c
-#define RK3188_DSP_HACT_ST_END		0x70
-#define RK3188_DSP_VTOTAL_VS_END	0x74
-#define RK3188_DSP_VACT_ST_END		0x78
-#define RK3188_REG_CFG_DONE		0x90
-/* rk3188 register definition end */
-
-/* rk3066 register definition */
 #define RK3066_SYS_CTRL0		0x00
 #define RK3066_SYS_CTRL1		0x04
 #define RK3066_DSP_CTRL0		0x08
@@ -1026,6 +943,691 @@
 #define RK3066_MCU_BYPASS_RPORT		0x200
 #define RK3066_WIN2_LUT_ADDR		0x400
 #define RK3066_DSP_LUT_ADDR		0x800
-/* rk3066 register definition end */
+
+/* rk3366 register definition */
+#define RK3366_LIT_REG_CFG_DONE			0x00000
+#define RK3366_LIT_VERSION			0x00004
+#define RK3366_LIT_DSP_BG			0x00008
+#define RK3366_LIT_MCU_CTRL			0x0000c
+#define RK3366_LIT_SYS_CTRL0			0x00010
+#define RK3366_LIT_SYS_CTRL1			0x00014
+#define RK3366_LIT_SYS_CTRL2			0x00018
+#define RK3366_LIT_DSP_CTRL0			0x00020
+#define RK3366_LIT_DSP_CTRL2			0x00028
+#define RK3366_LIT_VOP_STATUS			0x0002c
+#define RK3366_LIT_LINE_FLAG			0x00030
+#define RK3366_LIT_INTR_EN			0x00034
+#define RK3366_LIT_INTR_CLEAR			0x00038
+#define RK3366_LIT_INTR_STATUS			0x0003c
+#define RK3366_LIT_WIN0_CTRL0			0x00050
+#define RK3366_LIT_WIN0_CTRL1			0x00054
+#define RK3366_LIT_WIN0_COLOR_KEY		0x00058
+#define RK3366_LIT_WIN0_VIR			0x0005c
+#define RK3366_LIT_WIN0_YRGB_MST0		0x00060
+#define RK3366_LIT_WIN0_CBR_MST0		0x00064
+#define RK3366_LIT_WIN0_ACT_INFO		0x00068
+#define RK3366_LIT_WIN0_DSP_INFO		0x0006c
+#define RK3366_LIT_WIN0_DSP_ST			0x00070
+#define RK3366_LIT_WIN0_SCL_FACTOR_YRGB		0x00074
+#define RK3366_LIT_WIN0_SCL_FACTOR_CBR		0x00078
+#define RK3366_LIT_WIN0_SCL_OFFSET		0x0007c
+#define RK3366_LIT_WIN0_ALPHA_CTRL		0x00080
+#define RK3366_LIT_WIN1_CTRL0			0x00090
+#define RK3366_LIT_WIN1_CTRL1			0x00094
+#define RK3366_LIT_WIN1_VIR			0x00098
+#define RK3366_LIT_WIN1_MST			0x000a0
+#define RK3366_LIT_WIN1_DSP_INFO		0x000a4
+#define RK3366_LIT_WIN1_DSP_ST			0x000a8
+#define RK3366_LIT_WIN1_COLOR_KEY		0x000ac
+#define RK3366_LIT_WIN1_ALPHA_CTRL		0x000bc
+#define RK3366_LIT_HWC_CTRL0			0x000e0
+#define RK3366_LIT_HWC_CTRL1			0x000e4
+#define RK3366_LIT_HWC_MST			0x000e8
+#define RK3366_LIT_HWC_DSP_ST			0x000ec
+#define RK3366_LIT_HWC_ALPHA_CTRL		0x000f0
+#define RK3366_LIT_DSP_HTOTAL_HS_END		0x00100
+#define RK3366_LIT_DSP_HACT_ST_END		0x00104
+#define RK3366_LIT_DSP_VTOTAL_VS_END		0x00108
+#define RK3366_LIT_DSP_VACT_ST_END		0x0010c
+#define RK3366_LIT_DSP_VS_ST_END_F1		0x00110
+#define RK3366_LIT_DSP_VACT_ST_END_F1		0x00114
+#define RK3366_LIT_BCSH_CTRL			0x00160
+#define RK3366_LIT_BCSH_COL_BAR			0x00164
+#define RK3366_LIT_BCSH_BCS			0x00168
+#define RK3366_LIT_BCSH_H			0x0016c
+#define RK3366_LIT_FRC_LOWER01_0		0x00170
+#define RK3366_LIT_FRC_LOWER01_1		0x00174
+#define RK3366_LIT_FRC_LOWER10_0		0x00178
+#define RK3366_LIT_FRC_LOWER10_1		0x0017c
+#define RK3366_LIT_FRC_LOWER11_0		0x00180
+#define RK3366_LIT_FRC_LOWER11_1		0x00184
+#define RK3366_LIT_MCU_RW_BYPASS_PORT		0x0018c
+#define RK3366_LIT_DBG_REG_000			0x00190
+#define RK3366_LIT_BLANKING_VALUE		0x001f4
+#define RK3366_LIT_FLAG_REG_FRM_VALID		0x001f8
+#define RK3366_LIT_FLAG_REG			0x001fc
+#define RK3366_LIT_HWC_LUT_ADDR			0x00600
+#define RK3366_LIT_GAMMA_LUT_ADDR		0x00a00
+/* rk3366 register definition end */
+
+/* rk3126 register definition */
+#define RK3126_WIN1_MST				0x0004c
+#define RK3126_WIN1_DSP_INFO			0x00050
+#define RK3126_WIN1_DSP_ST			0x00054
+/* rk3126 register definition end */
+
+/* px30 register definition */
+#define PX30_CABC_CTRL0				0x00200
+#define PX30_CABC_CTRL1				0x00204
+#define PX30_CABC_CTRL2				0x00208
+#define PX30_CABC_CTRL3				0x0020c
+#define PX30_CABC_GAUSS_LINE0_0			0x00210
+#define PX30_CABC_GAUSS_LINE0_1			0x00214
+#define PX30_CABC_GAUSS_LINE1_0			0x00218
+#define PX30_CABC_GAUSS_LINE1_1			0x0021c
+#define PX30_CABC_GAUSS_LINE2_0			0x00220
+#define PX30_CABC_GAUSS_LINE2_1			0x00224
+#define PX30_AFBCD0_CTRL			0x00240
+#define PX30_AFBCD0_HDR_PTR			0x00244
+#define PX30_AFBCD0_PIC_SIZE			0x00248
+#define PX30_AFBCD0_PIC_OFFSET			0x0024c
+#define PX30_AFBCD0_AXI_CTRL			0x00250
+#define PX30_GRF_PD_VO_CON1			0x00438
+/* px30 register definition end */
+
+#define RV1126_GRF_IOFUNC_CON3			0x1026c
+
+/* rk3568 vop registers definition */
+
+#define RK3568_GRF_VO_CON1			0x0364
+/* System registers definition */
+#define RK3568_REG_CFG_DONE			0x000
+#define RK3568_VOP2_WB_CFG_DONE			BIT(14)
+#define RK3568_VOP2_GLB_CFG_DONE_EN		BIT(15)
+#define RK3568_VERSION_INFO			0x004
+#define RK3568_SYS_AUTO_GATING_CTRL		0x008
+#define RK3568_SYS_AXI_LUT_CTRL			0x024
+#define RK3568_DSP_IF_EN			0x028
+#define RK3568_DSP_IF_CTRL			0x02c
+#define RK3568_DSP_IF_POL			0x030
+#define RK3568_SYS_PD_CTRL			0x034
+#define RK3568_WB_CTRL				0x40
+#define RK3568_WB_XSCAL_FACTOR			0x44
+#define RK3568_WB_YRGB_MST			0x48
+#define RK3568_WB_CBR_MST			0x4C
+#define RK3568_OTP_WIN_EN			0x50
+#define RK3568_LUT_PORT_SEL			0x58
+#define RK3568_SYS_STATUS0			0x60
+#define RK3568_SYS_STATUS1			0x64
+#define RK3568_SYS_STATUS2			0x68
+#define RK3568_SYS_STATUS3			0x6C
+#define RK3568_VP0_LINE_FLAG			0x70
+#define RK3568_VP1_LINE_FLAG			0x74
+#define RK3568_VP2_LINE_FLAG			0x78
+#define RK3588_VP3_LINE_FLAG			0x7C
+#define RK3568_SYS0_INT_EN			0x80
+#define RK3568_SYS0_INT_CLR			0x84
+#define RK3568_SYS0_INT_STATUS			0x88
+#define RK3568_SYS1_INT_EN			0x90
+#define RK3568_SYS1_INT_CLR			0x94
+#define RK3568_SYS1_INT_STATUS			0x98
+#define RK3568_VP0_INT_EN			0xA0
+#define RK3568_VP0_INT_CLR			0xA4
+#define RK3568_VP0_INT_STATUS			0xA8
+#define RK3568_VP0_INT_RAW_STATUS		0xAC
+#define RK3568_VP1_INT_EN			0xB0
+#define RK3568_VP1_INT_CLR			0xB4
+#define RK3568_VP1_INT_STATUS			0xB8
+#define RK3568_VP1_INT_RAW_STATUS		0xBC
+#define RK3568_VP2_INT_EN			0xC0
+#define RK3568_VP2_INT_CLR			0xC4
+#define RK3568_VP2_INT_STATUS			0xC8
+#define RK3568_VP2_INT_RAW_STATUS		0xCC
+#define RK3588_VP3_INT_EN			0xD0
+#define RK3588_VP3_INT_CLR			0xD4
+#define RK3588_VP3_INT_STATUS			0xD8
+
+#define RK3588_DSC_8K_SYS_CTRL			0x200
+#define RK3588_DSC_8K_RST			0x204
+#define RK3588_DSC_8K_CFG_DONE			0x208
+#define RK3588_DSC_8K_INIT_DLY			0x20C
+#define RK3588_DSC_8K_HTOTAL_HS_END		0x210
+#define RK3588_DSC_8K_HACT_ST_END		0x214
+#define RK3588_DSC_8K_VTOTAL_VS_END		0x218
+#define RK3588_DSC_8K_VACT_ST_END		0x21C
+#define RK3588_DSC_8K_STATUS			0x220
+#define RK3588_DSC_4K_SYS_CTRL			0x230
+#define RK3588_DSC_4K_RST			0x234
+#define RK3588_DSC_4K_CFG_DONE			0x238
+#define RK3588_DSC_4K_INIT_DLY			0x23C
+#define RK3588_DSC_4K_HTOTAL_HS_END		0x240
+#define RK3588_DSC_4K_HACT_ST_END		0x244
+#define RK3588_DSC_4K_VTOTAL_VS_END		0x248
+#define RK3588_DSC_4K_VACT_ST_END		0x24C
+#define RK3588_DSC_4K_STATUS			0x250
+
+/* Video Port registers definition */
+#define RK3568_VP0_DSP_CTRL				0xC00
+#define RK3568_VP0_DUAL_CHANNEL_CTRL			0xC04
+#define RK3568_VP0_COLOR_BAR_CTRL			0xC08
+#define RK3568_VP0_CLK_CTRL				0xC0C
+#define RK3568_VP0_3D_LUT_CTRL				0xC10
+#define RK3568_VP0_3D_LUT_MST				0xC20
+#define RK3568_VP0_DSP_BG				0xC2C
+#define RK3568_VP0_PRE_SCAN_HTIMING			0xC30
+#define RK3568_VP0_POST_DSP_HACT_INFO			0xC34
+#define RK3568_VP0_POST_DSP_VACT_INFO			0xC38
+#define RK3568_VP0_POST_SCL_FACTOR_YRGB			0xC3C
+#define RK3568_VP0_POST_SCL_CTRL			0xC40
+#define RK3568_VP0_POST_DSP_VACT_INFO_F1		0xC44
+#define RK3568_VP0_DSP_HTOTAL_HS_END			0xC48
+#define RK3568_VP0_DSP_HACT_ST_END			0xC4C
+#define RK3568_VP0_DSP_VTOTAL_VS_END			0xC50
+#define RK3568_VP0_DSP_VACT_ST_END			0xC54
+#define RK3568_VP0_DSP_VS_ST_END_F1			0xC58
+#define RK3568_VP0_DSP_VACT_ST_END_F1			0xC5C
+#define RK3568_VP0_BCSH_CTRL				0xC60
+#define RK3568_VP0_BCSH_BCS				0xC64
+#define RK3568_VP0_BCSH_H				0xC68
+#define RK3568_VP0_BCSH_COLOR_BAR			0xC6C
+
+#define RK3568_VP1_DSP_CTRL				0xD00
+#define RK3568_VP1_DUAL_CHANNEL_CTRL			0xD04
+#define RK3568_VP1_COLOR_BAR_CTRL			0xD08
+#define RK3568_VP1_CLK_CTRL				0xD0C
+#define RK3588_VP1_3D_LUT_CTRL				0xD10
+#define RK3588_VP1_3D_LUT_MST				0xD20
+#define RK3568_VP1_DSP_BG				0xD2C
+#define RK3568_VP1_PRE_SCAN_HTIMING			0xD30
+#define RK3568_VP1_POST_DSP_HACT_INFO			0xD34
+#define RK3568_VP1_POST_DSP_VACT_INFO			0xD38
+#define RK3568_VP1_POST_SCL_FACTOR_YRGB			0xD3C
+#define RK3568_VP1_POST_SCL_CTRL			0xD40
+#define RK3568_VP1_DSP_HACT_INFO			0xD34
+#define RK3568_VP1_DSP_VACT_INFO			0xD38
+#define RK3568_VP1_POST_DSP_VACT_INFO_F1		0xD44
+#define RK3568_VP1_DSP_HTOTAL_HS_END			0xD48
+#define RK3568_VP1_DSP_HACT_ST_END			0xD4C
+#define RK3568_VP1_DSP_VTOTAL_VS_END			0xD50
+#define RK3568_VP1_DSP_VACT_ST_END			0xD54
+#define RK3568_VP1_DSP_VS_ST_END_F1			0xD58
+#define RK3568_VP1_DSP_VACT_ST_END_F1			0xD5C
+#define RK3568_VP1_BCSH_CTRL				0xD60
+#define RK3568_VP1_BCSH_BCS				0xD64
+#define RK3568_VP1_BCSH_H				0xD68
+#define RK3568_VP1_BCSH_COLOR_BAR			0xD6C
+
+#define RK3568_VP2_DSP_CTRL				0xE00
+#define RK3568_VP2_DUAL_CHANNEL_CTRL			0xE04
+#define RK3568_VP2_COLOR_BAR_CTRL			0xE08
+#define RK3568_VP2_CLK_CTRL				0xE0C
+#define RK3588_VP2_3D_LUT_CTRL				0xE10
+#define RK3588_VP2_3D_LUT_MST				0xE20
+#define RK3568_VP2_DSP_BG				0xE2C
+#define RK3568_VP2_PRE_SCAN_HTIMING			0xE30
+#define RK3568_VP2_POST_DSP_HACT_INFO			0xE34
+#define RK3568_VP2_POST_DSP_VACT_INFO			0xE38
+#define RK3568_VP2_POST_SCL_FACTOR_YRGB			0xE3C
+#define RK3568_VP2_POST_SCL_CTRL			0xE40
+#define RK3568_VP2_DSP_HACT_INFO			0xE34
+#define RK3568_VP2_DSP_VACT_INFO			0xE38
+#define RK3568_VP2_POST_DSP_VACT_INFO_F1		0xE44
+#define RK3568_VP2_DSP_HTOTAL_HS_END			0xE48
+#define RK3568_VP2_DSP_HACT_ST_END			0xE4C
+#define RK3568_VP2_DSP_VTOTAL_VS_END			0xE50
+#define RK3568_VP2_DSP_VACT_ST_END			0xE54
+#define RK3568_VP2_DSP_VS_ST_END_F1			0xE58
+#define RK3568_VP2_DSP_VACT_ST_END_F1			0xE5C
+#define RK3568_VP2_BCSH_CTRL				0xE60
+#define RK3568_VP2_BCSH_BCS				0xE64
+#define RK3568_VP2_BCSH_H				0xE68
+#define RK3568_VP2_BCSH_COLOR_BAR			0xE6C
+
+#define RK3588_VP3_DSP_CTRL				0xF00
+#define RK3588_VP3_DUAL_CHANNEL_CTRL			0xF04
+#define RK3588_VP3_COLOR_BAR_CTRL			0xF08
+#define RK3568_VP3_CLK_CTRL				0xF0C
+#define RK3588_VP3_DSP_BG				0xF2C
+#define RK3588_VP3_PRE_SCAN_HTIMING			0xF30
+#define RK3588_VP3_POST_DSP_HACT_INFO			0xF34
+#define RK3588_VP3_POST_DSP_VACT_INFO			0xF38
+#define RK3588_VP3_POST_SCL_FACTOR_YRGB			0xF3C
+#define RK3588_VP3_POST_SCL_CTRL			0xF40
+#define RK3588_VP3_DSP_HACT_INFO			0xF34
+#define RK3588_VP3_DSP_VACT_INFO			0xF38
+#define RK3588_VP3_POST_DSP_VACT_INFO_F1		0xF44
+#define RK3588_VP3_DSP_HTOTAL_HS_END			0xF48
+#define RK3588_VP3_DSP_HACT_ST_END			0xF4C
+#define RK3588_VP3_DSP_VTOTAL_VS_END			0xF50
+#define RK3588_VP3_DSP_VACT_ST_END			0xF54
+#define RK3588_VP3_DSP_VS_ST_END_F1			0xF58
+#define RK3588_VP3_DSP_VACT_ST_END_F1			0xF5C
+#define RK3588_VP3_BCSH_CTRL				0xF60
+#define RK3588_VP3_BCSH_BCS				0xF64
+#define RK3588_VP3_BCSH_H				0xF68
+#define RK3588_VP3_BCSH_COLOR_BAR			0xF6C
+
+/* Overlay registers definition    */
+#define RK3568_OVL_CTRL				0x600
+#define RK3568_OVL_LAYER_SEL			0x604
+#define RK3568_OVL_PORT_SEL			0x608
+#define RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL	0x610
+#define RK3568_CLUSTER0_MIX_DST_COLOR_CTRL	0x614
+#define RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL	0x618
+#define RK3568_CLUSTER0_MIX_DST_ALPHA_CTRL	0x61C
+#define RK3568_MIX0_SRC_COLOR_CTRL		0x650
+#define RK3568_MIX0_DST_COLOR_CTRL		0x654
+#define RK3568_MIX0_SRC_ALPHA_CTRL		0x658
+#define RK3568_MIX0_DST_ALPHA_CTRL		0x65C
+#define RK3568_HDR0_SRC_COLOR_CTRL		0x6C0
+#define RK3568_HDR0_DST_COLOR_CTRL		0x6C4
+#define RK3568_HDR0_SRC_ALPHA_CTRL		0x6C8
+#define RK3568_HDR0_DST_ALPHA_CTRL		0x6CC
+#define RK3568_HDR1_SRC_COLOR_CTRL		0x6D0
+#define RK3568_HDR1_DST_COLOR_CTRL		0x6D4
+#define RK3568_HDR1_SRC_ALPHA_CTRL		0x6D8
+#define RK3568_HDR1_DST_ALPHA_CTRL		0x6DC
+#define RK3568_VP0_BG_MIX_CTRL			0x6E0
+#define RK3568_VP1_BG_MIX_CTRL			0x6E4
+#define RK3568_VP2_BG_MIX_CTRL			0x6E8
+#define RK3588_VP3_BG_MIX_CTRL			0x6EC
+#define RK3568_CLUSTER_DLY_NUM			0x6F0
+#define RK3568_CLUSTER_DLY_NUM1			0x6F4
+#define RK3568_SMART_DLY_NUM			0x6F8
+
+/* Cluster0 register definition */
+#define RK3568_CLUSTER0_WIN0_CTRL0		0x1000
+#define RK3568_CLUSTER0_WIN0_CTRL1		0x1004
+#define RK3568_CLUSTER0_WIN0_CTRL2		0x1008
+#define RK3568_CLUSTER0_WIN0_YRGB_MST		0x1010
+#define RK3568_CLUSTER0_WIN0_CBR_MST		0x1014
+#define RK3568_CLUSTER0_WIN0_VIR		0x1018
+#define RK3568_CLUSTER0_WIN0_ACT_INFO		0x1020
+#define RK3568_CLUSTER0_WIN0_DSP_INFO		0x1024
+#define RK3568_CLUSTER0_WIN0_DSP_ST		0x1028
+#define RK3568_CLUSTER0_WIN0_SCL_FACTOR_YRGB	0x1030
+#define RK3568_CLUSTER0_WIN0_AFBCD_TRANSFORM_OFFSET	0x103C
+#define RK3568_CLUSTER0_WIN0_AFBCD_OUTPUT_CTRL	0x1050
+#define RK3568_CLUSTER0_WIN0_AFBCD_ROTATE_MODE	0x1054
+#define RK3568_CLUSTER0_WIN0_AFBCD_HDR_PTR	0x1058
+#define RK3568_CLUSTER0_WIN0_AFBCD_VIR_WIDTH	0x105C
+#define RK3568_CLUSTER0_WIN0_AFBCD_PIC_SIZE	0x1060
+#define RK3568_CLUSTER0_WIN0_AFBCD_PIC_OFFSET	0x1064
+#define RK3568_CLUSTER0_WIN0_AFBCD_DSP_OFFSET	0x1068
+#define RK3568_CLUSTER0_WIN0_AFBCD_CTRL		0x106C
+
+#define RK3568_CLUSTER0_WIN1_CTRL0		0x1080
+#define RK3568_CLUSTER0_WIN1_CTRL1		0x1084
+#define RK3568_CLUSTER0_WIN1_YRGB_MST		0x1090
+#define RK3568_CLUSTER0_WIN1_CBR_MST		0x1094
+#define RK3568_CLUSTER0_WIN1_VIR		0x1098
+#define RK3568_CLUSTER0_WIN1_ACT_INFO		0x10A0
+#define RK3568_CLUSTER0_WIN1_DSP_INFO		0x10A4
+#define RK3568_CLUSTER0_WIN1_DSP_ST		0x10A8
+#define RK3568_CLUSTER0_WIN1_SCL_FACTOR_YRGB	0x10B0
+#define RK3568_CLUSTER0_WIN1_AFBCD_OUTPUT_CTRL	0x10D0
+#define RK3568_CLUSTER0_WIN1_AFBCD_ROTATE_MODE	0x10D4
+#define RK3568_CLUSTER0_WIN1_AFBCD_HDR_PTR	0x10D8
+#define RK3568_CLUSTER0_WIN1_AFBCD_VIR_WIDTH	0x10DC
+#define RK3568_CLUSTER0_WIN1_AFBCD_PIC_SIZE	0x10E0
+#define RK3568_CLUSTER0_WIN1_AFBCD_PIC_OFFSET	0x10E4
+#define RK3568_CLUSTER0_WIN1_AFBCD_DSP_OFFSET	0x10E8
+#define RK3568_CLUSTER0_WIN1_AFBCD_CTRL		0x10EC
+
+#define RK3568_CLUSTER0_CTRL			0x1100
+
+#define RK3568_CLUSTER1_WIN0_CTRL0		0x1200
+#define RK3568_CLUSTER1_WIN0_CTRL1		0x1204
+#define RK3568_CLUSTER1_WIN0_CTRL2		0x1208
+#define RK3568_CLUSTER1_WIN0_YRGB_MST		0x1210
+#define RK3568_CLUSTER1_WIN0_CBR_MST		0x1214
+#define RK3568_CLUSTER1_WIN0_VIR		0x1218
+#define RK3568_CLUSTER1_WIN0_ACT_INFO		0x1220
+#define RK3568_CLUSTER1_WIN0_DSP_INFO		0x1224
+#define RK3568_CLUSTER1_WIN0_DSP_ST		0x1228
+#define RK3568_CLUSTER1_WIN0_SCL_FACTOR_YRGB	0x1230
+#define RK3568_CLUSTER1_WIN0_AFBCD_TRANSFORM_OFFSET	0x123C
+#define RK3568_CLUSTER1_WIN0_AFBCD_OUTPUT_CTRL	0x1250
+#define RK3568_CLUSTER1_WIN0_AFBCD_ROTATE_MODE	0x1254
+#define RK3568_CLUSTER1_WIN0_AFBCD_HDR_PTR	0x1258
+#define RK3568_CLUSTER1_WIN0_AFBCD_VIR_WIDTH	0x125C
+#define RK3568_CLUSTER1_WIN0_AFBCD_PIC_SIZE	0x1260
+#define RK3568_CLUSTER1_WIN0_AFBCD_PIC_OFFSET	0x1264
+#define RK3568_CLUSTER1_WIN0_AFBCD_DSP_OFFSET	0x1268
+#define RK3568_CLUSTER1_WIN0_AFBCD_CTRL		0x126C
+
+#define RK3568_CLUSTER1_WIN1_CTRL0		0x1280
+#define RK3568_CLUSTER1_WIN1_CTRL1		0x1284
+#define RK3568_CLUSTER1_WIN1_YRGB_MST		0x1290
+#define RK3568_CLUSTER1_WIN1_CBR_MST		0x1294
+#define RK3568_CLUSTER1_WIN1_VIR		0x1298
+#define RK3568_CLUSTER1_WIN1_ACT_INFO		0x12A0
+#define RK3568_CLUSTER1_WIN1_DSP_INFO		0x12A4
+#define RK3568_CLUSTER1_WIN1_DSP_ST		0x12A8
+#define RK3568_CLUSTER1_WIN1_SCL_FACTOR_YRGB	0x12B0
+#define RK3568_CLUSTER1_WIN1_AFBCD_OUTPUT_CTRL	0x12D0
+#define RK3568_CLUSTER1_WIN1_AFBCD_ROTATE_MODE	0x12D4
+#define RK3568_CLUSTER1_WIN1_AFBCD_HDR_PTR	0x12D8
+#define RK3568_CLUSTER1_WIN1_AFBCD_VIR_WIDTH	0x12DC
+#define RK3568_CLUSTER1_WIN1_AFBCD_PIC_SIZE	0x12E0
+#define RK3568_CLUSTER1_WIN1_AFBCD_PIC_OFFSET	0x12E4
+#define RK3568_CLUSTER1_WIN1_AFBCD_DSP_OFFSET	0x12E8
+#define RK3568_CLUSTER1_WIN1_AFBCD_CTRL		0x12EC
+
+#define RK3568_CLUSTER1_CTRL			0x1300
+
+#define RK3588_CLUSTER2_WIN0_CTRL0		0x1400
+#define RK3588_CLUSTER2_WIN0_CTRL1		0x1404
+#define RK3588_CLUSTER2_WIN0_CTRL2		0x1408
+#define RK3588_CLUSTER2_WIN0_YRGB_MST		0x1410
+#define RK3588_CLUSTER2_WIN0_CBR_MST		0x1414
+#define RK3588_CLUSTER2_WIN0_VIR		0x1418
+#define RK3588_CLUSTER2_WIN0_ACT_INFO		0x1420
+#define RK3588_CLUSTER2_WIN0_DSP_INFO		0x1424
+#define RK3588_CLUSTER2_WIN0_DSP_ST		0x1428
+#define RK3588_CLUSTER2_WIN0_SCL_FACTOR_YRGB	0x1430
+#define RK3588_CLUSTER2_WIN0_AFBCD_TRANSFORM_OFFSET	0x143C
+#define RK3588_CLUSTER2_WIN0_AFBCD_OUTPUT_CTRL	0x1450
+#define RK3588_CLUSTER2_WIN0_AFBCD_ROTATE_MODE	0x1454
+#define RK3588_CLUSTER2_WIN0_AFBCD_HDR_PTR	0x1458
+#define RK3588_CLUSTER2_WIN0_AFBCD_VIR_WIDTH	0x145C
+#define RK3588_CLUSTER2_WIN0_AFBCD_PIC_SIZE	0x1460
+#define RK3588_CLUSTER2_WIN0_AFBCD_PIC_OFFSET	0x1464
+#define RK3588_CLUSTER2_WIN0_AFBCD_DSP_OFFSET	0x1468
+#define RK3588_CLUSTER2_WIN0_AFBCD_CTRL		0x146C
+
+#define RK3588_CLUSTER2_WIN1_CTRL0		0x1480
+#define RK3588_CLUSTER2_WIN1_CTRL1		0x1484
+#define RK3588_CLUSTER2_WIN1_YRGB_MST		0x1490
+#define RK3588_CLUSTER2_WIN1_CBR_MST		0x1494
+#define RK3588_CLUSTER2_WIN1_VIR		0x1498
+#define RK3588_CLUSTER2_WIN1_ACT_INFO		0x14A0
+#define RK3588_CLUSTER2_WIN1_DSP_INFO		0x14A4
+#define RK3588_CLUSTER2_WIN1_DSP_ST		0x14A8
+#define RK3588_CLUSTER2_WIN1_SCL_FACTOR_YRGB	0x14B0
+#define RK3588_CLUSTER2_WIN1_AFBCD_OUTPUT_CTRL	0x14D0
+#define RK3588_CLUSTER2_WIN1_AFBCD_ROTATE_MODE	0x14D4
+#define RK3588_CLUSTER2_WIN1_AFBCD_HDR_PTR	0x14D8
+#define RK3588_CLUSTER2_WIN1_AFBCD_VIR_WIDTH	0x14DC
+#define RK3588_CLUSTER2_WIN1_AFBCD_PIC_SIZE	0x14E0
+#define RK3588_CLUSTER2_WIN1_AFBCD_PIC_OFFSET	0x14E4
+#define RK3588_CLUSTER2_WIN1_AFBCD_DSP_OFFSET	0x14E8
+#define RK3588_CLUSTER2_WIN1_AFBCD_CTRL		0x14EC
+
+#define RK3588_CLUSTER2_CTRL			0x1500
+
+#define RK3588_CLUSTER3_WIN0_CTRL0		0x1600
+#define RK3588_CLUSTER3_WIN0_CTRL1		0x1604
+#define RK3588_CLUSTER3_WIN0_CTRL2		0x1608
+#define RK3588_CLUSTER3_WIN0_YRGB_MST		0x1610
+#define RK3588_CLUSTER3_WIN0_CBR_MST		0x1614
+#define RK3588_CLUSTER3_WIN0_VIR		0x1618
+#define RK3588_CLUSTER3_WIN0_ACT_INFO		0x1620
+#define RK3588_CLUSTER3_WIN0_DSP_INFO		0x1624
+#define RK3588_CLUSTER3_WIN0_DSP_ST		0x1628
+#define RK3588_CLUSTER3_WIN0_SCL_FACTOR_YRGB	0x1630
+#define RK3588_CLUSTER3_WIN0_AFBCD_TRANSFORM_OFFSET	0x163C
+#define RK3588_CLUSTER3_WIN0_AFBCD_OUTPUT_CTRL	0x1650
+#define RK3588_CLUSTER3_WIN0_AFBCD_ROTATE_MODE	0x1654
+#define RK3588_CLUSTER3_WIN0_AFBCD_HDR_PTR	0x1658
+#define RK3588_CLUSTER3_WIN0_AFBCD_VIR_WIDTH	0x165C
+#define RK3588_CLUSTER3_WIN0_AFBCD_PIC_SIZE	0x1660
+#define RK3588_CLUSTER3_WIN0_AFBCD_PIC_OFFSET	0x1664
+#define RK3588_CLUSTER3_WIN0_AFBCD_DSP_OFFSET	0x1668
+#define RK3588_CLUSTER3_WIN0_AFBCD_CTRL		0x166C
+
+#define RK3588_CLUSTER3_WIN1_CTRL0		0x1680
+#define RK3588_CLUSTER3_WIN1_CTRL1		0x1684
+#define RK3588_CLUSTER3_WIN1_YRGB_MST		0x1690
+#define RK3588_CLUSTER3_WIN1_CBR_MST		0x1694
+#define RK3588_CLUSTER3_WIN1_VIR		0x1698
+#define RK3588_CLUSTER3_WIN1_ACT_INFO		0x16A0
+#define RK3588_CLUSTER3_WIN1_DSP_INFO		0x16A4
+#define RK3588_CLUSTER3_WIN1_DSP_ST		0x16A8
+#define RK3588_CLUSTER3_WIN1_SCL_FACTOR_YRGB	0x16B0
+#define RK3588_CLUSTER3_WIN1_AFBCD_OUTPUT_CTRL	0x16D0
+#define RK3588_CLUSTER3_WIN1_AFBCD_ROTATE_MODE	0x16D4
+#define RK3588_CLUSTER3_WIN1_AFBCD_HDR_PTR	0x16D8
+#define RK3588_CLUSTER3_WIN1_AFBCD_VIR_WIDTH	0x16DC
+#define RK3588_CLUSTER3_WIN1_AFBCD_PIC_SIZE	0x16E0
+#define RK3588_CLUSTER3_WIN1_AFBCD_PIC_OFFSET	0x16E4
+#define RK3588_CLUSTER3_WIN1_AFBCD_DSP_OFFSET	0x16E8
+#define RK3588_CLUSTER3_WIN1_AFBCD_CTRL		0x16EC
+
+#define RK3588_CLUSTER3_CTRL			0x1700
+
+/* Esmart register definition */
+#define RK3568_ESMART0_CTRL0			0x1800
+#define RK3568_ESMART0_CTRL1			0x1804
+#define RK3568_ESMART0_AXI_CTRL			0x1808
+#define RK3568_ESMART0_REGION0_CTRL		0x1810
+#define RK3568_ESMART0_REGION0_YRGB_MST		0x1814
+#define RK3568_ESMART0_REGION0_CBR_MST		0x1818
+#define RK3568_ESMART0_REGION0_VIR		0x181C
+#define RK3568_ESMART0_REGION0_ACT_INFO		0x1820
+#define RK3568_ESMART0_REGION0_DSP_INFO		0x1824
+#define RK3568_ESMART0_REGION0_DSP_ST		0x1828
+#define RK3568_ESMART0_REGION0_SCL_CTRL		0x1830
+#define RK3568_ESMART0_REGION0_SCL_FACTOR_YRGB	0x1834
+#define RK3568_ESMART0_REGION0_SCL_FACTOR_CBR	0x1838
+#define RK3568_ESMART0_REGION0_SCL_OFFSET	0x183C
+#define RK3568_ESMART0_REGION1_CTRL		0x1840
+#define RK3568_ESMART0_REGION1_YRGB_MST		0x1844
+#define RK3568_ESMART0_REGION1_CBR_MST		0x1848
+#define RK3568_ESMART0_REGION1_VIR		0x184C
+#define RK3568_ESMART0_REGION1_ACT_INFO		0x1850
+#define RK3568_ESMART0_REGION1_DSP_INFO		0x1854
+#define RK3568_ESMART0_REGION1_DSP_ST		0x1858
+#define RK3568_ESMART0_REGION1_SCL_CTRL		0x1860
+#define RK3568_ESMART0_REGION1_SCL_FACTOR_YRGB	0x1864
+#define RK3568_ESMART0_REGION1_SCL_FACTOR_CBR	0x1868
+#define RK3568_ESMART0_REGION1_SCL_OFFSET	0x186C
+#define RK3568_ESMART0_REGION2_CTRL		0x1870
+#define RK3568_ESMART0_REGION2_YRGB_MST		0x1874
+#define RK3568_ESMART0_REGION2_CBR_MST		0x1878
+#define RK3568_ESMART0_REGION2_VIR		0x187C
+#define RK3568_ESMART0_REGION2_ACT_INFO		0x1880
+#define RK3568_ESMART0_REGION2_DSP_INFO		0x1884
+#define RK3568_ESMART0_REGION2_DSP_ST		0x1888
+#define RK3568_ESMART0_REGION2_SCL_CTRL		0x1890
+#define RK3568_ESMART0_REGION2_SCL_FACTOR_YRGB	0x1894
+#define RK3568_ESMART0_REGION2_SCL_FACTOR_CBR	0x1898
+#define RK3568_ESMART0_REGION2_SCL_OFFSET	0x189C
+#define RK3568_ESMART0_REGION3_CTRL		0x18A0
+#define RK3568_ESMART0_REGION3_YRGB_MST		0x18A4
+#define RK3568_ESMART0_REGION3_CBR_MST		0x18A8
+#define RK3568_ESMART0_REGION3_VIR		0x18AC
+#define RK3568_ESMART0_REGION3_ACT_INFO		0x18B0
+#define RK3568_ESMART0_REGION3_DSP_INFO		0x18B4
+#define RK3568_ESMART0_REGION3_DSP_ST		0x18B8
+#define RK3568_ESMART0_REGION3_SCL_CTRL		0x18C0
+#define RK3568_ESMART0_REGION3_SCL_FACTOR_YRGB	0x18C4
+#define RK3568_ESMART0_REGION3_SCL_FACTOR_CBR	0x18C8
+#define RK3568_ESMART0_REGION3_SCL_OFFSET	0x18CC
+#define RK3568_ESMART0_COLOR_KEY_CTRL		0x18D0
+
+#define RK3568_ESMART1_CTRL0			0x1A00
+#define RK3568_ESMART1_CTRL1			0x1A04
+#define RK3568_ESMART1_REGION0_CTRL		0x1A10
+#define RK3568_ESMART1_REGION0_YRGB_MST		0x1A14
+#define RK3568_ESMART1_REGION0_CBR_MST		0x1A18
+#define RK3568_ESMART1_REGION0_VIR		0x1A1C
+#define RK3568_ESMART1_REGION0_ACT_INFO		0x1A20
+#define RK3568_ESMART1_REGION0_DSP_INFO		0x1A24
+#define RK3568_ESMART1_REGION0_DSP_ST		0x1A28
+#define RK3568_ESMART1_REGION0_SCL_CTRL		0x1A30
+#define RK3568_ESMART1_REGION0_SCL_FACTOR_YRGB	0x1A34
+#define RK3568_ESMART1_REGION0_SCL_FACTOR_CBR	0x1A38
+#define RK3568_ESMART1_REGION0_SCL_OFFSET	0x1A3C
+#define RK3568_ESMART1_REGION1_CTRL		0x1A40
+#define RK3568_ESMART1_REGION1_YRGB_MST		0x1A44
+#define RK3568_ESMART1_REGION1_CBR_MST		0x1A48
+#define RK3568_ESMART1_REGION1_VIR		0x1A4C
+#define RK3568_ESMART1_REGION1_ACT_INFO		0x1A50
+#define RK3568_ESMART1_REGION1_DSP_INFO		0x1A54
+#define RK3568_ESMART1_REGION1_DSP_ST		0x1A58
+#define RK3568_ESMART1_REGION1_SCL_CTRL		0x1A60
+#define RK3568_ESMART1_REGION1_SCL_FACTOR_YRGB	0x1A64
+#define RK3568_ESMART1_REGION1_SCL_FACTOR_CBR	0x1A68
+#define RK3568_ESMART1_REGION1_SCL_OFFSET	0x1A6C
+#define RK3568_ESMART1_REGION2_CTRL		0x1A70
+#define RK3568_ESMART1_REGION2_YRGB_MST		0x1A74
+#define RK3568_ESMART1_REGION2_CBR_MST		0x1A78
+#define RK3568_ESMART1_REGION2_VIR		0x1A7C
+#define RK3568_ESMART1_REGION2_ACT_INFO		0x1A80
+#define RK3568_ESMART1_REGION2_DSP_INFO		0x1A84
+#define RK3568_ESMART1_REGION2_DSP_ST		0x1A88
+#define RK3568_ESMART1_REGION2_SCL_CTRL		0x1A90
+#define RK3568_ESMART1_REGION2_SCL_FACTOR_YRGB	0x1A94
+#define RK3568_ESMART1_REGION2_SCL_FACTOR_CBR	0x1A98
+#define RK3568_ESMART1_REGION2_SCL_OFFSET	0x1A9C
+#define RK3568_ESMART1_REGION3_CTRL		0x1AA0
+#define RK3568_ESMART1_REGION3_YRGB_MST		0x1AA4
+#define RK3568_ESMART1_REGION3_CBR_MST		0x1AA8
+#define RK3568_ESMART1_REGION3_VIR		0x1AAC
+#define RK3568_ESMART1_REGION3_ACT_INFO		0x1AB0
+#define RK3568_ESMART1_REGION3_DSP_INFO		0x1AB4
+#define RK3568_ESMART1_REGION3_DSP_ST		0x1AB8
+#define RK3568_ESMART1_REGION3_SCL_CTRL		0x1AC0
+#define RK3568_ESMART1_REGION3_SCL_FACTOR_YRGB	0x1AC4
+#define RK3568_ESMART1_REGION3_SCL_FACTOR_CBR	0x1AC8
+#define RK3568_ESMART1_REGION3_SCL_OFFSET	0x1ACC
+
+#define RK3568_SMART0_CTRL0			0x1C00
+#define RK3568_SMART0_CTRL1			0x1C04
+#define RK3568_SMART0_REGION0_CTRL		0x1C10
+#define RK3568_SMART0_REGION0_YRGB_MST		0x1C14
+#define RK3568_SMART0_REGION0_CBR_MST		0x1C18
+#define RK3568_SMART0_REGION0_VIR		0x1C1C
+#define RK3568_SMART0_REGION0_ACT_INFO		0x1C20
+#define RK3568_SMART0_REGION0_DSP_INFO		0x1C24
+#define RK3568_SMART0_REGION0_DSP_ST		0x1C28
+#define RK3568_SMART0_REGION0_SCL_CTRL		0x1C30
+#define RK3568_SMART0_REGION0_SCL_FACTOR_YRGB	0x1C34
+#define RK3568_SMART0_REGION0_SCL_FACTOR_CBR	0x1C38
+#define RK3568_SMART0_REGION0_SCL_OFFSET	0x1C3C
+#define RK3568_SMART0_REGION1_CTRL		0x1C40
+#define RK3568_SMART0_REGION1_YRGB_MST		0x1C44
+#define RK3568_SMART0_REGION1_CBR_MST		0x1C48
+#define RK3568_SMART0_REGION1_VIR		0x1C4C
+#define RK3568_SMART0_REGION1_ACT_INFO		0x1C50
+#define RK3568_SMART0_REGION1_DSP_INFO		0x1C54
+#define RK3568_SMART0_REGION1_DSP_ST		0x1C58
+#define RK3568_SMART0_REGION1_SCL_CTRL		0x1C60
+#define RK3568_SMART0_REGION1_SCL_FACTOR_YRGB	0x1C64
+#define RK3568_SMART0_REGION1_SCL_FACTOR_CBR	0x1C68
+#define RK3568_SMART0_REGION1_SCL_OFFSET	0x1C6C
+#define RK3568_SMART0_REGION2_CTRL		0x1C70
+#define RK3568_SMART0_REGION2_YRGB_MST		0x1C74
+#define RK3568_SMART0_REGION2_CBR_MST		0x1C78
+#define RK3568_SMART0_REGION2_VIR		0x1C7C
+#define RK3568_SMART0_REGION2_ACT_INFO		0x1C80
+#define RK3568_SMART0_REGION2_DSP_INFO		0x1C84
+#define RK3568_SMART0_REGION2_DSP_ST		0x1C88
+#define RK3568_SMART0_REGION2_SCL_CTRL		0x1C90
+#define RK3568_SMART0_REGION2_SCL_FACTOR_YRGB	0x1C94
+#define RK3568_SMART0_REGION2_SCL_FACTOR_CBR	0x1C98
+#define RK3568_SMART0_REGION2_SCL_OFFSET	0x1C9C
+#define RK3568_SMART0_REGION3_CTRL		0x1CA0
+#define RK3568_SMART0_REGION3_YRGB_MST		0x1CA4
+#define RK3568_SMART0_REGION3_CBR_MST		0x1CA8
+#define RK3568_SMART0_REGION3_VIR		0x1CAC
+#define RK3568_SMART0_REGION3_ACT_INFO		0x1CB0
+#define RK3568_SMART0_REGION3_DSP_INFO		0x1CB4
+#define RK3568_SMART0_REGION3_DSP_ST		0x1CB8
+#define RK3568_SMART0_REGION3_SCL_CTRL		0x1CC0
+#define RK3568_SMART0_REGION3_SCL_FACTOR_YRGB	0x1CC4
+#define RK3568_SMART0_REGION3_SCL_FACTOR_CBR	0x1CC8
+#define RK3568_SMART0_REGION3_SCL_OFFSET	0x1CCC
+
+#define RK3568_SMART1_CTRL0			0x1E00
+#define RK3568_SMART1_CTRL1			0x1E04
+#define RK3568_SMART1_REGION0_CTRL		0x1E10
+#define RK3568_SMART1_REGION0_YRGB_MST		0x1E14
+#define RK3568_SMART1_REGION0_CBR_MST		0x1E18
+#define RK3568_SMART1_REGION0_VIR		0x1E1C
+#define RK3568_SMART1_REGION0_ACT_INFO		0x1E20
+#define RK3568_SMART1_REGION0_DSP_INFO		0x1E24
+#define RK3568_SMART1_REGION0_DSP_ST		0x1E28
+#define RK3568_SMART1_REGION0_SCL_CTRL		0x1E30
+#define RK3568_SMART1_REGION0_SCL_FACTOR_YRGB	0x1E34
+#define RK3568_SMART1_REGION0_SCL_FACTOR_CBR	0x1E38
+#define RK3568_SMART1_REGION0_SCL_OFFSET	0x1E3C
+#define RK3568_SMART1_REGION1_CTRL		0x1E40
+#define RK3568_SMART1_REGION1_YRGB_MST		0x1E44
+#define RK3568_SMART1_REGION1_CBR_MST		0x1E48
+#define RK3568_SMART1_REGION1_VIR		0x1E4C
+#define RK3568_SMART1_REGION1_ACT_INFO		0x1E50
+#define RK3568_SMART1_REGION1_DSP_INFO		0x1E54
+#define RK3568_SMART1_REGION1_DSP_ST		0x1E58
+#define RK3568_SMART1_REGION1_SCL_CTRL		0x1E60
+#define RK3568_SMART1_REGION1_SCL_FACTOR_YRGB	0x1E64
+#define RK3568_SMART1_REGION1_SCL_FACTOR_CBR	0x1E68
+#define RK3568_SMART1_REGION1_SCL_OFFSET	0x1E6C
+#define RK3568_SMART1_REGION2_CTRL		0x1E70
+#define RK3568_SMART1_REGION2_YRGB_MST		0x1E74
+#define RK3568_SMART1_REGION2_CBR_MST		0x1E78
+#define RK3568_SMART1_REGION2_VIR		0x1E7C
+#define RK3568_SMART1_REGION2_ACT_INFO		0x1E80
+#define RK3568_SMART1_REGION2_DSP_INFO		0x1E84
+#define RK3568_SMART1_REGION2_DSP_ST		0x1E88
+#define RK3568_SMART1_REGION2_SCL_CTRL		0x1E90
+#define RK3568_SMART1_REGION2_SCL_FACTOR_YRGB	0x1E94
+#define RK3568_SMART1_REGION2_SCL_FACTOR_CBR	0x1E98
+#define RK3568_SMART1_REGION2_SCL_OFFSET	0x1E9C
+#define RK3568_SMART1_REGION3_CTRL		0x1EA0
+#define RK3568_SMART1_REGION3_YRGB_MST		0x1EA4
+#define RK3568_SMART1_REGION3_CBR_MST		0x1EA8
+#define RK3568_SMART1_REGION3_VIR		0x1EAC
+#define RK3568_SMART1_REGION3_ACT_INFO		0x1EB0
+#define RK3568_SMART1_REGION3_DSP_INFO		0x1EB4
+#define RK3568_SMART1_REGION3_DSP_ST		0x1EB8
+#define RK3568_SMART1_REGION3_SCL_CTRL		0x1EC0
+#define RK3568_SMART1_REGION3_SCL_FACTOR_YRGB	0x1EC4
+#define RK3568_SMART1_REGION3_SCL_FACTOR_CBR	0x1EC8
+#define RK3568_SMART1_REGION3_SCL_OFFSET	0x1ECC
+
+/* HDR register definition */
+#define RK3568_HDR_LUT_CTRL				0x2000
+#define RK3568_HDR_LUT_MST				0x2004
+#define RK3568_SDR2HDR_CTRL				0x2010
+/* for HDR10 controller1 */
+#define RK3568_SDR2HDR_CTRL1				0x2018
+#define RK3568_HDR2SDR_CTRL1				0x201C
+#define RK3568_HDR2SDR_CTRL				0x2020
+#define RK3568_HDR2SDR_SRC_RANGE			0x2024
+#define RK3568_HDR2SDR_NORMFACEETF			0x2028
+#define RK3568_HDR2SDR_DST_RANGE			0x202C
+#define RK3568_HDR2SDR_NORMFACCGAMMA			0x2030
+#define RK3568_HDR_EETF_OETF_Y0				0x203C
+#define RK3568_HDR_SAT_Y0				0x20C0
+#define RK3568_HDR_EOTF_OETF_Y0				0x20F0
+#define RK3568_HDR_OETF_DX_POW1				0x2200
+#define RK3568_HDR_OETF_XN1				0x2300
+
+/* DSC register definition */
+#define RK3588_DSC_8K_PPS0_3				0x4000
+#define RK3588_DSC_8K_CTRL0				0x40A0
+#define RK3588_DSC_8K_CTRL1				0x40A4
+#define RK3588_DSC_8K_STS0				0x40A8
+#define RK3588_DSC_8K_ERS				0x40C4
+
+#define RK3588_DSC_4K_PPS0_3				0x4100
+#define RK3588_DSC_4K_CTRL0				0x41A0
+#define RK3588_DSC_4K_CTRL1				0x41A4
+#define RK3588_DSC_4K_STS0				0x41A8
+#define RK3588_DSC_4K_ERS				0x41C4
+
+#define RK3588_GRF_SOC_CON1				0x0304
+#define RK3588_GRF_VOP_CON2				0x08
+#define RK3588_GRF_VO1_CON0				0x00
+
+
+#define RK3588_PMU_PWR_GATE_CON1			0x150
+#define RK3588_PMU_SUBMEM_PWR_GATE_CON1			0x1B4
+#define RK3588_PMU_SUBMEM_PWR_GATE_CON2			0x1B8
+#define RK3588_PMU_SUBMEM_PWR_GATE_STATUS		0x1BC
+#define RK3588_PMU_BISR_CON3				0x20C
+#define RK3588_PMU_BISR_STATUS5				0x294
 
 #endif /* _ROCKCHIP_VOP_REG_H */
diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c
index 02ddb237f..cd137f05f 100644
--- a/drivers/i2c/busses/i2c-rk3x.c
+++ b/drivers/i2c/busses/i2c-rk3x.c
@@ -23,6 +23,8 @@
 #include <linux/mfd/syscon.h>
 #include <linux/regmap.h>
 #include <linux/math64.h>
+#include <linux/reboot.h>
+#include <linux/delay.h>
 
 
 /* Register Map */
@@ -35,6 +37,7 @@
 #define REG_IEN        0x18 /* interrupt enable */
 #define REG_IPD        0x1c /* interrupt pending */
 #define REG_FCNT       0x20 /* finished count */
+#define REG_CON1       0x228 /* control register1 */
 
 /* Data buffer offsets */
 #define TXBUFFER_BASE 0x100
@@ -62,6 +65,15 @@ enum {
 #define REG_CON_STA_CFG(cfg) ((cfg) << 12)
 #define REG_CON_STO_CFG(cfg) ((cfg) << 14)
 
+enum {
+	RK_I2C_VERSION0 = 0,
+	RK_I2C_VERSION1,
+	RK_I2C_VERSION5 = 5,
+};
+
+#define REG_CON_VERSION GENMASK_ULL(24, 16)
+#define REG_CON_VERSION_SHIFT 16
+
 /* REG_MRXADDR bits */
 #define REG_MRXADDR_VALID(x) BIT(24 + (x)) /* [x*8+7:x*8] of MRX[R]ADDR valid */
 
@@ -73,7 +85,14 @@ enum {
 #define REG_INT_START     BIT(4) /* START condition generated */
 #define REG_INT_STOP      BIT(5) /* STOP condition generated */
 #define REG_INT_NAKRCV    BIT(6) /* NACK received */
-#define REG_INT_ALL       0x7f
+#define REG_INT_ALL       0xff
+
+/* Disable i2c all irqs */
+#define IEN_ALL_DISABLE   0
+
+#define REG_CON1_AUTO_STOP BIT(0)
+#define REG_CON1_TRANSFER_AUTO_STOP BIT(1)
+#define REG_CON1_NACK_AUTO_STOP BIT(2)
 
 /* Constants */
 #define WAIT_TIMEOUT      1000 /* ms */
@@ -152,7 +171,6 @@ struct rk3x_i2c_calced_timings {
 
 enum rk3x_i2c_state {
 	STATE_IDLE,
-	STATE_START,
 	STATE_READ,
 	STATE_WRITE,
 	STATE_STOP
@@ -189,6 +207,8 @@ struct rk3x_i2c_soc_data {
  * @state: state of i2c transfer
  * @processed: byte length which has been send or received
  * @error: error code for i2c transfer
+ * @i2c_restart_nb: make sure the i2c transfer to be finished
+ * @system_restarting: true if system is restarting
  */
 struct rk3x_i2c {
 	struct i2c_adapter adap;
@@ -200,6 +220,7 @@ struct rk3x_i2c {
 	struct clk *clk;
 	struct clk *pclk;
 	struct notifier_block clk_rate_nb;
+	bool autostop_supported;
 
 	/* Settings */
 	struct i2c_timings t;
@@ -219,8 +240,21 @@ struct rk3x_i2c {
 	enum rk3x_i2c_state state;
 	unsigned int processed;
 	int error;
+	unsigned int suspended:1;
+
+	struct notifier_block i2c_restart_nb;
+	bool system_restarting;
 };
 
+static void rk3x_i2c_prepare_read(struct rk3x_i2c *i2c);
+static int rk3x_i2c_fill_transmit_buf(struct rk3x_i2c *i2c, bool sended);
+
+static inline void rk3x_i2c_wake_up(struct rk3x_i2c *i2c)
+{
+	if (!i2c->system_restarting)
+		wake_up(&i2c->wait);
+}
+
 static inline void i2c_writel(struct rk3x_i2c *i2c, u32 value,
 			      unsigned int offset)
 {
@@ -238,14 +272,75 @@ static inline void rk3x_i2c_clean_ipd(struct rk3x_i2c *i2c)
 	i2c_writel(i2c, REG_INT_ALL, REG_IPD);
 }
 
+static inline void rk3x_i2c_disable_irq(struct rk3x_i2c *i2c)
+{
+	i2c_writel(i2c, IEN_ALL_DISABLE, REG_IEN);
+}
+
+static inline void rk3x_i2c_disable(struct rk3x_i2c *i2c)
+{
+	u32 val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK;
+
+	i2c_writel(i2c, val, REG_CON);
+}
+
+static bool rk3x_i2c_auto_stop(struct rk3x_i2c *i2c)
+{
+	unsigned int len, con1 = 0;
+
+	if (!i2c->autostop_supported)
+		return false;
+
+	if (!(i2c->msg->flags & I2C_M_IGNORE_NAK))
+		con1 = REG_CON1_NACK_AUTO_STOP | REG_CON1_AUTO_STOP;
+
+	if (!i2c->is_last_msg)
+		goto out;
+
+	len = i2c->msg->len - i2c->processed;
+
+	if (len > 32)
+		goto out;
+
+	i2c->state = STATE_STOP;
+
+	con1 |= REG_CON1_TRANSFER_AUTO_STOP | REG_CON1_AUTO_STOP;
+	i2c_writel(i2c, con1, REG_CON1);
+	if (con1 & REG_CON1_NACK_AUTO_STOP)
+		i2c_writel(i2c, REG_INT_STOP, REG_IEN);
+	else
+		i2c_writel(i2c, REG_INT_STOP | REG_INT_NAKRCV, REG_IEN);
+
+	return true;
+
+out:
+	i2c_writel(i2c, con1, REG_CON1);
+	return false;
+}
+
 /**
  * Generate a START condition, which triggers a REG_INT_START interrupt.
  */
 static void rk3x_i2c_start(struct rk3x_i2c *i2c)
 {
 	u32 val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK;
+	bool auto_stop = rk3x_i2c_auto_stop(i2c);
+	int length = 0;
 
-	i2c_writel(i2c, REG_INT_START, REG_IEN);
+	/* enable appropriate interrupts */
+	if (i2c->mode == REG_CON_MOD_TX) {
+		if (!auto_stop) {
+			i2c_writel(i2c, REG_INT_MBTF | REG_INT_NAKRCV, REG_IEN);
+			i2c->state = STATE_WRITE;
+		}
+		length = rk3x_i2c_fill_transmit_buf(i2c, false);
+	} else {
+		/* in any other case, we are going to be reading. */
+		if (!auto_stop) {
+			i2c_writel(i2c, REG_INT_MBRF | REG_INT_NAKRCV, REG_IEN);
+			i2c->state = STATE_READ;
+		}
+	}
 
 	/* enable adapter with correct mode, send START condition */
 	val |= REG_CON_EN | REG_CON_MOD(i2c->mode) | REG_CON_START;
@@ -255,6 +350,12 @@ static void rk3x_i2c_start(struct rk3x_i2c *i2c)
 		val |= REG_CON_ACTACK;
 
 	i2c_writel(i2c, val, REG_CON);
+
+	/* enable transition */
+	if (i2c->mode == REG_CON_MOD_TX)
+		i2c_writel(i2c, length, REG_MTXCNT);
+	else
+		rk3x_i2c_prepare_read(i2c);
 }
 
 /**
@@ -278,6 +379,7 @@ static void rk3x_i2c_stop(struct rk3x_i2c *i2c, int error)
 
 		ctrl = i2c_readl(i2c, REG_CON);
 		ctrl |= REG_CON_STOP;
+		ctrl &= ~REG_CON_START;
 		i2c_writel(i2c, ctrl, REG_CON);
 	} else {
 		/* Signal rk3x_i2c_xfer to start the next message. */
@@ -293,7 +395,7 @@ static void rk3x_i2c_stop(struct rk3x_i2c *i2c, int error)
 		i2c_writel(i2c, ctrl, REG_CON);
 
 		/* signal that we are finished with the current msg */
-		wake_up(&i2c->wait);
+		rk3x_i2c_wake_up(i2c);
 	}
 }
 
@@ -322,6 +424,8 @@ static void rk3x_i2c_prepare_read(struct rk3x_i2c *i2c)
 	if (i2c->processed != 0) {
 		con &= ~REG_CON_MOD_MASK;
 		con |= REG_CON_MOD(REG_CON_MOD_RX);
+		if (con & REG_CON_START)
+			con &= ~REG_CON_START;
 	}
 
 	i2c_writel(i2c, con, REG_CON);
@@ -331,7 +435,7 @@ static void rk3x_i2c_prepare_read(struct rk3x_i2c *i2c)
 /**
  * Fill the transmit buffer with data from i2c->msg
  */
-static void rk3x_i2c_fill_transmit_buf(struct rk3x_i2c *i2c)
+static int rk3x_i2c_fill_transmit_buf(struct rk3x_i2c *i2c, bool sendend)
 {
 	unsigned int i, j;
 	u32 cnt = 0;
@@ -359,40 +463,15 @@ static void rk3x_i2c_fill_transmit_buf(struct rk3x_i2c *i2c)
 			break;
 	}
 
-	i2c_writel(i2c, cnt, REG_MTXCNT);
+	if (sendend)
+		i2c_writel(i2c, cnt, REG_MTXCNT);
+
+	return cnt;
 }
 
 
 /* IRQ handlers for individual states */
 
-static void rk3x_i2c_handle_start(struct rk3x_i2c *i2c, unsigned int ipd)
-{
-	if (!(ipd & REG_INT_START)) {
-		rk3x_i2c_stop(i2c, -EIO);
-		dev_warn(i2c->dev, "unexpected irq in START: 0x%x\n", ipd);
-		rk3x_i2c_clean_ipd(i2c);
-		return;
-	}
-
-	/* ack interrupt */
-	i2c_writel(i2c, REG_INT_START, REG_IPD);
-
-	/* disable start bit */
-	i2c_writel(i2c, i2c_readl(i2c, REG_CON) & ~REG_CON_START, REG_CON);
-
-	/* enable appropriate interrupts and transition */
-	if (i2c->mode == REG_CON_MOD_TX) {
-		i2c_writel(i2c, REG_INT_MBTF | REG_INT_NAKRCV, REG_IEN);
-		i2c->state = STATE_WRITE;
-		rk3x_i2c_fill_transmit_buf(i2c);
-	} else {
-		/* in any other case, we are going to be reading. */
-		i2c_writel(i2c, REG_INT_MBRF | REG_INT_NAKRCV, REG_IEN);
-		i2c->state = STATE_READ;
-		rk3x_i2c_prepare_read(i2c);
-	}
-}
-
 static void rk3x_i2c_handle_write(struct rk3x_i2c *i2c, unsigned int ipd)
 {
 	if (!(ipd & REG_INT_MBTF)) {
@@ -404,28 +483,22 @@ static void rk3x_i2c_handle_write(struct rk3x_i2c *i2c, unsigned int ipd)
 
 	/* ack interrupt */
 	i2c_writel(i2c, REG_INT_MBTF, REG_IPD);
+	rk3x_i2c_auto_stop(i2c);
 
 	/* are we finished? */
 	if (i2c->processed == i2c->msg->len)
 		rk3x_i2c_stop(i2c, i2c->error);
 	else
-		rk3x_i2c_fill_transmit_buf(i2c);
+		rk3x_i2c_fill_transmit_buf(i2c, true);
 }
 
-static void rk3x_i2c_handle_read(struct rk3x_i2c *i2c, unsigned int ipd)
+static void rk3x_i2c_read(struct rk3x_i2c *i2c)
 {
 	unsigned int i;
 	unsigned int len = i2c->msg->len - i2c->processed;
 	u32 val;
 	u8 byte;
 
-	/* we only care for MBRF here. */
-	if (!(ipd & REG_INT_MBRF))
-		return;
-
-	/* ack interrupt (read also produces a spurious START flag, clear it too) */
-	i2c_writel(i2c, REG_INT_MBRF | REG_INT_START, REG_IPD);
-
 	/* Can only handle a maximum of 32 bytes at a time */
 	if (len > 32)
 		len = 32;
@@ -438,7 +511,21 @@ static void rk3x_i2c_handle_read(struct rk3x_i2c *i2c, unsigned int ipd)
 		byte = (val >> ((i % 4) * 8)) & 0xff;
 		i2c->msg->buf[i2c->processed++] = byte;
 	}
+}
+
+static void rk3x_i2c_handle_read(struct rk3x_i2c *i2c, unsigned int ipd)
+{
+	/* we only care for MBRF here. */
+	if (!(ipd & REG_INT_MBRF))
+		return;
 
+	/* ack interrupt */
+	i2c_writel(i2c, REG_INT_MBRF, REG_IPD);
+
+	/* read the data from receive buffer */
+	rk3x_i2c_read(i2c);
+
+	rk3x_i2c_auto_stop(i2c);
 	/* are we finished? */
 	if (i2c->processed == i2c->msg->len)
 		rk3x_i2c_stop(i2c, i2c->error);
@@ -457,19 +544,31 @@ static void rk3x_i2c_handle_stop(struct rk3x_i2c *i2c, unsigned int ipd)
 		return;
 	}
 
+	if (i2c->autostop_supported && !i2c->error) {
+		if (i2c->mode != REG_CON_MOD_TX && i2c->msg) {
+			if ((i2c->msg->len - i2c->processed) > 0)
+				rk3x_i2c_read(i2c);
+		}
+
+		i2c->processed = 0;
+		i2c->msg = NULL;
+	}
+
 	/* ack interrupt */
 	i2c_writel(i2c, REG_INT_STOP, REG_IPD);
 
 	/* disable STOP bit */
 	con = i2c_readl(i2c, REG_CON);
 	con &= ~REG_CON_STOP;
+	if (i2c->autostop_supported)
+		con &= ~REG_CON_START;
 	i2c_writel(i2c, con, REG_CON);
 
 	i2c->busy = false;
 	i2c->state = STATE_IDLE;
 
 	/* signal rk3x_i2c_xfer that we are finished */
-	wake_up(&i2c->wait);
+	rk3x_i2c_wake_up(i2c);
 }
 
 static irqreturn_t rk3x_i2c_irq(int irqno, void *dev_id)
@@ -481,7 +580,9 @@ static irqreturn_t rk3x_i2c_irq(int irqno, void *dev_id)
 
 	ipd = i2c_readl(i2c, REG_IPD);
 	if (i2c->state == STATE_IDLE) {
-		dev_warn(i2c->dev, "irq in STATE_IDLE, ipd = 0x%x\n", ipd);
+		dev_warn_ratelimited(i2c->dev,
+				     "irq in STATE_IDLE, ipd = 0x%x\n",
+				     ipd);
 		rk3x_i2c_clean_ipd(i2c);
 		goto out;
 	}
@@ -501,8 +602,15 @@ static irqreturn_t rk3x_i2c_irq(int irqno, void *dev_id)
 
 		ipd &= ~REG_INT_NAKRCV;
 
-		if (!(i2c->msg->flags & I2C_M_IGNORE_NAK))
-			rk3x_i2c_stop(i2c, -ENXIO);
+		if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
+			if (i2c->autostop_supported) {
+				i2c->error = -ENXIO;
+				i2c->state = STATE_STOP;
+			} else {
+				rk3x_i2c_stop(i2c, -ENXIO);
+				goto out;
+			}
+		}
 	}
 
 	/* is there anything left to handle? */
@@ -510,9 +618,6 @@ static irqreturn_t rk3x_i2c_irq(int irqno, void *dev_id)
 		goto out;
 
 	switch (i2c->state) {
-	case STATE_START:
-		rk3x_i2c_handle_start(i2c, ipd);
-		break;
 	case STATE_WRITE:
 		rk3x_i2c_handle_write(i2c, ipd);
 		break;
@@ -1032,11 +1137,12 @@ static int rk3x_i2c_setup(struct rk3x_i2c *i2c, struct i2c_msg *msgs, int num)
 
 	i2c->addr = msgs[0].addr;
 	i2c->busy = true;
-	i2c->state = STATE_START;
 	i2c->processed = 0;
 	i2c->error = 0;
 
 	rk3x_i2c_clean_ipd(i2c);
+	if (i2c->autostop_supported)
+		i2c_writel(i2c, 0, REG_CON1);
 
 	return ret;
 }
@@ -1063,6 +1169,9 @@ static int rk3x_i2c_xfer_common(struct i2c_adapter *adap,
 	int ret = 0;
 	int i;
 
+	if (i2c->suspended)
+		return -EACCES;
+
 	spin_lock_irqsave(&i2c->lock, flags);
 
 	clk_enable(i2c->clk);
@@ -1085,10 +1194,10 @@ static int rk3x_i2c_xfer_common(struct i2c_adapter *adap,
 		if (i + ret >= num)
 			i2c->is_last_msg = true;
 
-		spin_unlock_irqrestore(&i2c->lock, flags);
-
 		rk3x_i2c_start(i2c);
 
+		spin_unlock_irqrestore(&i2c->lock, flags);
+
 		if (!polling) {
 			timeout = wait_event_timeout(i2c->wait, !i2c->busy,
 						     msecs_to_jiffies(WAIT_TIMEOUT));
@@ -1103,7 +1212,7 @@ static int rk3x_i2c_xfer_common(struct i2c_adapter *adap,
 				i2c_readl(i2c, REG_IPD), i2c->state);
 
 			/* Force a STOP condition without interrupt */
-			i2c_writel(i2c, 0, REG_IEN);
+			rk3x_i2c_disable_irq(i2c);
 			val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK;
 			val |= REG_CON_EN | REG_CON_STOP;
 			i2c_writel(i2c, val, REG_CON);
@@ -1120,6 +1229,9 @@ static int rk3x_i2c_xfer_common(struct i2c_adapter *adap,
 		}
 	}
 
+	rk3x_i2c_disable_irq(i2c);
+	rk3x_i2c_disable(i2c);
+
 	clk_disable(i2c->pclk);
 	clk_disable(i2c->clk);
 
@@ -1140,12 +1252,81 @@ static int rk3x_i2c_xfer_polling(struct i2c_adapter *adap,
 	return rk3x_i2c_xfer_common(adap, msgs, num, true);
 }
 
-static __maybe_unused int rk3x_i2c_resume(struct device *dev)
+static int rk3x_i2c_restart_notify(struct notifier_block *this,
+				   unsigned long mode, void *cmd)
+{
+	struct rk3x_i2c *i2c = container_of(this, struct rk3x_i2c,
+					    i2c_restart_nb);
+	int tmo = WAIT_TIMEOUT * USEC_PER_MSEC;
+	u32 val;
+
+	if (i2c->state != STATE_IDLE) {
+		i2c->system_restarting = true;
+		/* complete the unfinished job */
+		while (tmo-- && i2c->busy) {
+			udelay(1);
+			rk3x_i2c_irq(0, i2c);
+		}
+	}
+
+	if (tmo <= 0) {
+		dev_err(i2c->dev, "restart timeout, ipd: 0x%02x, state: %d\n",
+			i2c_readl(i2c, REG_IPD), i2c->state);
+
+		/* Force a STOP condition without interrupt */
+		i2c_writel(i2c, 0, REG_IEN);
+		val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK;
+		val |= REG_CON_EN | REG_CON_STOP;
+		i2c_writel(i2c, val, REG_CON);
+
+		udelay(10);
+		i2c->state = STATE_IDLE;
+	}
+
+	return NOTIFY_DONE;
+}
+
+static unsigned int rk3x_i2c_get_version(struct rk3x_i2c *i2c)
+{
+	unsigned int version;
+
+	clk_enable(i2c->pclk);
+	version = i2c_readl(i2c, REG_CON) & REG_CON_VERSION;
+	clk_disable(i2c->pclk);
+	version >>= REG_CON_VERSION_SHIFT;
+
+	return version;
+}
+
+static __maybe_unused int rk3x_i2c_suspend_noirq(struct device *dev)
+{
+	struct rk3x_i2c *i2c = dev_get_drvdata(dev);
+
+	/*
+	 * Below code is needed only to ensure that there are no
+	 * activities on I2C bus. if at this moment any driver
+	 * is trying to use I2C bus - this may cause i2c timeout.
+	 *
+	 * So forbid access to I2C device using i2c->suspended flag.
+	 */
+	i2c_lock_bus(&i2c->adap, I2C_LOCK_ROOT_ADAPTER);
+	i2c->suspended = 1;
+	i2c_unlock_bus(&i2c->adap, I2C_LOCK_ROOT_ADAPTER);
+
+	return 0;
+}
+
+static __maybe_unused int rk3x_i2c_resume_noirq(struct device *dev)
 {
 	struct rk3x_i2c *i2c = dev_get_drvdata(dev);
 
 	rk3x_i2c_adapt_div(i2c, clk_get_rate(i2c->clk));
 
+	/* Allow access to I2C bus */
+	i2c_lock_bus(&i2c->adap, I2C_LOCK_ROOT_ADAPTER);
+	i2c->suspended = 0;
+	i2c_unlock_bus(&i2c->adap, I2C_LOCK_ROOT_ADAPTER);
+
 	return 0;
 }
 
@@ -1161,7 +1342,12 @@ static const struct i2c_algorithm rk3x_i2c_algorithm = {
 };
 
 static const struct rk3x_i2c_soc_data rv1108_soc_data = {
-	.grf_offset = -1,
+	.grf_offset = 0x408,
+	.calc_timings = rk3x_i2c_v1_calc_timings,
+};
+
+static const struct rk3x_i2c_soc_data rv1126_soc_data = {
+	.grf_offset = 0x118,
 	.calc_timings = rk3x_i2c_v1_calc_timings,
 };
 
@@ -1195,6 +1381,10 @@ static const struct of_device_id rk3x_i2c_match[] = {
 		.compatible = "rockchip,rv1108-i2c",
 		.data = &rv1108_soc_data
 	},
+	{
+		.compatible = "rockchip,rv1126-i2c",
+		.data = &rv1126_soc_data
+	},
 	{
 		.compatible = "rockchip,rk3066-i2c",
 		.data = &rk3066_soc_data
@@ -1225,7 +1415,6 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
 	const struct of_device_id *match;
 	struct rk3x_i2c *i2c;
 	int ret = 0;
-	int bus_nr;
 	u32 value;
 	int irq;
 	unsigned long clk_rate;
@@ -1253,13 +1442,18 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
 	spin_lock_init(&i2c->lock);
 	init_waitqueue_head(&i2c->wait);
 
+	i2c->i2c_restart_nb.notifier_call = rk3x_i2c_restart_notify;
+	i2c->i2c_restart_nb.priority = 128;
+	ret = register_pre_restart_handler(&i2c->i2c_restart_nb);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to setup i2c restart handler.\n");
+		return ret;
+	}
+
 	i2c->regs = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(i2c->regs))
 		return PTR_ERR(i2c->regs);
 
-	/* Try to set the I2C adapter number from dt */
-	bus_nr = of_alias_get_id(np, "i2c");
-
 	/*
 	 * Switch to new interface if the SoC also offers the old one.
 	 * The control bit is located in the GRF register space.
@@ -1268,24 +1462,34 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
 		struct regmap *grf;
 
 		grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
-		if (IS_ERR(grf)) {
-			dev_err(&pdev->dev,
-				"rk3x-i2c needs 'rockchip,grf' property\n");
-			return PTR_ERR(grf);
-		}
-
-		if (bus_nr < 0) {
-			dev_err(&pdev->dev, "rk3x-i2c needs i2cX alias");
-			return -EINVAL;
-		}
-
-		/* 27+i: write mask, 11+i: value */
-		value = BIT(27 + bus_nr) | BIT(11 + bus_nr);
-
-		ret = regmap_write(grf, i2c->soc_data->grf_offset, value);
-		if (ret != 0) {
-			dev_err(i2c->dev, "Could not write to GRF: %d\n", ret);
-			return ret;
+		if (!IS_ERR(grf)) {
+			int bus_nr;
+
+			/* Try to set the I2C adapter number from dt */
+			bus_nr = of_alias_get_id(np, "i2c");
+			if (bus_nr < 0) {
+				dev_err(&pdev->dev, "rk3x-i2c needs i2cX alias");
+				return -EINVAL;
+			}
+
+			if (i2c->soc_data == &rv1108_soc_data && bus_nr == 2)
+				/* rv1108 i2c2 set grf offset-0x408, bit-10 */
+				value = BIT(26) | BIT(10);
+			else if (i2c->soc_data == &rv1126_soc_data &&
+				 bus_nr == 2)
+				/* rv1126 i2c2 set pmugrf offset-0x118, bit-4 */
+				value = BIT(20) | BIT(4);
+			else
+				/* rk3xxx 27+i: write mask, 11+i: value */
+				value = BIT(27 + bus_nr) | BIT(11 + bus_nr);
+
+			ret = regmap_write(grf, i2c->soc_data->grf_offset,
+					   value);
+			if (ret != 0) {
+				dev_err(i2c->dev, "Could not write to GRF: %d\n",
+					ret);
+				return ret;
+			}
 		}
 	}
 
@@ -1341,6 +1545,9 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
 	clk_rate = clk_get_rate(i2c->clk);
 	rk3x_i2c_adapt_div(i2c, clk_rate);
 
+	if (rk3x_i2c_get_version(i2c) >= RK_I2C_VERSION5)
+		i2c->autostop_supported = true;
+
 	ret = i2c_add_adapter(&i2c->adap);
 	if (ret < 0)
 		goto err_clk_notifier;
@@ -1363,13 +1570,17 @@ static int rk3x_i2c_remove(struct platform_device *pdev)
 	i2c_del_adapter(&i2c->adap);
 
 	clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb);
+	unregister_pre_restart_handler(&i2c->i2c_restart_nb);
 	clk_unprepare(i2c->pclk);
 	clk_unprepare(i2c->clk);
 
 	return 0;
 }
 
-static SIMPLE_DEV_PM_OPS(rk3x_i2c_pm_ops, NULL, rk3x_i2c_resume);
+static const struct dev_pm_ops rk3x_i2c_pm_ops = {
+	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rk3x_i2c_suspend_noirq,
+				      rk3x_i2c_resume_noirq)
+};
 
 static struct platform_driver rk3x_i2c_driver = {
 	.probe   = rk3x_i2c_probe,
@@ -1381,7 +1592,21 @@ static struct platform_driver rk3x_i2c_driver = {
 	},
 };
 
+#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT
+static int __init rk3x_i2c_driver_init(void)
+{
+	return platform_driver_register(&rk3x_i2c_driver);
+}
+subsys_initcall_sync(rk3x_i2c_driver_init);
+
+static void __exit rk3x_i2c_driver_exit(void)
+{
+	platform_driver_unregister(&rk3x_i2c_driver);
+}
+module_exit(rk3x_i2c_driver_exit);
+#else
 module_platform_driver(rk3x_i2c_driver);
+#endif
 
 MODULE_DESCRIPTION("Rockchip RK3xxx I2C Bus driver");
 MODULE_AUTHOR("Max Schwarz <max.schwarz@online.de>");
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index 34fecf97a..d041b7fa5 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -61,6 +61,7 @@
 static DEFINE_MUTEX(core_lock);
 static DEFINE_IDR(i2c_adapter_idr);
 
+static int i2c_check_addr_ex(struct i2c_adapter *adapter, int addr);
 static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver);
 
 static DEFINE_STATIC_KEY_FALSE(i2c_trace_msg_key);
@@ -808,7 +809,8 @@ static void i2c_adapter_unlock_bus(struct i2c_adapter *adapter,
 
 static void i2c_dev_set_name(struct i2c_adapter *adap,
 			     struct i2c_client *client,
-			     struct i2c_board_info const *info)
+			     struct i2c_board_info const *info,
+			     int status)
 {
 	struct acpi_device *adev = ACPI_COMPANION(&client->dev);
 
@@ -822,8 +824,12 @@ static void i2c_dev_set_name(struct i2c_adapter *adap,
 		return;
 	}
 
-	dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
-		     i2c_encode_flags_to_addr(client));
+	if (status == 0)
+		dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
+			i2c_encode_flags_to_addr(client));
+	else
+		dev_set_name(&client->dev, "%d-%04x-%01x", i2c_adapter_id(adap),
+			i2c_encode_flags_to_addr(client), status);
 }
 
 int i2c_dev_irq_from_resources(const struct resource *resources,
@@ -899,9 +905,11 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf
 	}
 
 	/* Check for address business */
-	status = i2c_check_addr_busy(adap, i2c_encode_flags_to_addr(client));
+	status = i2c_check_addr_ex(adap, i2c_encode_flags_to_addr(client));
 	if (status)
-		goto out_err;
+		dev_err(&adap->dev,
+			"%d i2c clients have been registered at 0x%02x",
+			status, client->addr);
 
 	client->dev.parent = &client->adapter->dev;
 	client->dev.bus = &i2c_bus_type;
@@ -909,7 +917,7 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf
 	client->dev.of_node = of_node_get(info->of_node);
 	client->dev.fwnode = info->fwnode;
 
-	i2c_dev_set_name(adap, client, info);
+	i2c_dev_set_name(adap, client, info, status);
 
 	if (info->properties) {
 		status = device_add_properties(&client->dev, info->properties);
@@ -935,10 +943,6 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf
 		device_remove_properties(&client->dev);
 out_err_put_of_node:
 	of_node_put(info->of_node);
-out_err:
-	dev_err(&adap->dev,
-		"Failed to register i2c client %s at 0x%02x (%d)\n",
-		client->name, client->addr, status);
 out_err_silent:
 	kfree(client);
 	return ERR_PTR(status);
@@ -1838,6 +1842,33 @@ EXPORT_SYMBOL(i2c_del_driver);
 
 /* ------------------------------------------------------------------------- */
 
+struct i2c_addr_cnt {
+	int addr;
+	int cnt;
+};
+
+static int __i2c_check_addr_ex(struct device *dev, void *addrp)
+{
+	struct i2c_client *client = i2c_verify_client(dev);
+	struct i2c_addr_cnt *addrinfo = (struct i2c_addr_cnt *)addrp;
+	int addr = addrinfo->addr;
+
+	if (client && client->addr == addr)
+		addrinfo->cnt++;
+
+	return 0;
+}
+
+static int i2c_check_addr_ex(struct i2c_adapter *adapter, int addr)
+{
+	struct i2c_addr_cnt addrinfo;
+
+	addrinfo.addr = addr;
+	addrinfo.cnt = 0;
+	device_for_each_child(&adapter->dev, &addrinfo, __i2c_check_addr_ex);
+	return addrinfo.cnt;
+}
+
 struct i2c_cmd_arg {
 	unsigned	cmd;
 	void		*arg;
diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
index 12584f163..03cc336bf 100644
--- a/drivers/iio/adc/rockchip_saradc.c
+++ b/drivers/iio/adc/rockchip_saradc.c
@@ -35,12 +35,31 @@
 #define SARADC_DLY_PU_SOC_MASK		0x3f
 
 #define SARADC_TIMEOUT			msecs_to_jiffies(100)
-#define SARADC_MAX_CHANNELS		6
+#define SARADC_MAX_CHANNELS		8
+
+/* v2 registers */
+#define SARADC2_CONV_CON		0x0
+#define SARADC_T_PD_SOC			0x4
+#define SARADC_T_DAS_SOC		0xc
+#define SARADC2_END_INT_EN		0x104
+#define SARADC2_ST_CON			0x108
+#define SARADC2_STATUS			0x10c
+#define SARADC2_END_INT_ST		0x110
+#define SARADC2_DATA_BASE		0x120
+
+#define SARADC2_EN_END_INT		BIT(0)
+#define SARADC2_START			BIT(4)
+#define SARADC2_SINGLE_MODE		BIT(5)
+
+struct rockchip_saradc;
 
 struct rockchip_saradc_data {
 	const struct iio_chan_spec	*channels;
 	int				num_channels;
 	unsigned long			clk_rate;
+	void (*start)(struct rockchip_saradc *info, int chn);
+	int (*read)(struct rockchip_saradc *info);
+	void (*power_down)(struct rockchip_saradc *info);
 };
 
 struct rockchip_saradc {
@@ -49,33 +68,99 @@ struct rockchip_saradc {
 	struct clk		*clk;
 	struct completion	completion;
 	struct regulator	*vref;
+	int			uv_vref;
 	struct reset_control	*reset;
 	const struct rockchip_saradc_data *data;
 	u16			last_val;
 	const struct iio_chan_spec *last_chan;
+	bool			suspended;
+#ifdef CONFIG_ROCKCHIP_SARADC_TEST_CHN
+	bool			test;
+	u32			chn;
+	spinlock_t		lock;
+	struct workqueue_struct *wq;
+	struct delayed_work	work;
+#endif
 };
 
-static void rockchip_saradc_power_down(struct rockchip_saradc *info)
+static void rockchip_saradc_reset_controller(struct reset_control *reset);
+
+static void rockchip_saradc_start_v1(struct rockchip_saradc *info,
+					int chn)
+{
+	/* 8 clock periods as delay between power up and start cmd */
+	writel_relaxed(8, info->regs + SARADC_DLY_PU_SOC);
+	/* Select the channel to be used and trigger conversion */
+	writel(SARADC_CTRL_POWER_CTRL | (chn & SARADC_CTRL_CHN_MASK) |
+	       SARADC_CTRL_IRQ_ENABLE, info->regs + SARADC_CTRL);
+}
+
+static void rockchip_saradc_start_v2(struct rockchip_saradc *info,
+					int chn)
+{
+	int val;
+
+	/* If read other chn at anytime, then chn1 will error, assert
+	 * controller as a workaround.
+	 */
+	if (info->reset)
+		rockchip_saradc_reset_controller(info->reset);
+
+	writel_relaxed(0xc, info->regs + SARADC_T_DAS_SOC);
+	writel_relaxed(0x20, info->regs + SARADC_T_PD_SOC);
+	val = SARADC2_EN_END_INT << 16 | SARADC2_EN_END_INT;
+	writel_relaxed(val, info->regs + SARADC2_END_INT_EN);
+	val = SARADC2_START | SARADC2_SINGLE_MODE | chn;
+	writel(val << 16 | val, info->regs + SARADC2_CONV_CON);
+}
+
+static void rockchip_saradc_start(struct rockchip_saradc *info,
+					int chn)
+{
+	info->data->start(info, chn);
+}
+
+static int rockchip_saradc_read_v1(struct rockchip_saradc *info)
+{
+	return readl_relaxed(info->regs + SARADC_DATA);
+}
+
+static int rockchip_saradc_read_v2(struct rockchip_saradc *info)
+{
+	int offset;
+
+	/* Clear irq */
+	writel_relaxed(0x1, info->regs + SARADC2_END_INT_ST);
+
+	offset = SARADC2_DATA_BASE + info->last_chan->channel * 0x4;
+
+	return readl_relaxed(info->regs + offset);
+}
+
+static int rockchip_saradc_read(struct rockchip_saradc *info)
+{
+	return info->data->read(info);
+}
+
+static void rockchip_saradc_power_down_v1(struct rockchip_saradc *info)
 {
-	/* Clear irq & power down adc */
 	writel_relaxed(0, info->regs + SARADC_CTRL);
 }
 
+static void rockchip_saradc_power_down(struct rockchip_saradc *info)
+{
+	if (info->data->power_down)
+		info->data->power_down(info);
+}
+
 static int rockchip_saradc_conversion(struct rockchip_saradc *info,
 				   struct iio_chan_spec const *chan)
 {
 	reinit_completion(&info->completion);
 
-	/* 8 clock periods as delay between power up and start cmd */
-	writel_relaxed(8, info->regs + SARADC_DLY_PU_SOC);
-
+	/* prevent isr get NULL last_chan */
 	info->last_chan = chan;
-
-	/* Select the channel to be used and trigger conversion */
-	writel(SARADC_CTRL_POWER_CTRL
-			| (chan->channel & SARADC_CTRL_CHN_MASK)
-			| SARADC_CTRL_IRQ_ENABLE,
-		   info->regs + SARADC_CTRL);
+	rockchip_saradc_start(info, chan->channel);
 
 	if (!wait_for_completion_timeout(&info->completion, SARADC_TIMEOUT))
 		return -ETIMEDOUT;
@@ -90,10 +175,19 @@ static int rockchip_saradc_read_raw(struct iio_dev *indio_dev,
 	struct rockchip_saradc *info = iio_priv(indio_dev);
 	int ret;
 
+#ifdef CONFIG_ROCKCHIP_SARADC_TEST_CHN
+	if (info->test)
+		return 0;
+#endif
 	switch (mask) {
 	case IIO_CHAN_INFO_RAW:
 		mutex_lock(&indio_dev->mlock);
 
+		if (info->suspended) {
+			mutex_unlock(&indio_dev->mlock);
+			return -EBUSY;
+		}
+
 		ret = rockchip_saradc_conversion(info, chan);
 		if (ret) {
 			rockchip_saradc_power_down(info);
@@ -105,13 +199,11 @@ static int rockchip_saradc_read_raw(struct iio_dev *indio_dev,
 		mutex_unlock(&indio_dev->mlock);
 		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_SCALE:
-		ret = regulator_get_voltage(info->vref);
-		if (ret < 0) {
-			dev_err(&indio_dev->dev, "failed to get voltage\n");
-			return ret;
-		}
+		/* It is a dummy regulator */
+		if (info->uv_vref < 0)
+			return info->uv_vref;
 
-		*val = ret / 1000;
+		*val = info->uv_vref / 1000;
 		*val2 = chan->scan_type.realbits;
 		return IIO_VAL_FRACTIONAL_LOG2;
 	default:
@@ -122,15 +214,25 @@ static int rockchip_saradc_read_raw(struct iio_dev *indio_dev,
 static irqreturn_t rockchip_saradc_isr(int irq, void *dev_id)
 {
 	struct rockchip_saradc *info = dev_id;
+#ifdef CONFIG_ROCKCHIP_SARADC_TEST_CHN
+	unsigned long flags;
+#endif
 
 	/* Read value */
-	info->last_val = readl_relaxed(info->regs + SARADC_DATA);
+	info->last_val = rockchip_saradc_read(info);
 	info->last_val &= GENMASK(info->last_chan->scan_type.realbits - 1, 0);
 
 	rockchip_saradc_power_down(info);
 
 	complete(&info->completion);
-
+#ifdef CONFIG_ROCKCHIP_SARADC_TEST_CHN
+	spin_lock_irqsave(&info->lock, flags);
+	if (info->test) {
+		pr_info("chn[%d] val = %d\n", info->chn, info->last_val);
+		mod_delayed_work(info->wq, &info->work, msecs_to_jiffies(100));
+	}
+	spin_unlock_irqrestore(&info->lock, flags);
+#endif
 	return IRQ_HANDLED;
 }
 
@@ -164,6 +266,9 @@ static const struct rockchip_saradc_data saradc_data = {
 	.channels = rockchip_saradc_iio_channels,
 	.num_channels = ARRAY_SIZE(rockchip_saradc_iio_channels),
 	.clk_rate = 1000000,
+	.start = rockchip_saradc_start_v1,
+	.read = rockchip_saradc_read_v1,
+	.power_down = rockchip_saradc_power_down_v1,
 };
 
 static const struct iio_chan_spec rockchip_rk3066_tsadc_iio_channels[] = {
@@ -175,6 +280,9 @@ static const struct rockchip_saradc_data rk3066_tsadc_data = {
 	.channels = rockchip_rk3066_tsadc_iio_channels,
 	.num_channels = ARRAY_SIZE(rockchip_rk3066_tsadc_iio_channels),
 	.clk_rate = 50000,
+	.start = rockchip_saradc_start_v1,
+	.read = rockchip_saradc_read_v1,
+	.power_down = rockchip_saradc_power_down_v1,
 };
 
 static const struct iio_chan_spec rockchip_rk3399_saradc_iio_channels[] = {
@@ -190,6 +298,48 @@ static const struct rockchip_saradc_data rk3399_saradc_data = {
 	.channels = rockchip_rk3399_saradc_iio_channels,
 	.num_channels = ARRAY_SIZE(rockchip_rk3399_saradc_iio_channels),
 	.clk_rate = 1000000,
+	.start = rockchip_saradc_start_v1,
+	.read = rockchip_saradc_read_v1,
+	.power_down = rockchip_saradc_power_down_v1,
+};
+
+static const struct iio_chan_spec rockchip_rk3568_saradc_iio_channels[] = {
+	SARADC_CHANNEL(0, "adc0", 10),
+	SARADC_CHANNEL(1, "adc1", 10),
+	SARADC_CHANNEL(2, "adc2", 10),
+	SARADC_CHANNEL(3, "adc3", 10),
+	SARADC_CHANNEL(4, "adc4", 10),
+	SARADC_CHANNEL(5, "adc5", 10),
+	SARADC_CHANNEL(6, "adc6", 10),
+	SARADC_CHANNEL(7, "adc7", 10),
+};
+
+static const struct rockchip_saradc_data rk3568_saradc_data = {
+	.channels = rockchip_rk3568_saradc_iio_channels,
+	.num_channels = ARRAY_SIZE(rockchip_rk3568_saradc_iio_channels),
+	.clk_rate = 1000000,
+	.start = rockchip_saradc_start_v1,
+	.read = rockchip_saradc_read_v1,
+	.power_down = rockchip_saradc_power_down_v1,
+};
+
+static const struct iio_chan_spec rockchip_rk3588_saradc_iio_channels[] = {
+	SARADC_CHANNEL(0, "adc0", 12),
+	SARADC_CHANNEL(1, "adc1", 12),
+	SARADC_CHANNEL(2, "adc2", 12),
+	SARADC_CHANNEL(3, "adc3", 12),
+	SARADC_CHANNEL(4, "adc4", 12),
+	SARADC_CHANNEL(5, "adc5", 12),
+	SARADC_CHANNEL(6, "adc6", 12),
+	SARADC_CHANNEL(7, "adc7", 12),
+};
+
+static const struct rockchip_saradc_data rk3588_saradc_data = {
+	.channels = rockchip_rk3588_saradc_iio_channels,
+	.num_channels = ARRAY_SIZE(rockchip_rk3588_saradc_iio_channels),
+	.clk_rate = 1000000,
+	.start = rockchip_saradc_start_v2,
+	.read = rockchip_saradc_read_v2,
 };
 
 static const struct of_device_id rockchip_saradc_match[] = {
@@ -202,6 +352,12 @@ static const struct of_device_id rockchip_saradc_match[] = {
 	}, {
 		.compatible = "rockchip,rk3399-saradc",
 		.data = &rk3399_saradc_data,
+	}, {
+		.compatible = "rockchip,rk3568-saradc",
+		.data = &rk3568_saradc_data,
+	}, {
+		.compatible = "rockchip,rk3588-saradc",
+		.data = &rk3588_saradc_data,
 	},
 	{},
 };
@@ -278,6 +434,75 @@ static irqreturn_t rockchip_saradc_trigger_handler(int irq, void *p)
 	return IRQ_HANDLED;
 }
 
+#ifdef CONFIG_ROCKCHIP_SARADC_TEST_CHN
+static ssize_t saradc_test_chn_store(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t size)
+{
+	u32 val = 0;
+	int err;
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct rockchip_saradc *info = iio_priv(indio_dev);
+	unsigned long flags;
+
+	err = kstrtou32(buf, 10, &val);
+	if (err)
+		return err;
+
+	spin_lock_irqsave(&info->lock, flags);
+
+	if (val > SARADC_CTRL_CHN_MASK && info->test) {
+		info->test = false;
+		spin_unlock_irqrestore(&info->lock, flags);
+		cancel_delayed_work_sync(&info->work);
+		return size;
+	}
+
+	if (!info->test && val < SARADC_CTRL_CHN_MASK) {
+		info->test = true;
+		info->chn = val;
+		mod_delayed_work(info->wq, &info->work, msecs_to_jiffies(100));
+	}
+
+	spin_unlock_irqrestore(&info->lock, flags);
+
+	return size;
+}
+
+static DEVICE_ATTR_WO(saradc_test_chn);
+
+static struct attribute *saradc_attrs[] = {
+	&dev_attr_saradc_test_chn.attr,
+	NULL
+};
+
+static const struct attribute_group rockchip_saradc_attr_group = {
+	.attrs = saradc_attrs,
+};
+
+static void rockchip_saradc_remove_sysgroup(void *data)
+{
+	struct platform_device *pdev = data;
+
+	sysfs_remove_group(&pdev->dev.kobj, &rockchip_saradc_attr_group);
+}
+
+static void rockchip_saradc_destroy_wq(void *data)
+{
+	struct rockchip_saradc *info = data;
+
+	destroy_workqueue(info->wq);
+}
+
+static void rockchip_saradc_test_work(struct work_struct *work)
+{
+	struct rockchip_saradc *info = container_of(work,
+					struct rockchip_saradc, work.work);
+
+	rockchip_saradc_start(info, info->chn);
+}
+#endif
+
 static int rockchip_saradc_probe(struct platform_device *pdev)
 {
 	struct rockchip_saradc *info = NULL;
@@ -390,6 +615,13 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	info->uv_vref = regulator_get_voltage(info->vref);
+	if (info->uv_vref < 0) {
+		dev_err(&pdev->dev, "failed to get voltage\n");
+		ret = info->uv_vref;
+		return ret;
+	}
+
 	ret = clk_prepare_enable(info->pclk);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to enable pclk\n");
@@ -430,6 +662,30 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
+#ifdef CONFIG_ROCKCHIP_SARADC_TEST_CHN
+	info->wq = create_singlethread_workqueue("adc_wq");
+	INIT_DELAYED_WORK(&info->work, rockchip_saradc_test_work);
+	spin_lock_init(&info->lock);
+	ret = sysfs_create_group(&pdev->dev.kobj, &rockchip_saradc_attr_group);
+	if (ret)
+		return ret;
+
+	ret = devm_add_action_or_reset(&pdev->dev,
+				       rockchip_saradc_remove_sysgroup, pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register devm action, %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = devm_add_action_or_reset(&pdev->dev,
+				       rockchip_saradc_destroy_wq, info);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register destroy_wq, %d\n",
+			ret);
+		return ret;
+	}
+#endif
 	return devm_iio_device_register(&pdev->dev, indio_dev);
 }
 
@@ -439,10 +695,16 @@ static int rockchip_saradc_suspend(struct device *dev)
 	struct iio_dev *indio_dev = dev_get_drvdata(dev);
 	struct rockchip_saradc *info = iio_priv(indio_dev);
 
+	/* Avoid reading saradc when suspending */
+	mutex_lock(&indio_dev->mlock);
+
 	clk_disable_unprepare(info->clk);
 	clk_disable_unprepare(info->pclk);
 	regulator_disable(info->vref);
 
+	info->suspended = true;
+	mutex_unlock(&indio_dev->mlock);
+
 	return 0;
 }
 
@@ -464,6 +726,8 @@ static int rockchip_saradc_resume(struct device *dev)
 	if (ret)
 		clk_disable_unprepare(info->pclk);
 
+	info->suspended = false;
+
 	return ret;
 }
 #endif
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index e35650930..175acb2f6 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_INPUT_MOUSE)	+= mouse/
 obj-$(CONFIG_INPUT_JOYSTICK)	+= joystick/
 obj-$(CONFIG_INPUT_TABLET)	+= tablet/
 obj-$(CONFIG_INPUT_TOUCHSCREEN)	+= touchscreen/
+obj-$(CONFIG_SENSOR_DEVICE)	+= sensors/
 obj-$(CONFIG_INPUT_MISC)	+= misc/
 
 obj-$(CONFIG_INPUT_APMPOWER)	+= apm-power.o
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 362e8a019..1f3e8132a 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -578,7 +578,7 @@ config INPUT_PWM_VIBRA
 
 config INPUT_RK805_PWRKEY
 	tristate "Rockchip RK805 PMIC power key support"
-	depends on MFD_RK808
+	depends on MFD_RK806 || MFD_RK808
 	help
 	  Select this option to enable power key driver for RK805.
 
diff --git a/drivers/input/misc/rk805-pwrkey.c b/drivers/input/misc/rk805-pwrkey.c
index 76873aa00..769821763 100644
--- a/drivers/input/misc/rk805-pwrkey.c
+++ b/drivers/input/misc/rk805-pwrkey.c
@@ -13,6 +13,7 @@
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 
 static irqreturn_t pwrkey_fall_irq(int irq, void *_pwr)
@@ -39,8 +40,15 @@ static int rk805_pwrkey_probe(struct platform_device *pdev)
 {
 	struct input_dev *pwr;
 	int fall_irq, rise_irq;
+	struct device_node *np;
 	int err;
 
+	np = of_get_child_by_name(pdev->dev.parent->of_node, "pwrkey");
+	if (np && !of_device_is_available(np)) {
+		dev_info(&pdev->dev, "device is disabled\n");
+		return -EINVAL;
+	}
+
 	pwr = devm_input_allocate_device(&pdev->dev);
 	if (!pwr) {
 		dev_err(&pdev->dev, "Can't allocate power button\n");
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 04878caf6..fe1256b01 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -160,7 +160,8 @@ config OMAP_IOMMU_DEBUG
 	  Say N unless you know you need this.
 
 config ROCKCHIP_IOMMU
-	bool "Rockchip IOMMU Support"
+	tristate "Rockchip IOMMU Support"
+	depends on ARM || ARM64
 	depends on ARCH_ROCKCHIP || COMPILE_TEST
 	select IOMMU_API
 	select ARM_DMA_USE_IOMMU
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index d1539b739..dc5d98b1d 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -372,6 +372,52 @@ static int iommu_dma_deferred_attach(struct device *dev,
 	return 0;
 }
 
+/*
+ * Should be called prior to using dma-apis
+ */
+int iommu_dma_reserve_iova(struct device *dev, dma_addr_t base,
+			   u64 size)
+{
+	struct iommu_domain *domain;
+	struct iommu_dma_cookie *cookie;
+	struct iova_domain *iovad;
+	unsigned long pfn_lo, pfn_hi;
+
+	domain = iommu_get_domain_for_dev(dev);
+	if (!domain || !domain->iova_cookie)
+		return -EINVAL;
+
+	cookie = domain->iova_cookie;
+	iovad = &cookie->iovad;
+
+	/* iova will be freed automatically by put_iova_domain() */
+	pfn_lo = iova_pfn(iovad, base);
+	pfn_hi = iova_pfn(iovad, base + size - 1);
+	if (!reserve_iova(iovad, pfn_lo, pfn_hi))
+		return -EINVAL;
+
+	return 0;
+}
+EXPORT_SYMBOL(iommu_dma_reserve_iova);
+
+/*
+ * Should be called prior to using dma-apis.
+ */
+int iommu_dma_enable_best_fit_algo(struct device *dev)
+{
+	struct iommu_domain *domain;
+	struct iova_domain *iovad;
+
+	domain = iommu_get_domain_for_dev(dev);
+	if (!domain || !domain->iova_cookie)
+		return -EINVAL;
+
+	iovad = &((struct iommu_dma_cookie *)domain->iova_cookie)->iovad;
+	iovad->best_fit = true;
+	return 0;
+}
+EXPORT_SYMBOL(iommu_dma_enable_best_fit_algo);
+
 /**
  * dma_info_to_prot - Translate DMA API directions and attributes to IOMMU API
  *                    page flags.
@@ -388,6 +434,10 @@ static int dma_info_to_prot(enum dma_data_direction dir, bool coherent,
 
 	if (attrs & DMA_ATTR_PRIVILEGED)
 		prot |= IOMMU_PRIV;
+	if (attrs & DMA_ATTR_SYS_CACHE_ONLY)
+		prot |= IOMMU_SYS_CACHE;
+	if (attrs & DMA_ATTR_SYS_CACHE_ONLY_NWA)
+		prot |= IOMMU_SYS_CACHE_NWA;
 
 	switch (dir) {
 	case DMA_BIDIRECTIONAL:
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 9d65557df..0853dd704 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -8,6 +8,7 @@
 
 #include <linux/device.h>
 #include <linux/kernel.h>
+#include <linux/bits.h>
 #include <linux/bug.h>
 #include <linux/types.h>
 #include <linux/init.h>
@@ -2203,7 +2204,8 @@ void iommu_detach_device(struct iommu_domain *domain, struct device *dev)
 		return;
 
 	mutex_lock(&group->mutex);
-	if (iommu_group_device_count(group) != 1) {
+	/* Don't break detach if iommu shared by more than one master */
+	if (iommu_group_device_count(group) < 1) {
 		WARN_ON(1);
 		goto out_unlock;
 	}
@@ -2337,38 +2339,85 @@ phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
 }
 EXPORT_SYMBOL_GPL(iommu_iova_to_phys);
 
-static size_t iommu_pgsize(struct iommu_domain *domain,
-			   unsigned long addr_merge, size_t size)
+static size_t iommu_pgsize(struct iommu_domain *domain, unsigned long iova,
+			   phys_addr_t paddr, size_t size, size_t *count)
 {
-	unsigned int pgsize_idx;
-	size_t pgsize;
+	unsigned int pgsize_idx, pgsize_idx_next;
+	unsigned long pgsizes;
+	size_t offset, pgsize, pgsize_next;
+	unsigned long addr_merge = paddr | iova;
 
-	/* Max page size that still fits into 'size' */
-	pgsize_idx = __fls(size);
+	/* Page sizes supported by the hardware and small enough for @size */
+	pgsizes = domain->pgsize_bitmap & GENMASK(__fls(size), 0);
 
-	/* need to consider alignment requirements ? */
-	if (likely(addr_merge)) {
-		/* Max page size allowed by address */
-		unsigned int align_pgsize_idx = __ffs(addr_merge);
-		pgsize_idx = min(pgsize_idx, align_pgsize_idx);
-	}
+	/* Constrain the page sizes further based on the maximum alignment */
+	if (likely(addr_merge))
+		pgsizes &= GENMASK(__ffs(addr_merge), 0);
+
+	/* Make sure we have at least one suitable page size */
+	BUG_ON(!pgsizes);
+
+	/* Pick the biggest page size remaining */
+	pgsize_idx = __fls(pgsizes);
+	pgsize = BIT(pgsize_idx);
+	if (!count)
+		return pgsize;
 
-	/* build a mask of acceptable page sizes */
-	pgsize = (1UL << (pgsize_idx + 1)) - 1;
 
-	/* throw away page sizes not supported by the hardware */
-	pgsize &= domain->pgsize_bitmap;
+	/* Find the next biggest support page size, if it exists */
+	pgsizes = domain->pgsize_bitmap & ~GENMASK(pgsize_idx, 0);
+	if (!pgsizes)
+		goto out_set_count;
 
-	/* make sure we're still sane */
-	BUG_ON(!pgsize);
+	pgsize_idx_next = __ffs(pgsizes);
+	pgsize_next = BIT(pgsize_idx_next);
 
-	/* pick the biggest page */
-	pgsize_idx = __fls(pgsize);
-	pgsize = 1UL << pgsize_idx;
+	/*
+	 * There's no point trying a bigger page size unless the virtual
+	 * and physical addresses are similarly offset within the larger page.
+	 */
+	if ((iova ^ paddr) & (pgsize_next - 1))
+		goto out_set_count;
+
+	/* Calculate the offset to the next page size alignment boundary */
+	offset = pgsize_next - (addr_merge & (pgsize_next - 1));
+
+	/*
+	 * If size is big enough to accommodate the larger page, reduce
+	 * the number of smaller pages.
+	 */
+	if (offset + pgsize_next <= size)
+		size = offset;
 
+out_set_count:
+	*count = size >> pgsize_idx;
 	return pgsize;
 }
 
+static int __iommu_map_pages(struct iommu_domain *domain, unsigned long iova,
+			     phys_addr_t paddr, size_t size, int prot,
+			     gfp_t gfp, size_t *mapped)
+{
+	const struct iommu_ops *ops = domain->ops;
+	size_t pgsize, count;
+	int ret;
+
+	pgsize = iommu_pgsize(domain, iova, paddr, size, &count);
+
+	pr_debug("mapping: iova 0x%lx pa %pa pgsize 0x%zx count %zu\n",
+			 iova, &paddr, pgsize, count);
+
+	if (ops->map_pages) {
+		ret = ops->map_pages(domain, iova, paddr, pgsize, count, prot,
+				     gfp, mapped);
+	} else {
+		ret = ops->map(domain, iova, paddr, pgsize, prot, gfp);
+		*mapped = ret ? 0 : pgsize;
+	}
+
+	return ret;
+}
+
 static int __iommu_map(struct iommu_domain *domain, unsigned long iova,
 		       phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
 {
@@ -2379,7 +2428,7 @@ static int __iommu_map(struct iommu_domain *domain, unsigned long iova,
 	phys_addr_t orig_paddr = paddr;
 	int ret = 0;
 
-	if (unlikely(ops->map == NULL ||
+	if (unlikely(!(ops->map || ops->map_pages) ||
 		     domain->pgsize_bitmap == 0UL))
 		return -ENODEV;
 
@@ -2403,18 +2452,21 @@ static int __iommu_map(struct iommu_domain *domain, unsigned long iova,
 	pr_debug("map: iova 0x%lx pa %pa size 0x%zx\n", iova, &paddr, size);
 
 	while (size) {
-		size_t pgsize = iommu_pgsize(domain, iova | paddr, size);
+		size_t mapped = 0;
 
-		pr_debug("mapping: iova 0x%lx pa %pa pgsize 0x%zx\n",
-			 iova, &paddr, pgsize);
-		ret = ops->map(domain, iova, paddr, pgsize, prot, gfp);
+		ret = __iommu_map_pages(domain, iova, paddr, size, prot, gfp,
+					&mapped);
+		/*
+		 * Some pages may have been mapped, even if an error occurred,
+		 * so we should account for those so they can be unmapped.
+		 */
+		size -= mapped;
 
 		if (ret)
 			break;
 
-		iova += pgsize;
-		paddr += pgsize;
-		size -= pgsize;
+		iova += mapped;
+		paddr += mapped;
 	}
 
 	/* unroll mapping in case something went wrong */
@@ -2434,7 +2486,7 @@ static int _iommu_map(struct iommu_domain *domain, unsigned long iova,
 
 	ret = __iommu_map(domain, iova, paddr, size, prot, gfp);
 	if (ret == 0 && ops->iotlb_sync_map)
-		ops->iotlb_sync_map(domain);
+		ops->iotlb_sync_map(domain, iova, size);
 
 	return ret;
 }
@@ -2454,6 +2506,19 @@ int iommu_map_atomic(struct iommu_domain *domain, unsigned long iova,
 }
 EXPORT_SYMBOL_GPL(iommu_map_atomic);
 
+static size_t __iommu_unmap_pages(struct iommu_domain *domain,
+				  unsigned long iova, size_t size,
+				  struct iommu_iotlb_gather *iotlb_gather)
+{
+	const struct iommu_ops *ops = domain->ops;
+	size_t pgsize, count;
+
+	pgsize = iommu_pgsize(domain, iova, iova, size, &count);
+	return ops->unmap_pages ?
+	       ops->unmap_pages(domain, iova, pgsize, count, iotlb_gather) :
+	       ops->unmap(domain, iova, pgsize, iotlb_gather);
+}
+
 static size_t __iommu_unmap(struct iommu_domain *domain,
 			    unsigned long iova, size_t size,
 			    struct iommu_iotlb_gather *iotlb_gather)
@@ -2463,7 +2528,7 @@ static size_t __iommu_unmap(struct iommu_domain *domain,
 	unsigned long orig_iova = iova;
 	unsigned int min_pagesz;
 
-	if (unlikely(ops->unmap == NULL ||
+	if (unlikely(!(ops->unmap || ops->unmap_pages) ||
 		     domain->pgsize_bitmap == 0UL))
 		return 0;
 
@@ -2491,9 +2556,9 @@ static size_t __iommu_unmap(struct iommu_domain *domain,
 	 * or we hit an area that isn't mapped.
 	 */
 	while (unmapped < size) {
-		size_t pgsize = iommu_pgsize(domain, iova, size - unmapped);
-
-		unmapped_page = ops->unmap(domain, iova, pgsize, iotlb_gather);
+		unmapped_page = __iommu_unmap_pages(domain, iova,
+						    size - unmapped,
+						    iotlb_gather);
 		if (!unmapped_page)
 			break;
 
@@ -2540,6 +2605,18 @@ static size_t __iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
 	unsigned int i = 0;
 	int ret;
 
+	if (ops->map_sg) {
+		ret = ops->map_sg(domain, iova, sg, nents, prot, gfp, &mapped);
+
+		if (ops->iotlb_sync_map)
+			ops->iotlb_sync_map(domain, iova, mapped);
+
+		if (ret)
+			goto out_err;
+
+		return mapped;
+	}
+
 	while (i <= nents) {
 		phys_addr_t s_phys = sg_phys(sg);
 
@@ -2566,7 +2643,7 @@ static size_t __iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
 	}
 
 	if (ops->iotlb_sync_map)
-		ops->iotlb_sync_map(domain);
+		ops->iotlb_sync_map(domain, iova, mapped);
 	return mapped;
 
 out_err:
diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
index 4600e97ac..cf32b592a 100644
--- a/drivers/iommu/iova.c
+++ b/drivers/iommu/iova.c
@@ -50,6 +50,7 @@ init_iova_domain(struct iova_domain *iovad, unsigned long granule,
 	iovad->anchor.pfn_lo = iovad->anchor.pfn_hi = IOVA_ANCHOR;
 	rb_link_node(&iovad->anchor.node, NULL, &iovad->rbroot.rb_node);
 	rb_insert_color(&iovad->anchor.node, &iovad->rbroot);
+	iovad->best_fit = false;
 	init_iova_rcaches(iovad);
 }
 EXPORT_SYMBOL_GPL(init_iova_domain);
@@ -64,7 +65,8 @@ static void free_iova_flush_queue(struct iova_domain *iovad)
 	if (!has_iova_flush_queue(iovad))
 		return;
 
-	del_timer_sync(&iovad->fq_timer);
+	if (timer_pending(&iovad->fq_timer))
+		del_timer(&iovad->fq_timer);
 
 	fq_destroy_all_entries(iovad);
 
@@ -177,6 +179,24 @@ iova_insert_rbtree(struct rb_root *root, struct iova *iova,
 	rb_insert_color(&iova->node, root);
 }
 
+#ifdef CONFIG_IOMMU_LIMIT_IOVA_ALIGNMENT
+static unsigned long limit_align_shift(struct iova_domain *iovad,
+				       unsigned long shift)
+{
+	unsigned long max_align_shift;
+
+	max_align_shift = CONFIG_IOMMU_IOVA_ALIGNMENT + PAGE_SHIFT
+		- iova_shift(iovad);
+	return min_t(unsigned long, max_align_shift, shift);
+}
+#else
+static unsigned long limit_align_shift(struct iova_domain *iovad,
+				       unsigned long shift)
+{
+	return shift;
+}
+#endif
+
 static int __alloc_and_insert_iova_range(struct iova_domain *iovad,
 		unsigned long size, unsigned long limit_pfn,
 			struct iova *new, bool size_aligned)
@@ -184,11 +204,12 @@ static int __alloc_and_insert_iova_range(struct iova_domain *iovad,
 	struct rb_node *curr, *prev;
 	struct iova *curr_iova;
 	unsigned long flags;
-	unsigned long new_pfn;
+	unsigned long new_pfn, low_pfn_new;
 	unsigned long align_mask = ~0UL;
+	unsigned long high_pfn = limit_pfn, low_pfn = iovad->start_pfn;
 
 	if (size_aligned)
-		align_mask <<= fls_long(size - 1);
+		align_mask <<= limit_align_shift(iovad, fls_long(size - 1));
 
 	/* Walk the tree backwards */
 	spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
@@ -198,15 +219,25 @@ static int __alloc_and_insert_iova_range(struct iova_domain *iovad,
 
 	curr = __get_cached_rbnode(iovad, limit_pfn);
 	curr_iova = rb_entry(curr, struct iova, node);
+	low_pfn_new = curr_iova->pfn_hi + 1;
+
+retry:
 	do {
-		limit_pfn = min(limit_pfn, curr_iova->pfn_lo);
-		new_pfn = (limit_pfn - size) & align_mask;
+		high_pfn = min(high_pfn, curr_iova->pfn_lo);
+		new_pfn = (high_pfn - size) & align_mask;
 		prev = curr;
 		curr = rb_prev(curr);
 		curr_iova = rb_entry(curr, struct iova, node);
-	} while (curr && new_pfn <= curr_iova->pfn_hi);
-
-	if (limit_pfn < size || new_pfn < iovad->start_pfn) {
+	} while (curr && new_pfn <= curr_iova->pfn_hi && new_pfn >= low_pfn);
+
+	if (high_pfn < size || new_pfn < low_pfn) {
+		if (low_pfn == iovad->start_pfn && low_pfn_new < limit_pfn) {
+			high_pfn = limit_pfn;
+			low_pfn = low_pfn_new;
+			curr = &iovad->anchor.node;
+			curr_iova = rb_entry(curr, struct iova, node);
+			goto retry;
+		}
 		iovad->max32_alloc_size = size;
 		goto iova32_full;
 	}
@@ -227,6 +258,70 @@ static int __alloc_and_insert_iova_range(struct iova_domain *iovad,
 	return -ENOMEM;
 }
 
+static int __alloc_and_insert_iova_best_fit(struct iova_domain *iovad,
+					    unsigned long size,
+					    unsigned long limit_pfn,
+					    struct iova *new, bool size_aligned)
+{
+	struct rb_node *curr, *prev;
+	struct iova *curr_iova, *prev_iova;
+	unsigned long flags;
+	unsigned long align_mask = ~0UL;
+	struct rb_node *candidate_rb_parent;
+	unsigned long new_pfn, candidate_pfn = ~0UL;
+	unsigned long gap, candidate_gap = ~0UL;
+
+	if (size_aligned)
+		align_mask <<= limit_align_shift(iovad, fls_long(size - 1));
+
+	/* Walk the tree backwards */
+	spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
+	curr = &iovad->anchor.node;
+	prev = rb_prev(curr);
+	for (; prev; curr = prev, prev = rb_prev(curr)) {
+		curr_iova = rb_entry(curr, struct iova, node);
+		prev_iova = rb_entry(prev, struct iova, node);
+
+		limit_pfn = min(limit_pfn, curr_iova->pfn_lo);
+		new_pfn = (limit_pfn - size) & align_mask;
+		gap = curr_iova->pfn_lo - prev_iova->pfn_hi - 1;
+		if ((limit_pfn >= size) && (new_pfn > prev_iova->pfn_hi)
+				&& (gap < candidate_gap)) {
+			candidate_gap = gap;
+			candidate_pfn = new_pfn;
+			candidate_rb_parent = curr;
+			if (gap == size)
+				goto insert;
+		}
+	}
+
+	curr_iova = rb_entry(curr, struct iova, node);
+	limit_pfn = min(limit_pfn, curr_iova->pfn_lo);
+	new_pfn = (limit_pfn - size) & align_mask;
+	gap = curr_iova->pfn_lo - iovad->start_pfn;
+	if (limit_pfn >= size && new_pfn >= iovad->start_pfn &&
+			gap < candidate_gap) {
+		candidate_gap = gap;
+		candidate_pfn = new_pfn;
+		candidate_rb_parent = curr;
+	}
+
+insert:
+	if (candidate_pfn == ~0UL) {
+		spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
+		return -ENOMEM;
+	}
+
+	/* pfn_lo will point to size aligned address if size_aligned is set */
+	new->pfn_lo = candidate_pfn;
+	new->pfn_hi = new->pfn_lo + size - 1;
+
+	/* If we have 'prev', it's a valid place to start the insertion. */
+	iova_insert_rbtree(&iovad->rbroot, new, candidate_rb_parent);
+	spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
+	return 0;
+}
+
 static struct kmem_cache *iova_cache;
 static unsigned int iova_cache_users;
 static DEFINE_MUTEX(iova_cache_mutex);
@@ -302,8 +397,13 @@ alloc_iova(struct iova_domain *iovad, unsigned long size,
 	if (!new_iova)
 		return NULL;
 
-	ret = __alloc_and_insert_iova_range(iovad, size, limit_pfn + 1,
-			new_iova, size_aligned);
+	if (iovad->best_fit) {
+		ret = __alloc_and_insert_iova_best_fit(iovad, size,
+				limit_pfn + 1, new_iova, size_aligned);
+	} else {
+		ret = __alloc_and_insert_iova_range(iovad, size, limit_pfn + 1,
+				new_iova, size_aligned);
+	}
 
 	if (ret) {
 		free_iova_mem(new_iova);
@@ -431,6 +531,7 @@ alloc_iova_fast(struct iova_domain *iovad, unsigned long size,
 		flush_rcache = false;
 		for_each_online_cpu(cpu)
 			free_cpu_cached_iovas(cpu, iovad);
+		free_global_cached_iovas(iovad);
 		goto retry;
 	}
 
@@ -1046,5 +1147,27 @@ void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad)
 	}
 }
 
+/*
+ * free all the IOVA ranges of global cache
+ */
+void free_global_cached_iovas(struct iova_domain *iovad)
+{
+	struct iova_rcache *rcache;
+	unsigned long flags;
+	int i, j;
+
+	for (i = 0; i < IOVA_RANGE_CACHE_MAX_SIZE; ++i) {
+		rcache = &iovad->rcaches[i];
+		spin_lock_irqsave(&rcache->lock, flags);
+		for (j = 0; j < rcache->depot_size; ++j) {
+			iova_magazine_free_pfns(rcache->depot[j], iovad);
+			iova_magazine_free(rcache->depot[j]);
+			rcache->depot[j] = NULL;
+		}
+		rcache->depot_size = 0;
+		spin_unlock_irqrestore(&rcache->lock, flags);
+	}
+}
+
 MODULE_AUTHOR("Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>");
 MODULE_LICENSE("GPL");
diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
index e5d86b717..b359fcf2c 100644
--- a/drivers/iommu/rockchip-iommu.c
+++ b/drivers/iommu/rockchip-iommu.c
@@ -19,6 +19,7 @@
 #include <linux/iopoll.h>
 #include <linux/list.h>
 #include <linux/mm.h>
+#include <linux/module.h>
 #include <linux/init.h>
 #include <linux/of.h>
 #include <linux/of_iommu.h>
@@ -27,6 +28,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
+#include <soc/rockchip/rockchip_iommu.h>
 
 /** MMU register offsets */
 #define RK_MMU_DTE_ADDR		0x00	/* Directory table address */
@@ -75,25 +77,53 @@
 #define SPAGE_ORDER 12
 #define SPAGE_SIZE (1 << SPAGE_ORDER)
 
+#define DISABLE_FETCH_DTE_TIME_LIMIT BIT(31)
+
+#define CMD_RETRY_COUNT 10
+
  /*
   * Support mapping any size that fits in one page table:
   *   4 KiB to 4 MiB
   */
 #define RK_IOMMU_PGSIZE_BITMAP 0x007ff000
 
+#define DT_LO_MASK 0xfffff000
+#define DT_HI_MASK GENMASK_ULL(39, 32)
+#define DT_SHIFT   28
+
+#define DTE_BASE_HI_MASK GENMASK(11, 4)
+
+#define PAGE_DESC_LO_MASK   0xfffff000
+#define PAGE_DESC_HI1_LOWER 32
+#define PAGE_DESC_HI1_UPPER 35
+#define PAGE_DESC_HI2_LOWER 36
+#define PAGE_DESC_HI2_UPPER 39
+#define PAGE_DESC_HI_MASK1  GENMASK_ULL(PAGE_DESC_HI1_UPPER, PAGE_DESC_HI1_LOWER)
+#define PAGE_DESC_HI_MASK2  GENMASK_ULL(PAGE_DESC_HI2_UPPER, PAGE_DESC_HI2_LOWER)
+
+#define DTE_HI1_LOWER 8
+#define DTE_HI1_UPPER 11
+#define DTE_HI2_LOWER 4
+#define DTE_HI2_UPPER 7
+#define DTE_HI_MASK1  GENMASK(DTE_HI1_UPPER, DTE_HI1_LOWER)
+#define DTE_HI_MASK2  GENMASK(DTE_HI2_UPPER, DTE_HI2_LOWER)
+
+#define PAGE_DESC_HI_SHIFT1 (PAGE_DESC_HI1_LOWER - DTE_HI1_LOWER)
+#define PAGE_DESC_HI_SHIFT2 (PAGE_DESC_HI2_LOWER - DTE_HI2_LOWER)
+
 struct rk_iommu_domain {
 	struct list_head iommus;
 	u32 *dt; /* page directory table */
 	dma_addr_t dt_dma;
 	spinlock_t iommus_lock; /* lock for iommus list */
 	spinlock_t dt_lock; /* lock for modifying page directory table */
+	bool shootdown_entire;
 
 	struct iommu_domain domain;
 };
 
-/* list of clocks required by IOMMU */
-static const char * const rk_iommu_clocks[] = {
-	"aclk", "iface",
+struct rockchip_iommu_data {
+	u32 version;
 };
 
 struct rk_iommu {
@@ -104,15 +134,21 @@ struct rk_iommu {
 	struct clk_bulk_data *clocks;
 	int num_clocks;
 	bool reset_disabled;
+	bool skip_read; /* rk3126/rk3128 can't read vop iommu registers */
+	bool dlr_disable; /* avoid access iommu when runtime ops called */
+	bool cmd_retry;
 	struct iommu_device iommu;
 	struct list_head node; /* entry in rk_iommu_domain.iommus */
 	struct iommu_domain *domain; /* domain to which iommu is attached */
 	struct iommu_group *group;
+	u32 version;
+	bool shootdown_entire;
 };
 
 struct rk_iommudata {
 	struct device_link *link; /* runtime PM link from IOMMU to master */
 	struct rk_iommu *iommu;
+	bool defer_attach;
 };
 
 static struct device *dma_dev;
@@ -174,11 +210,32 @@ static struct rk_iommu_domain *to_rk_domain(struct iommu_domain *dom)
 #define RK_DTE_PT_ADDRESS_MASK    0xfffff000
 #define RK_DTE_PT_VALID           BIT(0)
 
+/*
+ * In v2:
+ * 31:12 - PT address bit 31:0
+ * 11: 8 - PT address bit 35:32
+ *  7: 4 - PT address bit 39:36
+ *  3: 1 - Reserved
+ *     0 - 1 if PT @ PT address is valid
+ */
+#define RK_DTE_PT_ADDRESS_MASK_V2 0xfffffff0
+
 static inline phys_addr_t rk_dte_pt_address(u32 dte)
 {
 	return (phys_addr_t)dte & RK_DTE_PT_ADDRESS_MASK;
 }
 
+static inline phys_addr_t rk_dte_pt_address_v2(u32 dte)
+{
+	u64 dte_v2 = dte;
+
+	dte_v2 = ((dte_v2 & DTE_HI_MASK2) << PAGE_DESC_HI_SHIFT2) |
+		 ((dte_v2 & DTE_HI_MASK1) << PAGE_DESC_HI_SHIFT1) |
+		 (dte_v2 & PAGE_DESC_LO_MASK);
+
+	return (phys_addr_t)dte_v2;
+}
+
 static inline bool rk_dte_is_pt_valid(u32 dte)
 {
 	return dte & RK_DTE_PT_VALID;
@@ -189,6 +246,15 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
 	return (pt_dma & RK_DTE_PT_ADDRESS_MASK) | RK_DTE_PT_VALID;
 }
 
+static inline u32 rk_mk_dte_v2(dma_addr_t pt_dma)
+{
+	pt_dma = (pt_dma & PAGE_DESC_LO_MASK) |
+		 ((pt_dma & PAGE_DESC_HI_MASK1) >> PAGE_DESC_HI_SHIFT1) |
+		 (pt_dma & PAGE_DESC_HI_MASK2) >> PAGE_DESC_HI_SHIFT2;
+
+	return (pt_dma & RK_DTE_PT_ADDRESS_MASK_V2) | RK_DTE_PT_VALID;
+}
+
 /*
  * Each PTE has a Page address, some flags and a valid bit:
  * +---------------------+---+-------+-+
@@ -215,11 +281,37 @@ static inline u32 rk_mk_dte(dma_addr_t pt_dma)
 #define RK_PTE_PAGE_READABLE      BIT(1)
 #define RK_PTE_PAGE_VALID         BIT(0)
 
+/*
+ * In v2:
+ * 31:12 - Page address bit 31:0
+ *  11:9 - Page address bit 34:32
+ *   8:4 - Page address bit 39:35
+ *     3 - Security
+ *     2 - Writable
+ *     1 - Readable
+ *     0 - 1 if Page @ Page address is valid
+ */
+#define RK_PTE_PAGE_ADDRESS_MASK_V2  0xfffffff0
+#define RK_PTE_PAGE_FLAGS_MASK_V2    0x0000000e
+#define RK_PTE_PAGE_READABLE_V2      BIT(1)
+#define RK_PTE_PAGE_WRITABLE_V2      BIT(2)
+
 static inline phys_addr_t rk_pte_page_address(u32 pte)
 {
 	return (phys_addr_t)pte & RK_PTE_PAGE_ADDRESS_MASK;
 }
 
+static inline phys_addr_t rk_pte_page_address_v2(u32 pte)
+{
+	u64 pte_v2 = pte;
+
+	pte_v2 = ((pte_v2 & DTE_HI_MASK2) << PAGE_DESC_HI_SHIFT2) |
+		 ((pte_v2 & DTE_HI_MASK1) << PAGE_DESC_HI_SHIFT1) |
+		 (pte_v2 & PAGE_DESC_LO_MASK);
+
+	return (phys_addr_t)pte_v2;
+}
+
 static inline bool rk_pte_is_page_valid(u32 pte)
 {
 	return pte & RK_PTE_PAGE_VALID;
@@ -235,6 +327,20 @@ static u32 rk_mk_pte(phys_addr_t page, int prot)
 	return page | flags | RK_PTE_PAGE_VALID;
 }
 
+static u32 rk_mk_pte_v2(phys_addr_t page, int prot)
+{
+	u32 flags = 0;
+
+	flags |= (prot & IOMMU_READ) ? RK_PTE_PAGE_READABLE_V2 : 0;
+	flags |= (prot & IOMMU_WRITE) ? RK_PTE_PAGE_WRITABLE_V2 : 0;
+	page = (page & PAGE_DESC_LO_MASK) |
+	       ((page & PAGE_DESC_HI_MASK1) >> PAGE_DESC_HI_SHIFT1) |
+	       (page & PAGE_DESC_HI_MASK2) >> PAGE_DESC_HI_SHIFT2;
+	page &= RK_PTE_PAGE_ADDRESS_MASK_V2;
+
+	return page | flags | RK_PTE_PAGE_VALID;
+}
+
 static u32 rk_mk_pte_invalid(u32 pte)
 {
 	return pte & ~RK_PTE_PAGE_VALID;
@@ -350,6 +456,10 @@ static int rk_iommu_enable_stall(struct rk_iommu *iommu)
 {
 	int ret, i;
 	bool val;
+	int retry_count = 0;
+
+	if (iommu->skip_read)
+		goto read_wa;
 
 	if (rk_iommu_is_stall_active(iommu))
 		return 0;
@@ -358,15 +468,22 @@ static int rk_iommu_enable_stall(struct rk_iommu *iommu)
 	if (!rk_iommu_is_paging_enabled(iommu))
 		return 0;
 
+read_wa:
 	rk_iommu_command(iommu, RK_MMU_CMD_ENABLE_STALL);
+	if (iommu->skip_read)
+		return 0;
 
 	ret = readx_poll_timeout(rk_iommu_is_stall_active, iommu, val,
 				 val, RK_MMU_POLL_PERIOD_US,
 				 RK_MMU_POLL_TIMEOUT_US);
-	if (ret)
+	if (ret) {
 		for (i = 0; i < iommu->num_mmu; i++)
-			dev_err(iommu->dev, "Enable stall request timed out, status: %#08x\n",
+			dev_err(iommu->dev, "Enable stall request timed out, retry_count = %d, status: %#08x\n",
+				retry_count,
 				rk_iommu_read(iommu->bases[i], RK_MMU_STATUS));
+		if (iommu->cmd_retry && (retry_count++ < CMD_RETRY_COUNT))
+			goto read_wa;
+	}
 
 	return ret;
 }
@@ -375,19 +492,30 @@ static int rk_iommu_disable_stall(struct rk_iommu *iommu)
 {
 	int ret, i;
 	bool val;
+	int retry_count = 0;
+
+	if (iommu->skip_read)
+		goto read_wa;
 
 	if (!rk_iommu_is_stall_active(iommu))
 		return 0;
 
+read_wa:
 	rk_iommu_command(iommu, RK_MMU_CMD_DISABLE_STALL);
+	if (iommu->skip_read)
+		return 0;
 
 	ret = readx_poll_timeout(rk_iommu_is_stall_active, iommu, val,
 				 !val, RK_MMU_POLL_PERIOD_US,
 				 RK_MMU_POLL_TIMEOUT_US);
-	if (ret)
+	if (ret) {
 		for (i = 0; i < iommu->num_mmu; i++)
-			dev_err(iommu->dev, "Disable stall request timed out, status: %#08x\n",
+			dev_err(iommu->dev, "Disable stall request timed out, retry_count = %d, status: %#08x\n",
+				retry_count,
 				rk_iommu_read(iommu->bases[i], RK_MMU_STATUS));
+		if (iommu->cmd_retry && (retry_count++ < CMD_RETRY_COUNT))
+			goto read_wa;
+	}
 
 	return ret;
 }
@@ -396,19 +524,30 @@ static int rk_iommu_enable_paging(struct rk_iommu *iommu)
 {
 	int ret, i;
 	bool val;
+	int retry_count = 0;
+
+	if (iommu->skip_read)
+		goto read_wa;
 
 	if (rk_iommu_is_paging_enabled(iommu))
 		return 0;
 
+read_wa:
 	rk_iommu_command(iommu, RK_MMU_CMD_ENABLE_PAGING);
+	if (iommu->skip_read)
+		return 0;
 
 	ret = readx_poll_timeout(rk_iommu_is_paging_enabled, iommu, val,
 				 val, RK_MMU_POLL_PERIOD_US,
 				 RK_MMU_POLL_TIMEOUT_US);
-	if (ret)
+	if (ret) {
 		for (i = 0; i < iommu->num_mmu; i++)
-			dev_err(iommu->dev, "Enable paging request timed out, status: %#08x\n",
+			dev_err(iommu->dev, "Enable paging request timed out, retry_count = %d, status: %#08x\n",
+				retry_count,
 				rk_iommu_read(iommu->bases[i], RK_MMU_STATUS));
+		if (iommu->cmd_retry && (retry_count++ < CMD_RETRY_COUNT))
+			goto read_wa;
+	}
 
 	return ret;
 }
@@ -417,19 +556,30 @@ static int rk_iommu_disable_paging(struct rk_iommu *iommu)
 {
 	int ret, i;
 	bool val;
+	int retry_count = 0;
+
+	if (iommu->skip_read)
+		goto read_wa;
 
 	if (!rk_iommu_is_paging_enabled(iommu))
 		return 0;
 
+read_wa:
 	rk_iommu_command(iommu, RK_MMU_CMD_DISABLE_PAGING);
+	if (iommu->skip_read)
+		return 0;
 
 	ret = readx_poll_timeout(rk_iommu_is_paging_enabled, iommu, val,
 				 !val, RK_MMU_POLL_PERIOD_US,
 				 RK_MMU_POLL_TIMEOUT_US);
-	if (ret)
+	if (ret) {
 		for (i = 0; i < iommu->num_mmu; i++)
-			dev_err(iommu->dev, "Disable paging request timed out, status: %#08x\n",
+			dev_err(iommu->dev, "Disable paging request timed out, retry_count = %d, status: %#08x\n",
+				retry_count,
 				rk_iommu_read(iommu->bases[i], RK_MMU_STATUS));
+		if (iommu->cmd_retry && (retry_count++ < CMD_RETRY_COUNT))
+			goto read_wa;
+	}
 
 	return ret;
 }
@@ -439,25 +589,40 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu)
 	int ret, i;
 	u32 dte_addr;
 	bool val;
+	u32 address_mask;
 
 	if (iommu->reset_disabled)
 		return 0;
 
+	if (iommu->skip_read)
+		goto read_wa;
+
 	/*
 	 * Check if register DTE_ADDR is working by writing DTE_ADDR_DUMMY
 	 * and verifying that upper 5 nybbles are read back.
 	 */
+
+	/*
+	 * In v2: upper 7 nybbles are read back.
+	 */
 	for (i = 0; i < iommu->num_mmu; i++) {
 		rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, DTE_ADDR_DUMMY);
 
+		if (iommu->version >= 0x2)
+			address_mask = RK_DTE_PT_ADDRESS_MASK_V2;
+		else
+			address_mask = RK_DTE_PT_ADDRESS_MASK;
 		dte_addr = rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR);
-		if (dte_addr != (DTE_ADDR_DUMMY & RK_DTE_PT_ADDRESS_MASK)) {
+		if (dte_addr != (DTE_ADDR_DUMMY & address_mask)) {
 			dev_err(iommu->dev, "Error during raw reset. MMU_DTE_ADDR is not functioning\n");
 			return -EFAULT;
 		}
 	}
 
+read_wa:
 	rk_iommu_command(iommu, RK_MMU_CMD_FORCE_RESET);
+	if (iommu->skip_read)
+		return 0;
 
 	ret = readx_poll_timeout(rk_iommu_is_reset_done, iommu, val,
 				 val, RK_MMU_FORCE_RESET_TIMEOUT_US,
@@ -490,6 +655,10 @@ static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
 
 	mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR);
 	mmu_dte_addr_phys = (phys_addr_t)mmu_dte_addr;
+	if (iommu->version >= 0x2) {
+		mmu_dte_addr_phys = (mmu_dte_addr_phys & DT_LO_MASK) |
+				    ((mmu_dte_addr_phys & DTE_BASE_HI_MASK) << DT_SHIFT);
+	}
 
 	dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index);
 	dte_addr = phys_to_virt(dte_addr_phys);
@@ -498,14 +667,20 @@ static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
 	if (!rk_dte_is_pt_valid(dte))
 		goto print_it;
 
-	pte_addr_phys = rk_dte_pt_address(dte) + (pte_index * 4);
+	if (iommu->version >= 0x2)
+		pte_addr_phys = rk_dte_pt_address_v2(dte) + (pte_index * 4);
+	else
+		pte_addr_phys = rk_dte_pt_address(dte) + (pte_index * 4);
 	pte_addr = phys_to_virt(pte_addr_phys);
 	pte = *pte_addr;
 
 	if (!rk_pte_is_page_valid(pte))
 		goto print_it;
 
-	page_addr_phys = rk_pte_page_address(pte) + page_offset;
+	if (iommu->version >= 0x2)
+		page_addr_phys = rk_pte_page_address_v2(pte) + page_offset;
+	else
+		page_addr_phys = rk_pte_page_address(pte) + page_offset;
 	page_flags = pte & RK_PTE_PAGE_FLAGS_MASK;
 
 print_it:
@@ -522,6 +697,7 @@ static irqreturn_t rk_iommu_irq(int irq, void *dev_id)
 	struct rk_iommu *iommu = dev_id;
 	u32 status;
 	u32 int_status;
+	u32 int_mask;
 	dma_addr_t iova;
 	irqreturn_t ret = IRQ_NONE;
 	int i, err;
@@ -561,12 +737,20 @@ static irqreturn_t rk_iommu_irq(int irq, void *dev_id)
 			 */
 			if (iommu->domain)
 				report_iommu_fault(iommu->domain, iommu->dev, iova,
-						   flags);
+						   status);
 			else
 				dev_err(iommu->dev, "Page fault while iommu not attached to domain?\n");
 
 			rk_iommu_base_command(iommu->bases[i], RK_MMU_CMD_ZAP_CACHE);
-			rk_iommu_base_command(iommu->bases[i], RK_MMU_CMD_PAGE_FAULT_DONE);
+
+			/*
+			 * Master may clear the int_mask to prevent iommu
+			 * re-enter interrupt when mapping. So we postpone
+			 * sending PAGE_FAULT_DONE command to mapping finished.
+			 */
+			int_mask = rk_iommu_read(iommu->bases[i], RK_MMU_INT_MASK);
+			if (int_mask != 0x0)
+				rk_iommu_base_command(iommu->bases[i], RK_MMU_CMD_PAGE_FAULT_DONE);
 		}
 
 		if (int_status & RK_MMU_IRQ_BUS_ERROR)
@@ -614,6 +798,34 @@ static phys_addr_t rk_iommu_iova_to_phys(struct iommu_domain *domain,
 	return phys;
 }
 
+static phys_addr_t rk_iommu_iova_to_phys_v2(struct iommu_domain *domain,
+					    dma_addr_t iova)
+{
+	struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
+	unsigned long flags;
+	phys_addr_t pt_phys, phys = 0;
+	u32 dte, pte;
+	u32 *page_table;
+
+	spin_lock_irqsave(&rk_domain->dt_lock, flags);
+
+	dte = rk_domain->dt[rk_iova_dte_index(iova)];
+	if (!rk_dte_is_pt_valid(dte))
+		goto out;
+
+	pt_phys = rk_dte_pt_address_v2(dte);
+	page_table = (u32 *)phys_to_virt(pt_phys);
+	pte = page_table[rk_iova_pte_index(iova)];
+	if (!rk_pte_is_page_valid(pte))
+		goto out;
+
+	phys = rk_pte_page_address_v2(pte) + rk_iova_page_offset(iova);
+out:
+	spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
+
+	return phys;
+}
+
 static void rk_iommu_zap_iova(struct rk_iommu_domain *rk_domain,
 			      dma_addr_t iova, size_t size)
 {
@@ -690,6 +902,44 @@ static u32 *rk_dte_get_page_table(struct rk_iommu_domain *rk_domain,
 	return (u32 *)phys_to_virt(pt_phys);
 }
 
+static u32 *rk_dte_get_page_table_v2(struct rk_iommu_domain *rk_domain,
+				     dma_addr_t iova)
+{
+	u32 *page_table, *dte_addr;
+	u32 dte_index, dte;
+	phys_addr_t pt_phys;
+	dma_addr_t pt_dma;
+
+	assert_spin_locked(&rk_domain->dt_lock);
+
+	dte_index = rk_iova_dte_index(iova);
+	dte_addr = &rk_domain->dt[dte_index];
+	dte = *dte_addr;
+	if (rk_dte_is_pt_valid(dte))
+		goto done;
+
+	page_table = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32);
+	if (!page_table)
+		return ERR_PTR(-ENOMEM);
+
+	pt_dma = dma_map_single(dma_dev, page_table, SPAGE_SIZE, DMA_TO_DEVICE);
+	if (dma_mapping_error(dma_dev, pt_dma)) {
+		dev_err(dma_dev, "DMA mapping error while allocating page table\n");
+		free_page((unsigned long)page_table);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	dte = rk_mk_dte_v2(pt_dma);
+	*dte_addr = dte;
+
+	rk_table_flush(rk_domain, pt_dma, NUM_PT_ENTRIES);
+	rk_table_flush(rk_domain,
+		       rk_domain->dt_dma + dte_index * sizeof(u32), 1);
+done:
+	pt_phys = rk_dte_pt_address_v2(dte);
+	return (u32 *)phys_to_virt(pt_phys);
+}
+
 static size_t rk_iommu_unmap_iova(struct rk_iommu_domain *rk_domain,
 				  u32 *pte_addr, dma_addr_t pte_dma,
 				  size_t size)
@@ -741,7 +991,9 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
 	 * We only zap the first and last iova, since only they could have
 	 * dte or pte shared with an existing mapping.
 	 */
-	rk_iommu_zap_iova_first_last(rk_domain, iova, size);
+	/* Do not zap tlb cache line if shootdown_entire set */
+	if (!rk_domain->shootdown_entire)
+		rk_iommu_zap_iova_first_last(rk_domain, iova, size);
 
 	return 0;
 unwind:
@@ -757,6 +1009,53 @@ static int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
 	return -EADDRINUSE;
 }
 
+static int rk_iommu_map_iova_v2(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
+				dma_addr_t pte_dma, dma_addr_t iova,
+				phys_addr_t paddr, size_t size, int prot)
+{
+	unsigned int pte_count;
+	unsigned int pte_total = size / SPAGE_SIZE;
+	phys_addr_t page_phys;
+
+	assert_spin_locked(&rk_domain->dt_lock);
+
+	for (pte_count = 0; pte_count < pte_total; pte_count++) {
+		u32 pte = pte_addr[pte_count];
+
+		if (rk_pte_is_page_valid(pte))
+			goto unwind;
+
+		pte_addr[pte_count] = rk_mk_pte_v2(paddr, prot);
+
+		paddr += SPAGE_SIZE;
+	}
+
+	rk_table_flush(rk_domain, pte_dma, pte_total);
+
+	/*
+	 * Zap the first and last iova to evict from iotlb any previously
+	 * mapped cachelines holding stale values for its dte and pte.
+	 * We only zap the first and last iova, since only they could have
+	 * dte or pte shared with an existing mapping.
+	 */
+	/* Do not zap tlb cache line if shootdown_entire set */
+	if (!rk_domain->shootdown_entire)
+		rk_iommu_zap_iova_first_last(rk_domain, iova, size);
+
+	return 0;
+unwind:
+	/* Unmap the range of iovas that we just mapped */
+	rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma,
+			    pte_count * SPAGE_SIZE);
+
+	iova += pte_count * SPAGE_SIZE;
+	page_phys = rk_pte_page_address_v2(pte_addr[pte_count]);
+	pr_err("iova: %pad already mapped to %pa cannot remap to phys: %pa prot: %#x\n",
+	       &iova, &page_phys, &paddr, prot);
+
+	return -EADDRINUSE;
+}
+
 static int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova,
 			phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
 {
@@ -764,7 +1063,7 @@ static int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova,
 	unsigned long flags;
 	dma_addr_t pte_dma, iova = (dma_addr_t)_iova;
 	u32 *page_table, *pte_addr;
-	u32 dte_index, pte_index;
+	u32 dte, pte_index;
 	int ret;
 
 	spin_lock_irqsave(&rk_domain->dt_lock, flags);
@@ -782,10 +1081,10 @@ static int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova,
 		return PTR_ERR(page_table);
 	}
 
-	dte_index = rk_domain->dt[rk_iova_dte_index(iova)];
+	dte = rk_domain->dt[rk_iova_dte_index(iova)];
 	pte_index = rk_iova_pte_index(iova);
 	pte_addr = &page_table[pte_index];
-	pte_dma = rk_dte_pt_address(dte_index) + pte_index * sizeof(u32);
+	pte_dma = rk_dte_pt_address(dte) + pte_index * sizeof(u32);
 	ret = rk_iommu_map_iova(rk_domain, pte_addr, pte_dma, iova,
 				paddr, size, prot);
 
@@ -794,6 +1093,43 @@ static int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova,
 	return ret;
 }
 
+static int rk_iommu_map_v2(struct iommu_domain *domain, unsigned long _iova,
+			phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
+{
+	struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
+	unsigned long flags;
+	dma_addr_t pte_dma, iova = (dma_addr_t)_iova;
+	u32 *page_table, *pte_addr;
+	u32 dte, pte_index;
+	int ret;
+
+	spin_lock_irqsave(&rk_domain->dt_lock, flags);
+
+	/*
+	 * pgsize_bitmap specifies iova sizes that fit in one page table
+	 * (1024 4-KiB pages = 4 MiB).
+	 * So, size will always be 4096 <= size <= 4194304.
+	 * Since iommu_map() guarantees that both iova and size will be
+	 * aligned, we will always only be mapping from a single dte here.
+	 */
+	page_table = rk_dte_get_page_table_v2(rk_domain, iova);
+	if (IS_ERR(page_table)) {
+		spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
+		return PTR_ERR(page_table);
+	}
+
+	dte = rk_domain->dt[rk_iova_dte_index(iova)];
+	pte_index = rk_iova_pte_index(iova);
+	pte_addr = &page_table[pte_index];
+	pte_dma = rk_dte_pt_address_v2(dte) + pte_index * sizeof(u32);
+	ret = rk_iommu_map_iova_v2(rk_domain, pte_addr, pte_dma, iova,
+				   paddr, size, prot);
+
+	spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
+
+	return ret;
+}
+
 static size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova,
 			     size_t size, struct iommu_iotlb_gather *gather)
 {
@@ -834,6 +1170,77 @@ static size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova,
 	return unmap_size;
 }
 
+static size_t rk_iommu_unmap_v2(struct iommu_domain *domain, unsigned long _iova,
+				size_t size, struct iommu_iotlb_gather *gather)
+{
+	struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
+	unsigned long flags;
+	dma_addr_t pte_dma, iova = (dma_addr_t)_iova;
+	phys_addr_t pt_phys;
+	u32 dte;
+	u32 *pte_addr;
+	size_t unmap_size;
+
+	spin_lock_irqsave(&rk_domain->dt_lock, flags);
+
+	/*
+	 * pgsize_bitmap specifies iova sizes that fit in one page table
+	 * (1024 4-KiB pages = 4 MiB).
+	 * So, size will always be 4096 <= size <= 4194304.
+	 * Since iommu_unmap() guarantees that both iova and size will be
+	 * aligned, we will always only be unmapping from a single dte here.
+	 */
+	dte = rk_domain->dt[rk_iova_dte_index(iova)];
+	/* Just return 0 if iova is unmapped */
+	if (!rk_dte_is_pt_valid(dte)) {
+		spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
+		return 0;
+	}
+
+	pt_phys = rk_dte_pt_address_v2(dte);
+	pte_addr = (u32 *)phys_to_virt(pt_phys) + rk_iova_pte_index(iova);
+	pte_dma = pt_phys + rk_iova_pte_index(iova) * sizeof(u32);
+	unmap_size = rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma, size);
+
+	spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
+
+	/* Shootdown iotlb entries for iova range that was just unmapped */
+	/* Do not zap tlb cache line if shootdown_entire set */
+	if (!rk_domain->shootdown_entire)
+		rk_iommu_zap_iova(rk_domain, iova, unmap_size);
+
+	return unmap_size;
+}
+
+static void rk_iommu_flush_tlb_all(struct iommu_domain *domain)
+{
+	struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
+	struct list_head *pos;
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&rk_domain->iommus_lock, flags);
+	list_for_each(pos, &rk_domain->iommus) {
+		struct rk_iommu *iommu;
+		int ret;
+
+		iommu = list_entry(pos, struct rk_iommu, node);
+
+		ret = pm_runtime_get_if_in_use(iommu->dev);
+		if (WARN_ON_ONCE(ret < 0))
+			continue;
+		if (ret) {
+			WARN_ON(clk_bulk_enable(iommu->num_clocks, iommu->clocks));
+			for (i = 0; i < iommu->num_mmu; i++)
+				rk_iommu_write(iommu->bases[i], RK_MMU_COMMAND,
+					       RK_MMU_CMD_ZAP_CACHE);
+			clk_bulk_disable(iommu->num_clocks, iommu->clocks);
+			pm_runtime_put(iommu->dev);
+		}
+	}
+	spin_unlock_irqrestore(&rk_domain->iommus_lock, flags);
+}
+
 static struct rk_iommu *rk_iommu_from_dev(struct device *dev)
 {
 	struct rk_iommudata *data = dev_iommu_priv_get(dev);
@@ -858,12 +1265,28 @@ static void rk_iommu_disable(struct rk_iommu *iommu)
 	clk_bulk_disable(iommu->num_clocks, iommu->clocks);
 }
 
+int rockchip_iommu_disable(struct device *dev)
+{
+	struct rk_iommu *iommu;
+
+	iommu = rk_iommu_from_dev(dev);
+	if (!iommu)
+		return -ENODEV;
+
+	rk_iommu_disable(iommu);
+
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_iommu_disable);
+
 /* Must be called with iommu powered on and attached */
 static int rk_iommu_enable(struct rk_iommu *iommu)
 {
 	struct iommu_domain *domain = iommu->domain;
 	struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
 	int ret, i;
+	u32 dt_v2;
+	u32 auto_gate;
 
 	ret = clk_bulk_enable(iommu->num_clocks, iommu->clocks);
 	if (ret)
@@ -878,10 +1301,21 @@ static int rk_iommu_enable(struct rk_iommu *iommu)
 		goto out_disable_stall;
 
 	for (i = 0; i < iommu->num_mmu; i++) {
-		rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR,
-			       rk_domain->dt_dma);
+		if (iommu->version >= 0x2) {
+			dt_v2 = (rk_domain->dt_dma & DT_LO_MASK) |
+				((rk_domain->dt_dma & DT_HI_MASK) >> DT_SHIFT);
+			rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, dt_v2);
+		} else {
+			rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR,
+				       rk_domain->dt_dma);
+		}
 		rk_iommu_base_command(iommu->bases[i], RK_MMU_CMD_ZAP_CACHE);
 		rk_iommu_write(iommu->bases[i], RK_MMU_INT_MASK, RK_MMU_IRQ_MASK);
+
+		/* Workaround for iommu blocked, BIT(31) default to 1 */
+		auto_gate = rk_iommu_read(iommu->bases[i], RK_MMU_AUTO_GATING);
+		auto_gate |= DISABLE_FETCH_DTE_TIME_LIMIT;
+		rk_iommu_write(iommu->bases[i], RK_MMU_AUTO_GATING, auto_gate);
 	}
 
 	ret = rk_iommu_enable_paging(iommu);
@@ -893,6 +1327,18 @@ static int rk_iommu_enable(struct rk_iommu *iommu)
 	return ret;
 }
 
+int rockchip_iommu_enable(struct device *dev)
+{
+	struct rk_iommu *iommu;
+
+	iommu = rk_iommu_from_dev(dev);
+	if (!iommu)
+		return -ENODEV;
+
+	return rk_iommu_enable(iommu);
+}
+EXPORT_SYMBOL(rockchip_iommu_enable);
+
 static void rk_iommu_detach_device(struct iommu_domain *domain,
 				   struct device *dev)
 {
@@ -908,8 +1354,7 @@ static void rk_iommu_detach_device(struct iommu_domain *domain,
 
 	dev_dbg(dev, "Detaching from iommu domain\n");
 
-	/* iommu already detached */
-	if (iommu->domain != domain)
+	if (!iommu->domain)
 		return;
 
 	iommu->domain = NULL;
@@ -944,19 +1389,20 @@ static int rk_iommu_attach_device(struct iommu_domain *domain,
 
 	dev_dbg(dev, "Attaching to iommu domain\n");
 
-	/* iommu already attached */
-	if (iommu->domain == domain)
-		return 0;
-
 	if (iommu->domain)
 		rk_iommu_detach_device(iommu->domain, dev);
 
 	iommu->domain = domain;
 
+	/* Attach NULL for disable iommu */
+	if (!domain)
+		return 0;
+
 	spin_lock_irqsave(&rk_domain->iommus_lock, flags);
 	list_add_tail(&iommu->node, &rk_domain->iommus);
 	spin_unlock_irqrestore(&rk_domain->iommus_lock, flags);
 
+	rk_domain->shootdown_entire = iommu->shootdown_entire;
 	ret = pm_runtime_get_if_in_use(iommu->dev);
 	if (!ret || WARN_ON_ONCE(ret < 0))
 		return 0;
@@ -1054,6 +1500,35 @@ static void rk_iommu_domain_free(struct iommu_domain *domain)
 	kfree(rk_domain);
 }
 
+static void rk_iommu_domain_free_v2(struct iommu_domain *domain)
+{
+	struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
+	int i;
+
+	WARN_ON(!list_empty(&rk_domain->iommus));
+
+	for (i = 0; i < NUM_DT_ENTRIES; i++) {
+		u32 dte = rk_domain->dt[i];
+
+		if (rk_dte_is_pt_valid(dte)) {
+			phys_addr_t pt_phys = rk_dte_pt_address_v2(dte);
+			u32 *page_table = phys_to_virt(pt_phys);
+
+			dma_unmap_single(dma_dev, pt_phys,
+					 SPAGE_SIZE, DMA_TO_DEVICE);
+			free_page((unsigned long)page_table);
+		}
+	}
+
+	dma_unmap_single(dma_dev, rk_domain->dt_dma,
+			 SPAGE_SIZE, DMA_TO_DEVICE);
+	free_page((unsigned long)rk_domain->dt);
+
+	if (domain->type == IOMMU_DOMAIN_DMA)
+		iommu_put_dma_cookie(&rk_domain->domain);
+	kfree(rk_domain);
+}
+
 static struct iommu_device *rk_iommu_probe_device(struct device *dev)
 {
 	struct rk_iommudata *data;
@@ -1068,6 +1543,16 @@ static struct iommu_device *rk_iommu_probe_device(struct device *dev)
 	data->link = device_link_add(dev, iommu->dev,
 				     DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
 
+	data->defer_attach = false;
+
+	/* set max segment size for dev, needed for single chunk map */
+	if (!dev->dma_parms)
+		dev->dma_parms = kzalloc(sizeof(*dev->dma_parms), GFP_KERNEL);
+	if (!dev->dma_parms)
+		return ERR_PTR(-ENOMEM);
+
+	dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
+
 	return &iommu->iommu;
 }
 
@@ -1087,6 +1572,14 @@ static struct iommu_group *rk_iommu_device_group(struct device *dev)
 	return iommu_group_ref_get(iommu->group);
 }
 
+static bool rk_iommu_is_attach_deferred(struct iommu_domain *domain,
+					struct device *dev)
+{
+	struct rk_iommudata *data = dev_iommu_priv_get(dev);
+
+	return data->defer_attach;
+}
+
 static int rk_iommu_of_xlate(struct device *dev,
 			     struct of_phandle_args *args)
 {
@@ -1100,6 +1593,10 @@ static int rk_iommu_of_xlate(struct device *dev,
 	iommu_dev = of_find_device_by_node(args->np);
 
 	data->iommu = platform_get_drvdata(iommu_dev);
+
+	if (strstr(dev_name(dev), "vop"))
+		data->defer_attach = true;
+
 	dev_iommu_priv_set(dev, data);
 
 	platform_device_put(iommu_dev);
@@ -1107,21 +1604,90 @@ static int rk_iommu_of_xlate(struct device *dev,
 	return 0;
 }
 
-static const struct iommu_ops rk_iommu_ops = {
+void rk_iommu_mask_irq(struct device *dev)
+{
+	struct rk_iommu *iommu = rk_iommu_from_dev(dev);
+	int i;
+
+	if (!iommu)
+		return;
+
+	for (i = 0; i < iommu->num_mmu; i++)
+		rk_iommu_write(iommu->bases[i], RK_MMU_INT_MASK, 0);
+}
+EXPORT_SYMBOL(rk_iommu_mask_irq);
+
+void rk_iommu_unmask_irq(struct device *dev)
+{
+	struct rk_iommu *iommu = rk_iommu_from_dev(dev);
+	int i;
+
+	if (!iommu)
+		return;
+
+	for (i = 0; i < iommu->num_mmu; i++) {
+		/* Need to zap tlb in case of mapping during pagefault */
+		rk_iommu_base_command(iommu->bases[i], RK_MMU_CMD_ZAP_CACHE);
+		rk_iommu_write(iommu->bases[i], RK_MMU_INT_MASK, RK_MMU_IRQ_MASK);
+		/* Leave iommu in pagefault state until mapping finished */
+		rk_iommu_base_command(iommu->bases[i], RK_MMU_CMD_PAGE_FAULT_DONE);
+	}
+}
+EXPORT_SYMBOL(rk_iommu_unmask_irq);
+
+static struct iommu_ops rk_iommu_ops = {
 	.domain_alloc = rk_iommu_domain_alloc,
 	.domain_free = rk_iommu_domain_free,
 	.attach_dev = rk_iommu_attach_device,
 	.detach_dev = rk_iommu_detach_device,
 	.map = rk_iommu_map,
 	.unmap = rk_iommu_unmap,
+	.flush_iotlb_all = rk_iommu_flush_tlb_all,
 	.probe_device = rk_iommu_probe_device,
 	.release_device = rk_iommu_release_device,
 	.iova_to_phys = rk_iommu_iova_to_phys,
+	.is_attach_deferred = rk_iommu_is_attach_deferred,
 	.device_group = rk_iommu_device_group,
 	.pgsize_bitmap = RK_IOMMU_PGSIZE_BITMAP,
 	.of_xlate = rk_iommu_of_xlate,
 };
 
+static struct iommu_ops rk_iommu_ops_v2 = {
+	.domain_alloc = rk_iommu_domain_alloc,
+	.domain_free = rk_iommu_domain_free_v2,
+	.attach_dev = rk_iommu_attach_device,
+	.detach_dev = rk_iommu_detach_device,
+	.map = rk_iommu_map_v2,
+	.unmap = rk_iommu_unmap_v2,
+	.flush_iotlb_all = rk_iommu_flush_tlb_all,
+	.probe_device = rk_iommu_probe_device,
+	.release_device = rk_iommu_release_device,
+	.iova_to_phys = rk_iommu_iova_to_phys_v2,
+	.is_attach_deferred = rk_iommu_is_attach_deferred,
+	.device_group = rk_iommu_device_group,
+	.pgsize_bitmap = RK_IOMMU_PGSIZE_BITMAP,
+	.of_xlate = rk_iommu_of_xlate,
+};
+
+static const struct rockchip_iommu_data iommu_data_v1 = {
+	.version = 0x1,
+};
+
+static const struct rockchip_iommu_data iommu_data_v2 = {
+	.version = 0x2,
+};
+
+static const struct of_device_id rk_iommu_dt_ids[] = {
+	{
+		.compatible = "rockchip,iommu",
+		.data = &iommu_data_v1,
+	}, {
+		.compatible = "rockchip,iommu-v2",
+		.data = &iommu_data_v2,
+	},
+	{ /* sentinel */ }
+};
+
 static int rk_iommu_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -1129,11 +1695,21 @@ static int rk_iommu_probe(struct platform_device *pdev)
 	struct resource *res;
 	int num_res = pdev->num_resources;
 	int err, i;
+	const struct of_device_id *match;
+	struct rockchip_iommu_data *data;
 
 	iommu = devm_kzalloc(dev, sizeof(*iommu), GFP_KERNEL);
 	if (!iommu)
 		return -ENOMEM;
 
+	match = of_match_device(rk_iommu_dt_ids, dev);
+	if (!match)
+		return -EINVAL;
+
+	data = (struct rockchip_iommu_data *)match->data;
+	iommu->version = data->version;
+	dev_info(dev, "version = %x\n", iommu->version);
+
 	platform_set_drvdata(pdev, iommu);
 	iommu->dev = dev;
 	iommu->num_mmu = 0;
@@ -1161,26 +1737,30 @@ static int rk_iommu_probe(struct platform_device *pdev)
 
 	iommu->reset_disabled = device_property_read_bool(dev,
 					"rockchip,disable-mmu-reset");
-
-	iommu->num_clocks = ARRAY_SIZE(rk_iommu_clocks);
-	iommu->clocks = devm_kcalloc(iommu->dev, iommu->num_clocks,
-				     sizeof(*iommu->clocks), GFP_KERNEL);
-	if (!iommu->clocks)
-		return -ENOMEM;
-
-	for (i = 0; i < iommu->num_clocks; ++i)
-		iommu->clocks[i].id = rk_iommu_clocks[i];
+	iommu->skip_read = device_property_read_bool(dev,
+					"rockchip,skip-mmu-read");
+	iommu->dlr_disable = device_property_read_bool(dev,
+					"rockchip,disable-device-link-resume");
+	iommu->shootdown_entire = device_property_read_bool(dev,
+					"rockchip,shootdown-entire");
+
+	if (of_machine_is_compatible("rockchip,rv1126") ||
+	    of_machine_is_compatible("rockchip,rv1109"))
+		iommu->cmd_retry = device_property_read_bool(dev,
+					"rockchip,enable-cmd-retry");
 
 	/*
 	 * iommu clocks should be present for all new devices and devicetrees
 	 * but there are older devicetrees without clocks out in the wild.
 	 * So clocks as optional for the time being.
 	 */
-	err = devm_clk_bulk_get(iommu->dev, iommu->num_clocks, iommu->clocks);
+	err = devm_clk_bulk_get_all(dev, &iommu->clocks);
 	if (err == -ENOENT)
 		iommu->num_clocks = 0;
-	else if (err)
+	else if (err < 0)
 		return err;
+	else
+		iommu->num_clocks = err;
 
 	err = clk_bulk_prepare(iommu->num_clocks, iommu->clocks);
 	if (err)
@@ -1196,7 +1776,10 @@ static int rk_iommu_probe(struct platform_device *pdev)
 	if (err)
 		goto err_put_group;
 
-	iommu_device_set_ops(&iommu->iommu, &rk_iommu_ops);
+	if (iommu->version >= 0x2)
+		iommu_device_set_ops(&iommu->iommu, &rk_iommu_ops_v2);
+	else
+		iommu_device_set_ops(&iommu->iommu, &rk_iommu_ops);
 	iommu_device_set_fwnode(&iommu->iommu, &dev->of_node->fwnode);
 
 	err = iommu_device_register(&iommu->iommu);
@@ -1211,10 +1794,16 @@ static int rk_iommu_probe(struct platform_device *pdev)
 	if (!dma_dev)
 		dma_dev = &pdev->dev;
 
-	bus_set_iommu(&platform_bus_type, &rk_iommu_ops);
+	if (iommu->version >= 0x2)
+		bus_set_iommu(&platform_bus_type, &rk_iommu_ops_v2);
+	else
+		bus_set_iommu(&platform_bus_type, &rk_iommu_ops);
 
 	pm_runtime_enable(dev);
 
+	if (iommu->skip_read)
+		goto skip_request_irq;
+
 	for (i = 0; i < iommu->num_irq; i++) {
 		int irq = platform_get_irq(pdev, i);
 
@@ -1229,6 +1818,7 @@ static int rk_iommu_probe(struct platform_device *pdev)
 		}
 	}
 
+skip_request_irq:
 	return 0;
 err_remove_sysfs:
 	iommu_device_sysfs_remove(&iommu->iommu);
@@ -1260,6 +1850,9 @@ static int __maybe_unused rk_iommu_suspend(struct device *dev)
 	if (!iommu->domain)
 		return 0;
 
+	if (iommu->dlr_disable)
+		return 0;
+
 	rk_iommu_disable(iommu);
 	return 0;
 }
@@ -1271,6 +1864,9 @@ static int __maybe_unused rk_iommu_resume(struct device *dev)
 	if (!iommu->domain)
 		return 0;
 
+	if (iommu->dlr_disable)
+		return 0;
+
 	return rk_iommu_enable(iommu);
 }
 
@@ -1280,11 +1876,6 @@ static const struct dev_pm_ops rk_iommu_pm_ops = {
 				pm_runtime_force_resume)
 };
 
-static const struct of_device_id rk_iommu_dt_ids[] = {
-	{ .compatible = "rockchip,iommu" },
-	{ /* sentinel */ }
-};
-
 static struct platform_driver rk_iommu_driver = {
 	.probe = rk_iommu_probe,
 	.shutdown = rk_iommu_shutdown,
@@ -1301,3 +1892,8 @@ static int __init rk_iommu_init(void)
 	return platform_driver_register(&rk_iommu_driver);
 }
 subsys_initcall(rk_iommu_init);
+
+MODULE_DESCRIPTION("IOMMU API for Rockchip");
+MODULE_AUTHOR("Simon Xue <xxm@rock-chips.com> and Daniel Kurtz <djkurtz@chromium.org>");
+MODULE_ALIAS("platform:rockchip-iommu");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index d8cb5bcd6..ed18e141e 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -2167,6 +2167,8 @@ static struct page *its_allocate_prop_table(gfp_t gfp_flags)
 {
 	struct page *prop_page;
 
+	if (of_machine_is_compatible("rockchip,rk3568") || of_machine_is_compatible("rockchip,rk3566"))
+		gfp_flags |= GFP_DMA32;
 	prop_page = alloc_pages(gfp_flags, get_order(LPI_PROPBASE_SZ));
 	if (!prop_page)
 		return NULL;
@@ -2290,6 +2292,7 @@ static int its_setup_baser(struct its_node *its, struct its_baser *baser,
 	u32 alloc_pages, psz;
 	struct page *page;
 	void *base;
+	gfp_t gfp_flags;
 
 	psz = baser->psz;
 	alloc_pages = (PAGE_ORDER_TO_SIZE(order) / psz);
@@ -2301,7 +2304,10 @@ static int its_setup_baser(struct its_node *its, struct its_baser *baser,
 		order = get_order(GITS_BASER_PAGES_MAX * psz);
 	}
 
-	page = alloc_pages_node(its->numa_node, GFP_KERNEL | __GFP_ZERO, order);
+	gfp_flags = GFP_KERNEL | __GFP_ZERO;
+	if (of_machine_is_compatible("rockchip,rk3568") || of_machine_is_compatible("rockchip,rk3566"))
+		gfp_flags |= GFP_DMA32;
+	page = alloc_pages_node(its->numa_node, gfp_flags, order);
 	if (!page)
 		return -ENOMEM;
 
@@ -2348,6 +2354,16 @@ static int its_setup_baser(struct its_node *its, struct its_baser *baser,
 	its_write_baser(its, baser, val);
 	tmp = baser->val;
 
+	if (IS_ENABLED(CONFIG_NO_GKI) &&
+	    (of_machine_is_compatible("rockchip,rk3568") ||
+	     of_machine_is_compatible("rockchip,rk3566") ||
+	     of_machine_is_compatible("rockchip,rk3588"))) {
+		if (tmp & GITS_BASER_SHAREABILITY_MASK)
+			tmp &= ~GITS_BASER_SHAREABILITY_MASK;
+		else
+			gic_flush_dcache_to_poc(base, PAGE_ORDER_TO_SIZE(order));
+	}
+
 	if ((val ^ tmp) & GITS_BASER_SHAREABILITY_MASK) {
 		/*
 		 * Shareability didn't stick. Just use
@@ -2930,6 +2946,8 @@ static struct page *its_allocate_pending_table(gfp_t gfp_flags)
 {
 	struct page *pend_page;
 
+	if (of_machine_is_compatible("rockchip,rk3568") || of_machine_is_compatible("rockchip,rk3566"))
+		gfp_flags |= GFP_DMA32;
 	pend_page = alloc_pages(gfp_flags | __GFP_ZERO,
 				get_order(LPI_PENDBASE_SZ));
 	if (!pend_page)
@@ -3077,6 +3095,12 @@ static void its_cpu_init_lpis(void)
 	gicr_write_propbaser(val, rbase + GICR_PROPBASER);
 	tmp = gicr_read_propbaser(rbase + GICR_PROPBASER);
 
+	if (IS_ENABLED(CONFIG_NO_GKI) &&
+	    (of_machine_is_compatible("rockchip,rk3568") ||
+	     of_machine_is_compatible("rockchip,rk3566") ||
+	     of_machine_is_compatible("rockchip,rk3588")))
+		tmp &= ~GICR_PROPBASER_SHAREABILITY_MASK;
+
 	if ((tmp ^ val) & GICR_PROPBASER_SHAREABILITY_MASK) {
 		if (!(tmp & GICR_PROPBASER_SHAREABILITY_MASK)) {
 			/*
@@ -3101,6 +3125,12 @@ static void its_cpu_init_lpis(void)
 	gicr_write_pendbaser(val, rbase + GICR_PENDBASER);
 	tmp = gicr_read_pendbaser(rbase + GICR_PENDBASER);
 
+	if (IS_ENABLED(CONFIG_NO_GKI) &&
+	    (of_machine_is_compatible("rockchip,rk3568") ||
+	     of_machine_is_compatible("rockchip,rk3566") ||
+	     of_machine_is_compatible("rockchip,rk3588")))
+		tmp &= ~GICR_PENDBASER_SHAREABILITY_MASK;
+
 	if (!(tmp & GICR_PENDBASER_SHAREABILITY_MASK)) {
 		/*
 		 * The HW reports non-shareable, we must remove the
@@ -3263,7 +3293,11 @@ static bool its_alloc_table_entry(struct its_node *its,
 
 	/* Allocate memory for 2nd level table */
 	if (!table[idx]) {
-		page = alloc_pages_node(its->numa_node, GFP_KERNEL | __GFP_ZERO,
+		gfp_t gfp_flags = GFP_KERNEL | __GFP_ZERO;
+
+		if (of_machine_is_compatible("rockchip,rk3568") || of_machine_is_compatible("rockchip,rk3566"))
+			gfp_flags |= GFP_DMA32;
+		page = alloc_pages_node(its->numa_node, gfp_flags,
 					get_order(baser->psz));
 		if (!page)
 			return false;
@@ -3352,6 +3386,7 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
 	int nr_lpis;
 	int nr_ites;
 	int sz;
+	gfp_t gfp_flags;
 
 	if (!its_alloc_device_table(its, dev_id))
 		return NULL;
@@ -3367,7 +3402,10 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id,
 	nr_ites = max(2, nvecs);
 	sz = nr_ites * (FIELD_GET(GITS_TYPER_ITT_ENTRY_SIZE, its->typer) + 1);
 	sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1;
-	itt = kzalloc_node(sz, GFP_KERNEL, its->numa_node);
+	gfp_flags = GFP_KERNEL;
+	if (of_machine_is_compatible("rockchip,rk3568") || of_machine_is_compatible("rockchip,rk3566"))
+		gfp_flags |= GFP_DMA32;
+	itt = kzalloc_node(sz, gfp_flags, its->numa_node);
 	if (alloc_lpis) {
 		lpi_map = its_lpi_alloc(nvecs, &lpi_base, &nr_lpis);
 		if (lpi_map)
@@ -3841,8 +3879,6 @@ static void its_vpe_schedule(struct its_vpe *vpe)
 	val |= vpe->idai ? GICR_VPENDBASER_IDAI : 0;
 	val |= GICR_VPENDBASER_Valid;
 	gicr_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
-
-	its_wait_vpt_parse_complete();
 }
 
 static void its_vpe_deschedule(struct its_vpe *vpe)
@@ -3890,6 +3926,10 @@ static int its_vpe_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
 		its_vpe_deschedule(vpe);
 		return 0;
 
+	case COMMIT_VPE:
+		its_wait_vpt_parse_complete();
+		return 0;
+
 	case INVALL_VPE:
 		its_vpe_invall(vpe);
 		return 0;
@@ -4051,8 +4091,6 @@ static void its_vpe_4_1_schedule(struct its_vpe *vpe,
 	val |= FIELD_PREP(GICR_VPENDBASER_4_1_VPEID, vpe->vpe_id);
 
 	gicr_write_vpendbaser(val, vlpi_base + GICR_VPENDBASER);
-
-	its_wait_vpt_parse_complete();
 }
 
 static void its_vpe_4_1_deschedule(struct its_vpe *vpe,
@@ -4127,6 +4165,10 @@ static int its_vpe_4_1_set_vcpu_affinity(struct irq_data *d, void *vcpu_info)
 		its_vpe_4_1_deschedule(vpe, info);
 		return 0;
 
+	case COMMIT_VPE:
+		its_wait_vpt_parse_complete();
+		return 0;
+
 	case INVALL_VPE:
 		its_vpe_4_1_invall(vpe);
 		return 0;
@@ -4489,7 +4531,7 @@ static int its_vpe_irq_domain_alloc(struct irq_domain *domain, unsigned int virq
 
 	if (err) {
 		if (i > 0)
-			its_vpe_irq_domain_free(domain, virq, i);
+			its_vpe_irq_domain_free(domain, virq, i - 1);
 
 		its_lpi_free(bitmap, base, nr_ids);
 		its_free_prop_table(vprop_page);
@@ -4945,6 +4987,7 @@ static int __init its_probe_one(struct resource *res,
 	u64 baser, tmp, typer;
 	struct page *page;
 	int err;
+	gfp_t gfp_flags;
 
 	its_base = ioremap(res->start, SZ_64K);
 	if (!its_base) {
@@ -5013,7 +5056,10 @@ static int __init its_probe_one(struct resource *res,
 
 	its->numa_node = numa_node;
 
-	page = alloc_pages_node(its->numa_node, GFP_KERNEL | __GFP_ZERO,
+	gfp_flags = GFP_KERNEL | __GFP_ZERO;
+	if (of_machine_is_compatible("rockchip,rk3568") || of_machine_is_compatible("rockchip,rk3566"))
+		gfp_flags |= GFP_DMA32;
+	page = alloc_pages_node(its->numa_node, gfp_flags,
 				get_order(ITS_CMD_QUEUE_SZ));
 	if (!page) {
 		err = -ENOMEM;
@@ -5044,6 +5090,12 @@ static int __init its_probe_one(struct resource *res,
 	gits_write_cbaser(baser, its->base + GITS_CBASER);
 	tmp = gits_read_cbaser(its->base + GITS_CBASER);
 
+	if (IS_ENABLED(CONFIG_NO_GKI) &&
+	    (of_machine_is_compatible("rockchip,rk3568") ||
+	     of_machine_is_compatible("rockchip,rk3566") ||
+	     of_machine_is_compatible("rockchip,rk3588")))
+		tmp &= ~GITS_CBASER_SHAREABILITY_MASK;
+
 	if ((tmp ^ baser) & GITS_CBASER_SHAREABILITY_MASK) {
 		if (!(tmp & GITS_CBASER_SHAREABILITY_MASK)) {
 			/*
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 4c8f18f0c..bafdbb18e 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -18,6 +18,8 @@
 #include <linux/percpu.h>
 #include <linux/refcount.h>
 #include <linux/slab.h>
+#include <linux/syscore_ops.h>
+#include <linux/wakeup_reason.h>
 
 #include <linux/irqchip.h>
 #include <linux/irqchip/arm-gic-common.h>
@@ -42,20 +44,7 @@ struct redist_region {
 	void __iomem		*redist_base;
 	phys_addr_t		phys_base;
 	bool			single_redist;
-};
 
-struct gic_chip_data {
-	struct fwnode_handle	*fwnode;
-	void __iomem		*dist_base;
-	struct redist_region	*redist_regions;
-	struct rdists		rdists;
-	struct irq_domain	*domain;
-	u64			redist_stride;
-	u32			nr_redist_regions;
-	u64			flags;
-	bool			has_rss;
-	unsigned int		ppi_nr;
-	struct partition_desc	**ppi_descs;
 };
 
 static struct gic_chip_data gic_data __read_mostly;
@@ -725,6 +714,7 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs
 
 	if (handle_domain_irq(gic_data.domain, irqnr, regs)) {
 		WARN_ONCE(true, "Unexpected interrupt received!\n");
+		log_abnormal_wakeup_reason("unexpected HW IRQ %u", irqnr);
 		gic_deactivate_unhandled(irqnr);
 	}
 }
@@ -915,22 +905,6 @@ static int __gic_update_rdist_properties(struct redist_region *region,
 {
 	u64 typer = gic_read_typer(ptr + GICR_TYPER);
 
-	/* Boot-time cleanip */
-	if ((typer & GICR_TYPER_VLPIS) && (typer & GICR_TYPER_RVPEID)) {
-		u64 val;
-
-		/* Deactivate any present vPE */
-		val = gicr_read_vpendbaser(ptr + SZ_128K + GICR_VPENDBASER);
-		if (val & GICR_VPENDBASER_Valid)
-			gicr_write_vpendbaser(GICR_VPENDBASER_PendingLast,
-					      ptr + SZ_128K + GICR_VPENDBASER);
-
-		/* Mark the VPE table as invalid */
-		val = gicr_read_vpropbaser(ptr + SZ_128K + GICR_VPROPBASER);
-		val &= ~GICR_VPROPBASER_4_1_VALID;
-		gicr_write_vpropbaser(val, ptr + SZ_128K + GICR_VPROPBASER);
-	}
-
 	gic_data.rdists.has_vlpis &= !!(typer & GICR_TYPER_VLPIS);
 
 	/* RVPEID implies some form of DirectLPI, no matter what the doc says... :-/ */
@@ -1324,6 +1298,22 @@ static void gic_cpu_pm_init(void)
 #else
 static inline void gic_cpu_pm_init(void) { }
 #endif /* CONFIG_CPU_PM */
+#ifdef CONFIG_PM
+void gic_resume(void)
+{
+}
+EXPORT_SYMBOL_GPL(gic_resume);
+static struct syscore_ops gic_syscore_ops = {
+	.resume = gic_resume,
+};
+static void gic_syscore_init(void)
+{
+	register_syscore_ops(&gic_syscore_ops);
+}
+#else
+static inline void gic_syscore_init(void) { }
+void gic_resume(void) { }
+#endif
 
 static struct irq_chip gic_chip = {
 	.name			= "GICv3",
@@ -1787,6 +1777,7 @@ static int __init gic_init_bases(void __iomem *dist_base,
 	gic_cpu_init();
 	gic_smp_init();
 	gic_cpu_pm_init();
+	gic_syscore_init();
 
 	if (gic_dist_supports_lpis()) {
 		its_init(handle, &gic_data.rdists, gic_data.domain);
diff --git a/drivers/irqchip/irq-gic-v4.c b/drivers/irqchip/irq-gic-v4.c
index 0c18714ae..5d1dc9915 100644
--- a/drivers/irqchip/irq-gic-v4.c
+++ b/drivers/irqchip/irq-gic-v4.c
@@ -232,6 +232,8 @@ int its_make_vpe_non_resident(struct its_vpe *vpe, bool db)
 	if (!ret)
 		vpe->resident = false;
 
+	vpe->ready = false;
+
 	return ret;
 }
 
@@ -258,6 +260,23 @@ int its_make_vpe_resident(struct its_vpe *vpe, bool g0en, bool g1en)
 	return ret;
 }
 
+int its_commit_vpe(struct its_vpe *vpe)
+{
+	struct its_cmd_info info = {
+		.cmd_type = COMMIT_VPE,
+	};
+	int ret;
+
+	WARN_ON(preemptible());
+
+	ret = its_send_vpe_cmd(vpe, &info);
+	if (!ret)
+		vpe->ready = true;
+
+	return ret;
+}
+
+
 int its_invall_vpe(struct its_vpe *vpe)
 {
 	struct its_cmd_info info = {
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 7e152bbb4..119e8c2e8 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -211,6 +211,7 @@ config VIDEO_MEDIATEK_JPEG
 	depends on MTK_IOMMU_V1 || MTK_IOMMU || COMPILE_TEST
 	depends on VIDEO_DEV && VIDEO_V4L2
 	depends on ARCH_MEDIATEK || COMPILE_TEST
+	depends on MTK_SMI || (COMPILE_TEST && MTK_SMI=n)
 	select VIDEOBUF2_DMA_CONTIG
 	select V4L2_MEM2MEM_DEV
 	help
@@ -238,6 +239,7 @@ config VIDEO_MEDIATEK_MDP
 	depends on MTK_IOMMU || COMPILE_TEST
 	depends on VIDEO_DEV && VIDEO_V4L2
 	depends on ARCH_MEDIATEK || COMPILE_TEST
+	depends on MTK_SMI || (COMPILE_TEST && MTK_SMI=n)
 	select VIDEOBUF2_DMA_CONTIG
 	select V4L2_MEM2MEM_DEV
 	select VIDEO_MEDIATEK_VPU
@@ -258,6 +260,7 @@ config VIDEO_MEDIATEK_VCODEC
 	# our dependencies, to avoid missing symbols during link.
 	depends on VIDEO_MEDIATEK_VPU || !VIDEO_MEDIATEK_VPU
 	depends on MTK_SCP || !MTK_SCP
+	depends on MTK_SMI || (COMPILE_TEST && MTK_SMI=n)
 	select VIDEOBUF2_DMA_CONTIG
 	select V4L2_MEM2MEM_DEV
 	select VIDEO_MEDIATEK_VCODEC_VPU if VIDEO_MEDIATEK_VPU
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 62b6cdc8c..df76a5ca5 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_VIDEO_RENESAS_VSP1)	+= vsp1/
 
 obj-$(CONFIG_VIDEO_ROCKCHIP_RGA)	+= rockchip/rga/
 
+
 obj-y	+= omap/
 
 obj-$(CONFIG_VIDEO_AM437X_VPFE)		+= am437x/
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index 282f3d238..5f3f90123 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -12,6 +12,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/usb.h>
+#include <linux/usb/quirks.h>
 #include <linux/videodev2.h>
 #include <linux/vmalloc.h>
 #include <linux/wait.h>
@@ -2341,7 +2342,11 @@ static int uvc_probe(struct usb_interface *intf,
 	}
 
 	uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
-	usb_enable_autosuspend(udev);
+	if (udev->quirks & USB_QUIRK_AUTO_SUSPEND ||
+	    udev->parent->quirks & USB_QUIRK_AUTO_SUSPEND)
+		uvc_printk(KERN_INFO, "auto-suspend is blacklisted for this device\n");
+	else
+		usb_enable_autosuspend(udev);
 	return 0;
 
 error:
diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c
index 33babe6e8..1b5d4d99e 100644
--- a/drivers/media/v4l2-core/v4l2-async.c
+++ b/drivers/media/v4l2-core/v4l2-async.c
@@ -555,6 +555,60 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
 }
 EXPORT_SYMBOL(v4l2_async_notifier_register);
 
+#if IS_ENABLED(CONFIG_NO_GKI)
+static int __v4l2_async_notifier_clr_unready_dev(
+	struct v4l2_async_notifier *notifier)
+{
+	struct v4l2_subdev *sd, *tmp;
+	int clr_num = 0;
+
+	list_for_each_entry_safe(sd, tmp, &notifier->done, async_list) {
+		struct v4l2_async_notifier *subdev_notifier =
+			v4l2_async_find_subdev_notifier(sd);
+
+		if (subdev_notifier)
+			clr_num += __v4l2_async_notifier_clr_unready_dev(
+					subdev_notifier);
+	}
+
+	list_for_each_entry_safe(sd, tmp, &notifier->waiting, async_list) {
+		list_del_init(&sd->async_list);
+		sd->asd = NULL;
+		sd->dev = NULL;
+		clr_num++;
+	}
+
+	return clr_num;
+}
+
+int v4l2_async_notifier_clr_unready_dev(struct v4l2_async_notifier *notifier)
+{
+	int ret = 0;
+	int clr_num = 0;
+
+	mutex_lock(&list_lock);
+
+	while (notifier->parent)
+		notifier = notifier->parent;
+
+	if (!notifier->v4l2_dev)
+		goto out;
+
+	clr_num = __v4l2_async_notifier_clr_unready_dev(notifier);
+	dev_info(notifier->v4l2_dev->dev,
+		 "clear unready subdev num: %d\n", clr_num);
+
+	if (clr_num > 0)
+		ret = v4l2_async_notifier_try_complete(notifier);
+
+out:
+	mutex_unlock(&list_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(v4l2_async_notifier_clr_unready_dev);
+#endif
+
 int v4l2_async_subdev_notifier_register(struct v4l2_subdev *sd,
 					struct v4l2_async_notifier *notifier)
 {
diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c
index d109b9f14..fd755d98a 100644
--- a/drivers/mfd/rk808.c
+++ b/drivers/mfd/rk808.c
@@ -2,7 +2,7 @@
 /*
  * MFD core driver for Rockchip RK808/RK818
  *
- * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
+ * Copyright (c) 2014-2018, Fuzhou Rockchip Electronics Co., Ltd
  *
  * Author: Chris Zhong <zyw@rock-chips.com>
  * Author: Zhang Qing <zhangqing@rock-chips.com>
@@ -18,7 +18,11 @@
 #include <linux/mfd/core.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
+#include <linux/reboot.h>
 #include <linux/regmap.h>
+#include <linux/syscore_ops.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/devinfo.h>
 
 struct rk808_reg_data {
 	int addr;
@@ -65,22 +69,61 @@ static bool rk817_is_volatile_reg(struct device *dev, unsigned int reg)
 	switch (reg) {
 	case RK817_SECONDS_REG ... RK817_WEEKS_REG:
 	case RK817_RTC_STATUS_REG:
+	case RK817_ADC_CONFIG0 ... RK817_CURE_ADC_K0:
+	case RK817_CHRG_STS:
+	case RK817_CHRG_OUT:
+	case RK817_CHRG_IN:
+	case RK817_SYS_STS:
 	case RK817_INT_STS_REG0:
 	case RK817_INT_STS_REG1:
 	case RK817_INT_STS_REG2:
-	case RK817_SYS_STS:
 		return true;
 	}
 
-	return true;
+	return false;
+}
+
+static bool rk818_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+	/*
+	 * Notes:
+	 * - Technically the ROUND_30s bit makes RTC_CTRL_REG volatile, but
+	 *   we don't use that feature.  It's better to cache.
+	 * - It's unlikely we care that RK808_DEVCTRL_REG is volatile since
+	 *   bits are cleared in case when we shutoff anyway, but better safe.
+	 */
+
+	switch (reg) {
+	case RK808_SECONDS_REG ... RK808_WEEKS_REG:
+	case RK808_RTC_STATUS_REG:
+	case RK808_VB_MON_REG:
+	case RK808_THERMAL_REG:
+	case RK808_DCDC_EN_REG:
+	case RK808_LDO_EN_REG:
+	case RK808_DCDC_UV_STS_REG:
+	case RK808_LDO_UV_STS_REG:
+	case RK808_DCDC_PG_REG:
+	case RK808_LDO_PG_REG:
+	case RK808_DEVCTRL_REG:
+	case RK808_INT_STS_REG1:
+	case RK808_INT_STS_REG2:
+	case RK808_INT_STS_MSK_REG1:
+	case RK808_INT_STS_MSK_REG2:
+	case RK816_INT_STS_REG1:
+	case RK816_INT_STS_MSK_REG1:
+	case RK818_SUP_STS_REG ... RK818_SAVE_DATA19:
+		return true;
+	}
+
+	return false;
 }
 
 static const struct regmap_config rk818_regmap_config = {
 	.reg_bits = 8,
 	.val_bits = 8,
-	.max_register = RK818_USB_CTRL_REG,
+	.max_register = RK818_SAVE_DATA19,
 	.cache_type = REGCACHE_RBTREE,
-	.volatile_reg = rk808_is_volatile_reg,
+	.volatile_reg = rk818_is_volatile_reg,
 };
 
 static const struct regmap_config rk805_regmap_config = {
@@ -99,11 +142,20 @@ static const struct regmap_config rk808_regmap_config = {
 	.volatile_reg = rk808_is_volatile_reg,
 };
 
+static const struct regmap_config rk816_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = RK816_DATA18_REG,
+	.cache_type = REGCACHE_RBTREE,
+	.volatile_reg = rk818_is_volatile_reg,
+};
+
 static const struct regmap_config rk817_regmap_config = {
 	.reg_bits = 8,
 	.val_bits = 8,
 	.max_register = RK817_GPIO_INT_CFG,
-	.cache_type = REGCACHE_NONE,
+	.num_reg_defaults_raw = RK817_GPIO_INT_CFG + 1,
+	.cache_type = REGCACHE_RBTREE,
 	.volatile_reg = rk817_is_volatile_reg,
 };
 
@@ -111,18 +163,27 @@ static struct resource rtc_resources[] = {
 	DEFINE_RES_IRQ(RK808_IRQ_RTC_ALARM),
 };
 
+static struct resource rk816_rtc_resources[] = {
+	DEFINE_RES_IRQ(RK816_IRQ_RTC_ALARM),
+};
+
 static struct resource rk817_rtc_resources[] = {
 	DEFINE_RES_IRQ(RK817_IRQ_RTC_ALARM),
 };
 
 static struct resource rk805_key_resources[] = {
-	DEFINE_RES_IRQ(RK805_IRQ_PWRON_RISE),
 	DEFINE_RES_IRQ(RK805_IRQ_PWRON_FALL),
+	DEFINE_RES_IRQ(RK805_IRQ_PWRON_RISE),
+};
+
+static struct resource rk816_pwrkey_resources[] = {
+	DEFINE_RES_IRQ(RK816_IRQ_PWRON_FALL),
+	DEFINE_RES_IRQ(RK816_IRQ_PWRON_RISE),
 };
 
 static struct resource rk817_pwrkey_resources[] = {
-	DEFINE_RES_IRQ(RK817_IRQ_PWRON_RISE),
 	DEFINE_RES_IRQ(RK817_IRQ_PWRON_FALL),
+	DEFINE_RES_IRQ(RK817_IRQ_PWRON_RISE),
 };
 
 static const struct mfd_cell rk805s[] = {
@@ -150,9 +211,28 @@ static const struct mfd_cell rk808s[] = {
 	},
 };
 
+static const struct mfd_cell rk816s[] = {
+	{ .name = "rk808-clkout", },
+	{ .name = "rk808-regulator", },
+	{ .name = "rk805-pinctrl", },
+	{ .name = "rk816-battery", .of_compatible = "rk816-battery", },
+	{
+		.name = "rk805-pwrkey",
+		.num_resources = ARRAY_SIZE(rk816_pwrkey_resources),
+		.resources = &rk816_pwrkey_resources[0],
+	},
+	{
+		.name = "rk808-rtc",
+		.num_resources = ARRAY_SIZE(rk816_rtc_resources),
+		.resources = &rk816_rtc_resources[0],
+	},
+};
+
 static const struct mfd_cell rk817s[] = {
 	{ .name = "rk808-clkout",},
 	{ .name = "rk808-regulator",},
+	{ .name = "rk817-battery", .of_compatible = "rk817,battery", },
+	{ .name = "rk817-charger", .of_compatible = "rk817,charger", },
 	{
 		.name = "rk805-pwrkey",
 		.num_resources = ARRAY_SIZE(rk817_pwrkey_resources),
@@ -163,11 +243,17 @@ static const struct mfd_cell rk817s[] = {
 		.num_resources = ARRAY_SIZE(rk817_rtc_resources),
 		.resources = &rk817_rtc_resources[0],
 	},
+	{
+		.name = "rk817-codec",
+		.of_compatible = "rockchip,rk817-codec",
+	},
 };
 
 static const struct mfd_cell rk818s[] = {
 	{ .name = "rk808-clkout", },
 	{ .name = "rk808-regulator", },
+	{ .name = "rk818-battery", .of_compatible = "rk818-battery", },
+	{ .name = "rk818-charger", },
 	{
 		.name = "rk808-rtc",
 		.num_resources = ARRAY_SIZE(rtc_resources),
@@ -176,16 +262,18 @@ static const struct mfd_cell rk818s[] = {
 };
 
 static const struct rk808_reg_data rk805_pre_init_reg[] = {
-	{RK805_BUCK1_CONFIG_REG, RK805_BUCK1_2_ILMAX_MASK,
-				 RK805_BUCK1_2_ILMAX_4000MA},
-	{RK805_BUCK2_CONFIG_REG, RK805_BUCK1_2_ILMAX_MASK,
-				 RK805_BUCK1_2_ILMAX_4000MA},
-	{RK805_BUCK3_CONFIG_REG, RK805_BUCK3_4_ILMAX_MASK,
-				 RK805_BUCK3_ILMAX_3000MA},
-	{RK805_BUCK4_CONFIG_REG, RK805_BUCK3_4_ILMAX_MASK,
-				 RK805_BUCK4_ILMAX_3500MA},
 	{RK805_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK, BUCK_ILMIN_400MA},
+	{RK805_GPIO_IO_POL_REG, SLP_SD_MSK, SLEEP_FUN},
 	{RK805_THERMAL_REG, TEMP_HOTDIE_MSK, TEMP115C},
+	{RK808_RTC_CTRL_REG, RTC_STOP, RTC_STOP},
+};
+
+static struct rk808_reg_data rk805_suspend_reg[] = {
+	{RK805_BUCK3_CONFIG_REG, PWM_MODE_MSK, AUTO_PWM_MODE},
+};
+
+static struct rk808_reg_data rk805_resume_reg[] = {
+	{RK805_BUCK3_CONFIG_REG, PWM_MODE_MSK, FPWM_MODE},
 };
 
 static const struct rk808_reg_data rk808_pre_init_reg[] = {
@@ -195,11 +283,40 @@ static const struct rk808_reg_data rk808_pre_init_reg[] = {
 	{ RK808_BUCK1_CONFIG_REG, BUCK1_RATE_MASK,  BUCK_ILMIN_200MA },
 	{ RK808_BUCK2_CONFIG_REG, BUCK2_RATE_MASK,  BUCK_ILMIN_200MA },
 	{ RK808_DCDC_UV_ACT_REG,  BUCK_UV_ACT_MASK, BUCK_UV_ACT_DISABLE},
+	{ RK808_RTC_CTRL_REG, RTC_STOP, RTC_STOP},
 	{ RK808_VB_MON_REG,       MASK_ALL,         VB_LO_ACT |
 						    VB_LO_SEL_3500MV },
 };
 
+static const struct rk808_reg_data rk816_pre_init_reg[] = {
+	/* buck4 Max ILMIT*/
+	{ RK816_BUCK4_CONFIG_REG, REG_WRITE_MSK, BUCK4_MAX_ILIMIT },
+	/* hotdie temperature: 105c*/
+	{ RK816_THERMAL_REG, REG_WRITE_MSK, TEMP105C },
+	/* set buck 12.5mv/us */
+	{ RK816_BUCK1_CONFIG_REG, BUCK_RATE_MSK, BUCK_RATE_12_5MV_US },
+	{ RK816_BUCK2_CONFIG_REG, BUCK_RATE_MSK, BUCK_RATE_12_5MV_US },
+	/* enable RTC_PERIOD & RTC_ALARM int */
+	{ RK816_INT_STS_MSK_REG2, REG_WRITE_MSK, RTC_PERIOD_ALARM_INT_EN },
+	/* set bat 3.0 low and act shutdown */
+	{ RK816_VB_MON_REG, VBAT_LOW_VOL_MASK | VBAT_LOW_ACT_MASK,
+	  RK816_VBAT_LOW_3V0 | EN_VABT_LOW_SHUT_DOWN },
+	/* enable PWRON rising/faling int */
+	{ RK816_INT_STS_MSK_REG1, REG_WRITE_MSK, RK816_PWRON_FALL_RISE_INT_EN },
+	/* enable PLUG IN/OUT int */
+	{ RK816_INT_STS_MSK_REG3, REG_WRITE_MSK, PLUGIN_OUT_INT_EN },
+	/* clear int flags */
+	{ RK816_INT_STS_REG1, REG_WRITE_MSK, ALL_INT_FLAGS_ST },
+	{ RK816_INT_STS_REG2, REG_WRITE_MSK, ALL_INT_FLAGS_ST },
+	{ RK816_INT_STS_REG3, REG_WRITE_MSK, ALL_INT_FLAGS_ST },
+	{ RK816_DCDC_EN_REG2, BOOST_EN_MASK, BOOST_DISABLE },
+	/* set write mask bit 1, otherwise 'is_enabled()' get wrong status */
+	{ RK816_LDO_EN_REG1, REGS_WMSK, REGS_WMSK },
+	{ RK816_LDO_EN_REG2, REGS_WMSK, REGS_WMSK },
+};
+
 static const struct rk808_reg_data rk817_pre_init_reg[] = {
+	{RK817_SYS_CFG(3), RK817_SLPPOL_MSK, RK817_SLPPOL_L},
 	{RK817_RTC_CTRL_REG, RTC_STOP, RTC_STOP},
 	{RK817_GPIO_INT_CFG, RK817_INT_POL_MSK, RK817_INT_POL_L},
 	{RK817_SYS_CFG(1), RK817_HOTDIE_TEMP_MSK | RK817_TSD_TEMP_MSK,
@@ -220,8 +337,10 @@ static const struct rk808_reg_data rk818_pre_init_reg[] = {
 	{ RK818_H5V_EN_REG,	  BIT(1),	    RK818_REF_RDY_CTRL },
 	/* enable HDMI 5V */
 	{ RK818_H5V_EN_REG,	  BIT(0),	    RK818_H5V_EN },
+	{ RK808_RTC_CTRL_REG, RTC_STOP, RTC_STOP},
 	{ RK808_VB_MON_REG,	  MASK_ALL,	    VB_LO_ACT |
 						    VB_LO_SEL_3500MV },
+	{RK808_CLK32OUT_REG, CLK32KOUT2_FUNC_MASK, CLK32KOUT2_FUNC},
 };
 
 static const struct regmap_irq rk805_irqs[] = {
@@ -301,6 +420,70 @@ static const struct regmap_irq rk808_irqs[] = {
 	},
 };
 
+static struct rk808_reg_data rk816_suspend_reg[] = {
+	/* set bat 3.4v low and act irq */
+	{ RK816_VB_MON_REG, VBAT_LOW_VOL_MASK | VBAT_LOW_ACT_MASK,
+	  RK816_VBAT_LOW_3V4 | EN_VBAT_LOW_IRQ },
+};
+
+static struct rk808_reg_data rk816_resume_reg[] = {
+	/* set bat 3.0v low and act shutdown */
+	{ RK816_VB_MON_REG, VBAT_LOW_VOL_MASK | VBAT_LOW_ACT_MASK,
+	  RK816_VBAT_LOW_3V0 | EN_VABT_LOW_SHUT_DOWN },
+};
+
+static const struct regmap_irq rk816_irqs[] = {
+	/* INT_STS */
+	[RK816_IRQ_PWRON_FALL] = {
+		.mask = RK816_IRQ_PWRON_FALL_MSK,
+		.reg_offset = 0,
+	},
+	[RK816_IRQ_PWRON_RISE] = {
+		.mask = RK816_IRQ_PWRON_RISE_MSK,
+		.reg_offset = 0,
+	},
+	[RK816_IRQ_VB_LOW] = {
+		.mask = RK816_IRQ_VB_LOW_MSK,
+		.reg_offset = 1,
+	},
+	[RK816_IRQ_PWRON] = {
+		.mask = RK816_IRQ_PWRON_MSK,
+		.reg_offset = 1,
+	},
+	[RK816_IRQ_PWRON_LP] = {
+		.mask = RK816_IRQ_PWRON_LP_MSK,
+		.reg_offset = 1,
+	},
+	[RK816_IRQ_HOTDIE] = {
+		.mask = RK816_IRQ_HOTDIE_MSK,
+		.reg_offset = 1,
+	},
+	[RK816_IRQ_RTC_ALARM] = {
+		.mask = RK816_IRQ_RTC_ALARM_MSK,
+		.reg_offset = 1,
+	},
+	[RK816_IRQ_RTC_PERIOD] = {
+		.mask = RK816_IRQ_RTC_PERIOD_MSK,
+		.reg_offset = 1,
+	},
+	[RK816_IRQ_USB_OV] = {
+		.mask = RK816_IRQ_USB_OV_MSK,
+		.reg_offset = 1,
+	},
+};
+
+static struct rk808_reg_data rk818_suspend_reg[] = {
+	/* set bat 3.4v low and act irq */
+	{ RK808_VB_MON_REG, VBAT_LOW_VOL_MASK | VBAT_LOW_ACT_MASK,
+	  RK808_VBAT_LOW_3V4 | EN_VBAT_LOW_IRQ },
+};
+
+static struct rk808_reg_data rk818_resume_reg[] = {
+	/* set bat 3.0v low and act shutdown */
+	{ RK808_VB_MON_REG, VBAT_LOW_VOL_MASK | VBAT_LOW_ACT_MASK,
+	  RK808_VBAT_LOW_3V0 | EN_VABT_LOW_SHUT_DOWN },
+};
+
 static const struct regmap_irq rk818_irqs[] = {
 	/* INT_STS */
 	[RK818_IRQ_VOUT_LO] = {
@@ -421,6 +604,61 @@ static const struct regmap_irq_chip rk808_irq_chip = {
 	.init_ack_masked = true,
 };
 
+static const struct regmap_irq rk816_battery_irqs[] = {
+	/* INT_STS */
+	[RK816_IRQ_PLUG_IN] = {
+		.mask = RK816_IRQ_PLUG_IN_MSK,
+		.reg_offset = 0,
+	},
+	[RK816_IRQ_PLUG_OUT] = {
+		.mask = RK816_IRQ_PLUG_OUT_MSK,
+		.reg_offset = 0,
+	},
+	[RK816_IRQ_CHG_OK] = {
+		.mask = RK816_IRQ_CHG_OK_MSK,
+		.reg_offset = 0,
+	},
+	[RK816_IRQ_CHG_TE] = {
+		.mask = RK816_IRQ_CHG_TE_MSK,
+		.reg_offset = 0,
+	},
+	[RK816_IRQ_CHG_TS] = {
+		.mask = RK816_IRQ_CHG_TS_MSK,
+		.reg_offset = 0,
+	},
+	[RK816_IRQ_CHG_CVTLIM] = {
+		.mask = RK816_IRQ_CHG_CVTLIM_MSK,
+		.reg_offset = 0,
+	},
+	[RK816_IRQ_DISCHG_ILIM] = {
+		.mask = RK816_IRQ_DISCHG_ILIM_MSK,
+		.reg_offset = 0,
+	},
+};
+
+static struct regmap_irq_chip rk816_irq_chip = {
+	.name = "rk816",
+	.irqs = rk816_irqs,
+	.num_irqs = ARRAY_SIZE(rk816_irqs),
+	.num_regs = 2,
+	.irq_reg_stride = 3,
+	.status_base = RK816_INT_STS_REG1,
+	.mask_base = RK816_INT_STS_MSK_REG1,
+	.ack_base = RK816_INT_STS_REG1,
+	.init_ack_masked = true,
+};
+
+static struct regmap_irq_chip rk816_battery_irq_chip = {
+	.name = "rk816_battery",
+	.irqs = rk816_battery_irqs,
+	.num_irqs = ARRAY_SIZE(rk816_battery_irqs),
+	.num_regs = 1,
+	.status_base = RK816_INT_STS_REG3,
+	.mask_base = RK816_INT_STS_MSK_REG3,
+	.ack_base = RK816_INT_STS_REG3,
+	.init_ack_masked = true,
+};
+
 static struct regmap_irq_chip rk817_irq_chip = {
 	.name = "rk817",
 	.irqs = rk817_irqs,
@@ -446,8 +684,70 @@ static const struct regmap_irq_chip rk818_irq_chip = {
 };
 
 static struct i2c_client *rk808_i2c_client;
+static struct rk808_reg_data *suspend_reg, *resume_reg;
+static int suspend_reg_num, resume_reg_num;
+
+static void rk805_device_shutdown_prepare(void)
+{
+	int ret;
+	struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
+
+	if (!rk808)
+		return;
+
+	ret = regmap_update_bits(rk808->regmap,
+				 RK805_GPIO_IO_POL_REG,
+				 SLP_SD_MSK, SHUTDOWN_FUN);
+	if (ret)
+		dev_err(&rk808_i2c_client->dev, "Failed to shutdown device!\n");
+}
+
+static void rk817_shutdown_prepare(void)
+{
+	int ret;
+	struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
+
+	/* close rtc int when power off */
+	regmap_update_bits(rk808->regmap,
+			   RK817_INT_STS_MSK_REG0,
+			   (0x3 << 5), (0x3 << 5));
+	regmap_update_bits(rk808->regmap,
+			   RK817_RTC_INT_REG,
+			   (0x3 << 2), (0x0 << 2));
+
+	if (rk808->pins && rk808->pins->p && rk808->pins->power_off) {
+		ret = regmap_update_bits(rk808->regmap,
+					 RK817_SYS_CFG(3),
+					 RK817_SLPPIN_FUNC_MSK,
+					 SLPPIN_NULL_FUN);
+		if (ret)
+			pr_err("shutdown: config SLPPIN_NULL_FUN error!\n");
+
+		ret = regmap_update_bits(rk808->regmap,
+					 RK817_SYS_CFG(3),
+					 RK817_SLPPOL_MSK,
+					 RK817_SLPPOL_H);
+		if (ret)
+			pr_err("shutdown: config RK817_SLPPOL_H error!\n");
+
+		ret = pinctrl_select_state(rk808->pins->p,
+					   rk808->pins->power_off);
+		if (ret)
+			pr_info("%s:failed to activate pwroff state\n",
+				__func__);
+	}
+
+	/* pmic sleep shutdown function */
+	ret = regmap_update_bits(rk808->regmap,
+				 RK817_SYS_CFG(3),
+				 RK817_SLPPIN_FUNC_MSK, SLPPIN_DN_FUN);
+	if (ret)
+		dev_err(&rk808_i2c_client->dev, "Failed to shutdown device!\n");
+	/* pmic need the SCL clock to synchronize register */
+	mdelay(2);
+}
 
-static void rk808_pm_power_off(void)
+static void rk8xx_device_shutdown(void)
 {
 	int ret;
 	unsigned int reg, bit;
@@ -462,6 +762,10 @@ static void rk808_pm_power_off(void)
 		reg = RK808_DEVCTRL_REG,
 		bit = DEV_OFF_RST;
 		break;
+	case RK816_ID:
+		reg = RK816_DEV_CTRL_REG;
+		bit = DEV_OFF;
+		break;
 	case RK818_ID:
 		reg = RK818_DEVCTRL_REG;
 		bit = DEV_OFF;
@@ -469,42 +773,345 @@ static void rk808_pm_power_off(void)
 	default:
 		return;
 	}
+
 	ret = regmap_update_bits(rk808->regmap, reg, bit, bit);
 	if (ret)
 		dev_err(&rk808_i2c_client->dev, "Failed to shutdown device!\n");
 }
 
-static void rk8xx_shutdown(struct i2c_client *client)
+/* Called in syscore shutdown */
+static void (*pm_shutdown)(void);
+
+static void rk8xx_syscore_shutdown(void)
 {
-	struct rk808 *rk808 = i2c_get_clientdata(client);
 	int ret;
+	struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
 
-	switch (rk808->variant) {
-	case RK805_ID:
-		ret = regmap_update_bits(rk808->regmap,
-					 RK805_GPIO_IO_POL_REG,
-					 SLP_SD_MSK,
-					 SHUTDOWN_FUN);
+	if (!rk808) {
+		dev_warn(&rk808_i2c_client->dev,
+			 "have no rk808, so do nothing here\n");
+		return;
+	}
+
+	/* close rtc int when power off */
+	regmap_update_bits(rk808->regmap,
+			   RK808_INT_STS_MSK_REG1,
+			   (0x3 << 5), (0x3 << 5));
+	regmap_update_bits(rk808->regmap,
+			   RK808_RTC_INT_REG,
+			   (0x3 << 2), (0x0 << 2));
+	/*
+	 * For PMIC that power off supplies by write register via i2c bus,
+	 * it's better to do power off at syscore shutdown here.
+	 *
+	 * Because when run to kernel's "pm_power_off" call, i2c may has
+	 * been stopped or PMIC may not be able to get i2c transfer while
+	 * there are too many devices are competiting.
+	 */
+	if (system_state == SYSTEM_POWER_OFF) {
+		if (rk808->variant == RK809_ID || rk808->variant == RK817_ID) {
+			ret = regmap_update_bits(rk808->regmap,
+						 RK817_SYS_CFG(3),
+						 RK817_SLPPIN_FUNC_MSK,
+						 SLPPIN_DN_FUN);
+			if (ret) {
+				dev_warn(&rk808_i2c_client->dev,
+					 "Cannot switch to power down function\n");
+			}
+		}
+
+		if (pm_shutdown) {
+			dev_info(&rk808_i2c_client->dev, "System power off\n");
+			pm_shutdown();
+			mdelay(10);
+			dev_info(&rk808_i2c_client->dev,
+				 "Power off failed !\n");
+			while (1)
+				;
+		}
+	}
+}
+
+static struct syscore_ops rk808_syscore_ops = {
+	.shutdown = rk8xx_syscore_shutdown,
+};
+
+/*
+ * RK8xx PMICs would do real power off in syscore shutdown, if "pm_power_off"
+ * is not assigned(e.g. PSCI is not enabled), we have to provide a dummy
+ * callback for it, otherwise there comes a halt in Reboot system call:
+ *
+ * if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
+ *		cmd = LINUX_REBOOT_CMD_HALT;
+ */
+static void rk808_pm_power_off_dummy(void)
+{
+	pr_info("Dummy power off for RK8xx PMICs, should never reach here!\n");
+
+	while (1)
+		;
+}
+
+static ssize_t rk8xx_dbg_store(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	int ret;
+	char cmd;
+	u32 input[2], addr, data;
+	struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
+
+	ret = sscanf(buf, "%c ", &cmd);
+	if (ret != 1) {
+		pr_err("Unknown command\n");
+		goto out;
+	}
+	switch (cmd) {
+	case 'w':
+		ret = sscanf(buf, "%c %x %x ", &cmd, &input[0], &input[1]);
+		if (ret != 3) {
+			pr_err("error! cmd format: echo w [addr] [value]\n");
+			goto out;
+		};
+		addr = input[0] & 0xff;
+		data = input[1] & 0xff;
+		pr_info("cmd : %c %x %x\n\n", cmd, input[0], input[1]);
+		regmap_write(rk808->regmap, addr, data);
+		regmap_read(rk808->regmap, addr, &data);
+		pr_info("new: %x %x\n", addr, data);
 		break;
-	case RK809_ID:
-	case RK817_ID:
-		ret = regmap_update_bits(rk808->regmap,
-					 RK817_SYS_CFG(3),
-					 RK817_SLPPIN_FUNC_MSK,
-					 SLPPIN_DN_FUN);
+	case 'r':
+		ret = sscanf(buf, "%c %x ", &cmd, &input[0]);
+		if (ret != 2) {
+			pr_err("error! cmd format: echo r [addr]\n");
+			goto out;
+		};
+		pr_info("cmd : %c %x\n\n", cmd, input[0]);
+		addr = input[0] & 0xff;
+		regmap_read(rk808->regmap, addr, &data);
+		pr_info("%x %x\n", input[0], data);
 		break;
 	default:
-		return;
+		pr_err("Unknown command\n");
+		break;
+	}
+
+out:
+	return count;
+}
+
+static int rk817_pinctrl_init(struct device *dev, struct rk808 *rk808)
+{
+	int ret;
+	struct platform_device	*pinctrl_dev;
+	struct pinctrl_state *default_st;
+
+	pinctrl_dev = platform_device_alloc("rk805-pinctrl", -1);
+	if (!pinctrl_dev) {
+		dev_err(dev, "Alloc pinctrl dev failed!\n");
+		return -ENOMEM;
 	}
+
+	pinctrl_dev->dev.parent = dev;
+
+	ret = platform_device_add(pinctrl_dev);
+
+	if (ret) {
+		platform_device_put(pinctrl_dev);
+		dev_err(dev, "Add rk805-pinctrl dev failed!\n");
+		return ret;
+	}
+	if (dev->pins && !IS_ERR(dev->pins->p)) {
+		dev_info(dev, "had get a pinctrl!\n");
+		return 0;
+	}
+
+	rk808->pins = devm_kzalloc(dev, sizeof(struct rk808_pin_info),
+				   GFP_KERNEL);
+	if (!rk808->pins)
+		return -ENOMEM;
+
+	rk808->pins->p = devm_pinctrl_get(dev);
+	if (IS_ERR(rk808->pins->p)) {
+		rk808->pins->p = NULL;
+		dev_err(dev, "no pinctrl handle\n");
+		return 0;
+	}
+
+	default_st = pinctrl_lookup_state(rk808->pins->p,
+					  PINCTRL_STATE_DEFAULT);
+
+	if (IS_ERR(default_st)) {
+		dev_dbg(dev, "no default pinctrl state\n");
+			return -EINVAL;
+	}
+
+	ret = pinctrl_select_state(rk808->pins->p, default_st);
+	if (ret) {
+		dev_dbg(dev, "failed to activate default pinctrl state\n");
+		return -EINVAL;
+	}
+
+	rk808->pins->power_off = pinctrl_lookup_state(rk808->pins->p,
+						      "pmic-power-off");
+	if (IS_ERR(rk808->pins->power_off)) {
+		rk808->pins->power_off = NULL;
+		dev_dbg(dev, "no power-off pinctrl state\n");
+	}
+
+	rk808->pins->sleep = pinctrl_lookup_state(rk808->pins->p,
+						  "pmic-sleep");
+	if (IS_ERR(rk808->pins->sleep)) {
+		rk808->pins->sleep = NULL;
+		dev_dbg(dev, "no sleep-setting state\n");
+	}
+
+	rk808->pins->reset = pinctrl_lookup_state(rk808->pins->p,
+						  "pmic-reset");
+	if (IS_ERR(rk808->pins->reset)) {
+		rk808->pins->reset = NULL;
+		dev_dbg(dev, "no reset-setting pinctrl state\n");
+		return 0;
+	}
+
+	ret = pinctrl_select_state(rk808->pins->p, rk808->pins->reset);
+
 	if (ret)
-		dev_warn(&client->dev,
-			 "Cannot switch to power down function\n");
+		dev_dbg(dev, "failed to activate reset-setting pinctrl state\n");
+
+	return 0;
+}
+
+struct rk817_reboot_data_t {
+	struct rk808 *rk808;
+	struct notifier_block reboot_notifier;
+};
+
+static struct rk817_reboot_data_t rk817_reboot_data;
+
+static int rk817_reboot_notifier_handler(struct notifier_block *nb,
+					 unsigned long action, void *cmd)
+{
+	struct rk817_reboot_data_t *data;
+	struct device *dev;
+	int value, power_en_active0, power_en_active1;
+	int ret, i;
+	static const char * const pmic_rst_reg_only_cmd[] = {
+		"loader", "bootloader", "fastboot", "recovery",
+		"ums", "panic", "watchdog", "charge",
+	};
+
+	data = container_of(nb, struct rk817_reboot_data_t, reboot_notifier);
+	dev = &data->rk808->i2c->dev;
+
+	regmap_read(data->rk808->regmap, RK817_POWER_EN_SAVE0,
+		    &power_en_active0);
+	if (power_en_active0 != 0) {
+		regmap_read(data->rk808->regmap, RK817_POWER_EN_SAVE1,
+			    &power_en_active1);
+		value = power_en_active0 & 0x0f;
+		regmap_write(data->rk808->regmap,
+			     RK817_POWER_EN_REG(0),
+			     value | 0xf0);
+		value = (power_en_active0 & 0xf0) >> 4;
+		regmap_write(data->rk808->regmap,
+			     RK817_POWER_EN_REG(1),
+			     value | 0xf0);
+		value = power_en_active1 & 0x0f;
+		regmap_write(data->rk808->regmap,
+			     RK817_POWER_EN_REG(2),
+			     value | 0xf0);
+		value = (power_en_active1 & 0xf0) >> 4;
+		regmap_write(data->rk808->regmap,
+			     RK817_POWER_EN_REG(3),
+			     value | 0xf0);
+	} else {
+		dev_info(dev, "reboot: not restore POWER_EN\n");
+	}
+
+	if (action != SYS_RESTART || !cmd)
+		return NOTIFY_OK;
+
+	/*
+	 * When system restart, there are two rst actions of PMIC sleep if
+	 * board hardware support:
+	 *
+	 *	0b'00: reset the PMIC itself completely.
+	 *	0b'01: reset the 'RST' related register only.
+	 *
+	 * In the case of 0b'00, PMIC reset itself which triggers SoC NPOR-reset
+	 * at the same time, so the command: reboot load/bootload/recovery, etc
+	 * is not effect any more.
+	 *
+	 * Here we check if this reboot cmd is what we expect for 0b'01.
+	 */
+	for (i = 0; i < ARRAY_SIZE(pmic_rst_reg_only_cmd); i++) {
+		if (!strcmp(cmd, pmic_rst_reg_only_cmd[i])) {
+			ret = regmap_update_bits(data->rk808->regmap,
+						 RK817_SYS_CFG(3),
+						 RK817_RST_FUNC_MSK,
+						 RK817_RST_FUNC_REG);
+			if (ret)
+				dev_err(dev, "reboot: force RK817_RST_FUNC_REG error!\n");
+			else
+				dev_info(dev, "reboot: force RK817_RST_FUNC_REG ok!\n");
+			break;
+		}
+	}
+
+	return NOTIFY_OK;
 }
 
+static void rk817_of_property_prepare(struct rk808 *rk808, struct device *dev)
+{
+	u32 inner;
+	int ret, func, msk, val;
+	struct device_node *np = dev->of_node;
+
+	ret = of_property_read_u32_index(np, "fb-inner-reg-idxs", 0, &inner);
+	if (!ret && inner == RK817_ID_DCDC3)
+		regmap_update_bits(rk808->regmap, RK817_POWER_CONFIG,
+				   RK817_BUCK3_FB_RES_MSK,
+				   RK817_BUCK3_FB_RES_INTER);
+	else
+		regmap_update_bits(rk808->regmap, RK817_POWER_CONFIG,
+				   RK817_BUCK3_FB_RES_MSK,
+				   RK817_BUCK3_FB_RES_EXT);
+	dev_info(dev, "support dcdc3 fb mode:%d, %d\n", ret, inner);
+
+	ret = of_property_read_u32(np, "pmic-reset-func", &func);
+
+	msk = RK817_SLPPIN_FUNC_MSK | RK817_RST_FUNC_MSK;
+	val = SLPPIN_NULL_FUN;
+
+	if (!ret && func < RK817_RST_FUNC_CNT) {
+		val |= RK817_RST_FUNC_MSK &
+		       (func << RK817_RST_FUNC_SFT);
+	} else {
+		val |= RK817_RST_FUNC_REG;
+	}
+
+	regmap_update_bits(rk808->regmap, RK817_SYS_CFG(3), msk, val);
+
+	dev_info(dev, "support pmic reset mode:%d,%d\n", ret, func);
+
+	rk817_reboot_data.rk808 = rk808;
+	rk817_reboot_data.reboot_notifier.notifier_call =
+		rk817_reboot_notifier_handler;
+	ret = register_reboot_notifier(&rk817_reboot_data.reboot_notifier);
+	if (ret)
+		dev_err(dev, "failed to register reboot nb\n");
+}
+
+static struct kobject *rk8xx_kobj;
+static struct device_attribute rk8xx_attrs =
+		__ATTR(rk8xx_dbg, 0200, NULL, rk8xx_dbg_store);
+
 static const struct of_device_id rk808_of_match[] = {
 	{ .compatible = "rockchip,rk805" },
 	{ .compatible = "rockchip,rk808" },
 	{ .compatible = "rockchip,rk809" },
+	{ .compatible = "rockchip,rk816" },
 	{ .compatible = "rockchip,rk817" },
 	{ .compatible = "rockchip,rk818" },
 	{ },
@@ -517,13 +1124,20 @@ static int rk808_probe(struct i2c_client *client,
 	struct device_node *np = client->dev.of_node;
 	struct rk808 *rk808;
 	const struct rk808_reg_data *pre_init_reg;
+	const struct regmap_irq_chip *battery_irq_chip = NULL;
 	const struct mfd_cell *cells;
+	unsigned char pmic_id_msb, pmic_id_lsb;
+	u8 on_source = 0, off_source = 0;
+	unsigned int on, off;
+	int pm_off = 0, msb, lsb;
 	int nr_pre_init_regs;
 	int nr_cells;
-	int msb, lsb;
-	unsigned char pmic_id_msb, pmic_id_lsb;
 	int ret;
 	int i;
+	void (*of_property_prepare_fn)(struct rk808 *rk808,
+				       struct device *dev) = NULL;
+	int (*pinctrl_init)(struct device *dev, struct rk808 *rk808) = NULL;
+	void (*device_shutdown_fn)(void) = NULL;
 
 	rk808 = devm_kzalloc(&client->dev, sizeof(*rk808), GFP_KERNEL);
 	if (!rk808)
@@ -564,6 +1178,14 @@ static int rk808_probe(struct i2c_client *client,
 		nr_pre_init_regs = ARRAY_SIZE(rk805_pre_init_reg);
 		cells = rk805s;
 		nr_cells = ARRAY_SIZE(rk805s);
+		on_source = RK805_ON_SOURCE_REG;
+		off_source = RK805_OFF_SOURCE_REG;
+		suspend_reg = rk805_suspend_reg;
+		suspend_reg_num = ARRAY_SIZE(rk805_suspend_reg);
+		resume_reg = rk805_resume_reg;
+		resume_reg_num = ARRAY_SIZE(rk805_resume_reg);
+		device_shutdown_fn = rk8xx_device_shutdown;
+		rk808->pm_pwroff_prep_fn = rk805_device_shutdown_prepare;
 		break;
 	case RK808_ID:
 		rk808->regmap_cfg = &rk808_regmap_config;
@@ -572,6 +1194,23 @@ static int rk808_probe(struct i2c_client *client,
 		nr_pre_init_regs = ARRAY_SIZE(rk808_pre_init_reg);
 		cells = rk808s;
 		nr_cells = ARRAY_SIZE(rk808s);
+		device_shutdown_fn = rk8xx_device_shutdown;
+		break;
+	case RK816_ID:
+		rk808->regmap_cfg = &rk816_regmap_config;
+		rk808->regmap_irq_chip = &rk816_irq_chip;
+		battery_irq_chip = &rk816_battery_irq_chip;
+		pre_init_reg = rk816_pre_init_reg;
+		nr_pre_init_regs = ARRAY_SIZE(rk816_pre_init_reg);
+		cells = rk816s;
+		nr_cells = ARRAY_SIZE(rk816s);
+		on_source = RK816_ON_SOURCE_REG;
+		off_source = RK816_OFF_SOURCE_REG;
+		suspend_reg = rk816_suspend_reg;
+		suspend_reg_num = ARRAY_SIZE(rk816_suspend_reg);
+		resume_reg = rk816_resume_reg;
+		resume_reg_num = ARRAY_SIZE(rk816_resume_reg);
+		device_shutdown_fn = rk8xx_device_shutdown;
 		break;
 	case RK818_ID:
 		rk808->regmap_cfg = &rk818_regmap_config;
@@ -580,6 +1219,13 @@ static int rk808_probe(struct i2c_client *client,
 		nr_pre_init_regs = ARRAY_SIZE(rk818_pre_init_reg);
 		cells = rk818s;
 		nr_cells = ARRAY_SIZE(rk818s);
+		on_source = RK818_ON_SOURCE_REG;
+		off_source = RK818_OFF_SOURCE_REG;
+		suspend_reg = rk818_suspend_reg;
+		suspend_reg_num = ARRAY_SIZE(rk818_suspend_reg);
+		resume_reg = rk818_resume_reg;
+		resume_reg_num = ARRAY_SIZE(rk818_resume_reg);
+		device_shutdown_fn = rk8xx_device_shutdown;
 		break;
 	case RK809_ID:
 	case RK817_ID:
@@ -589,6 +1235,11 @@ static int rk808_probe(struct i2c_client *client,
 		nr_pre_init_regs = ARRAY_SIZE(rk817_pre_init_reg);
 		cells = rk817s;
 		nr_cells = ARRAY_SIZE(rk817s);
+		on_source = RK817_ON_SOURCE_REG;
+		off_source = RK817_OFF_SOURCE_REG;
+		rk808->pm_pwroff_prep_fn = rk817_shutdown_prepare;
+		of_property_prepare_fn = rk817_of_property_prepare;
+		pinctrl_init = rk817_pinctrl_init;
 		break;
 	default:
 		dev_err(&client->dev, "Unsupported RK8XX ID %lu\n",
@@ -597,6 +1248,7 @@ static int rk808_probe(struct i2c_client *client,
 	}
 
 	rk808->i2c = client;
+	rk808_i2c_client = client;
 	i2c_set_clientdata(client, rk808);
 
 	rk808->regmap = devm_regmap_init_i2c(client, rk808->regmap_cfg);
@@ -605,11 +1257,50 @@ static int rk808_probe(struct i2c_client *client,
 		return PTR_ERR(rk808->regmap);
 	}
 
+	if (on_source && off_source) {
+		ret = regmap_read(rk808->regmap, on_source, &on);
+		if (ret) {
+			dev_err(&client->dev, "read 0x%x failed\n", on_source);
+			return ret;
+		}
+
+		ret = regmap_read(rk808->regmap, off_source, &off);
+		if (ret) {
+			dev_err(&client->dev, "read 0x%x failed\n", off_source);
+			return ret;
+		}
+
+		dev_info(&client->dev, "source: on=0x%02x, off=0x%02x\n",
+			 on, off);
+	}
+
 	if (!client->irq) {
 		dev_err(&client->dev, "No interrupt support, no core IRQ\n");
 		return -EINVAL;
 	}
 
+	if (of_property_prepare_fn)
+		of_property_prepare_fn(rk808, &client->dev);
+
+	for (i = 0; i < nr_pre_init_regs; i++) {
+		ret = regmap_update_bits(rk808->regmap,
+					 pre_init_reg[i].addr,
+					 pre_init_reg[i].mask,
+					 pre_init_reg[i].value);
+		if (ret) {
+			dev_err(&client->dev,
+				"0x%x write err\n",
+				pre_init_reg[i].addr);
+			return ret;
+		}
+	}
+
+	if (pinctrl_init) {
+		ret = pinctrl_init(&client->dev, rk808);
+		if (ret)
+			return ret;
+	}
+
 	ret = regmap_add_irq_chip(rk808->regmap, client->irq,
 				  IRQF_ONESHOT, -1,
 				  rk808->regmap_irq_chip, &rk808->irq_data);
@@ -618,15 +1309,15 @@ static int rk808_probe(struct i2c_client *client,
 		return ret;
 	}
 
-	for (i = 0; i < nr_pre_init_regs; i++) {
-		ret = regmap_update_bits(rk808->regmap,
-					pre_init_reg[i].addr,
-					pre_init_reg[i].mask,
-					pre_init_reg[i].value);
+	if (battery_irq_chip) {
+		ret = regmap_add_irq_chip(rk808->regmap, client->irq,
+					  IRQF_ONESHOT | IRQF_SHARED, -1,
+					  battery_irq_chip,
+					  &rk808->battery_irq_data);
 		if (ret) {
 			dev_err(&client->dev,
-				"0x%x write err\n",
-				pre_init_reg[i].addr);
+				"Failed to add batterry irq_chip %d\n", ret);
+			regmap_del_irq_chip(client->irq, rk808->irq_data);
 			return ret;
 		}
 	}
@@ -639,15 +1330,34 @@ static int rk808_probe(struct i2c_client *client,
 		goto err_irq;
 	}
 
-	if (of_property_read_bool(np, "rockchip,system-power-controller")) {
-		rk808_i2c_client = client;
-		pm_power_off = rk808_pm_power_off;
+	pm_off = of_property_read_bool(np, "rockchip,system-power-controller");
+	if (pm_off) {
+		if (!pm_power_off_prepare)
+			pm_power_off_prepare = rk808->pm_pwroff_prep_fn;
+
+		if (device_shutdown_fn) {
+			register_syscore_ops(&rk808_syscore_ops);
+			/* power off system in the syscore shutdown ! */
+			pm_shutdown = device_shutdown_fn;
+		}
+	}
+
+	rk8xx_kobj = kobject_create_and_add("rk8xx", NULL);
+	if (rk8xx_kobj) {
+		ret = sysfs_create_file(rk8xx_kobj, &rk8xx_attrs.attr);
+		if (ret)
+			dev_err(&client->dev, "create rk8xx sysfs error\n");
 	}
 
+	if (!pm_power_off)
+		pm_power_off = rk808_pm_power_off_dummy;
+
 	return 0;
 
 err_irq:
 	regmap_del_irq_chip(client->irq, rk808->irq_data);
+	if (battery_irq_chip)
+		regmap_del_irq_chip(client->irq, rk808->battery_irq_data);
 	return ret;
 }
 
@@ -656,21 +1366,45 @@ static int rk808_remove(struct i2c_client *client)
 	struct rk808 *rk808 = i2c_get_clientdata(client);
 
 	regmap_del_irq_chip(client->irq, rk808->irq_data);
+	mfd_remove_devices(&client->dev);
 
 	/**
 	 * pm_power_off may points to a function from another module.
 	 * Check if the pointer is set by us and only then overwrite it.
 	 */
-	if (pm_power_off == rk808_pm_power_off)
+	if (pm_power_off == rk808_pm_power_off_dummy)
 		pm_power_off = NULL;
 
+	/**
+	 * As above, check if the pointer is set by us before overwrite.
+	 */
+	if (rk808->pm_pwroff_prep_fn &&
+	    pm_power_off_prepare == rk808->pm_pwroff_prep_fn)
+		pm_power_off_prepare = NULL;
+
+	if (pm_shutdown)
+		unregister_syscore_ops(&rk808_syscore_ops);
+
 	return 0;
 }
 
 static int __maybe_unused rk8xx_suspend(struct device *dev)
 {
-	struct rk808 *rk808 = i2c_get_clientdata(to_i2c_client(dev));
-	int ret = 0;
+	struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
+	int i, ret = 0;
+	int value;
+
+	for (i = 0; i < suspend_reg_num; i++) {
+		ret = regmap_update_bits(rk808->regmap,
+					 suspend_reg[i].addr,
+					 suspend_reg[i].mask,
+					 suspend_reg[i].value);
+		if (ret) {
+			dev_err(dev, "0x%x write err\n",
+				suspend_reg[i].addr);
+			return ret;
+		}
+	}
 
 	switch (rk808->variant) {
 	case RK805_ID:
@@ -681,10 +1415,34 @@ static int __maybe_unused rk8xx_suspend(struct device *dev)
 		break;
 	case RK809_ID:
 	case RK817_ID:
-		ret = regmap_update_bits(rk808->regmap,
-					 RK817_SYS_CFG(3),
-					 RK817_SLPPIN_FUNC_MSK,
-					 SLPPIN_SLP_FUN);
+		if (rk808->pins && rk808->pins->p && rk808->pins->sleep) {
+			ret = regmap_update_bits(rk808->regmap,
+						 RK817_SYS_CFG(3),
+						 RK817_SLPPIN_FUNC_MSK,
+						 SLPPIN_NULL_FUN);
+			if (ret) {
+				dev_err(dev, "suspend: config SLPPIN_NULL_FUN error!\n");
+				return ret;
+			}
+
+			ret = regmap_update_bits(rk808->regmap,
+						 RK817_SYS_CFG(3),
+						 RK817_SLPPOL_MSK,
+						 RK817_SLPPOL_H);
+			if (ret) {
+				dev_err(dev, "suspend: config RK817_SLPPOL_H error!\n");
+				return ret;
+			}
+
+			/* pmic need the SCL clock to synchronize register */
+			regmap_read(rk808->regmap, RK817_SYS_STS, &value);
+			mdelay(2);
+			ret = pinctrl_select_state(rk808->pins->p, rk808->pins->sleep);
+			if (ret) {
+				dev_err(dev, "failed to act slp pinctrl state\n");
+				return ret;
+			}
+		}
 		break;
 	default:
 		break;
@@ -695,16 +1453,51 @@ static int __maybe_unused rk8xx_suspend(struct device *dev)
 
 static int __maybe_unused rk8xx_resume(struct device *dev)
 {
-	struct rk808 *rk808 = i2c_get_clientdata(to_i2c_client(dev));
-	int ret = 0;
+	struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
+	int i, ret = 0;
+	int value;
+
+	for (i = 0; i < resume_reg_num; i++) {
+		ret = regmap_update_bits(rk808->regmap,
+					 resume_reg[i].addr,
+					 resume_reg[i].mask,
+					 resume_reg[i].value);
+		if (ret) {
+			dev_err(dev, "0x%x write err\n",
+				resume_reg[i].addr);
+			return ret;
+		}
+	}
 
 	switch (rk808->variant) {
 	case RK809_ID:
 	case RK817_ID:
-		ret = regmap_update_bits(rk808->regmap,
-					 RK817_SYS_CFG(3),
-					 RK817_SLPPIN_FUNC_MSK,
-					 SLPPIN_NULL_FUN);
+		if (rk808->pins && rk808->pins->p && rk808->pins->reset) {
+			ret = regmap_update_bits(rk808->regmap,
+						 RK817_SYS_CFG(3),
+						 RK817_SLPPIN_FUNC_MSK,
+						 SLPPIN_NULL_FUN);
+			if (ret) {
+				dev_err(dev, "resume: config SLPPIN_NULL_FUN error!\n");
+				return ret;
+			}
+
+			ret = regmap_update_bits(rk808->regmap,
+						 RK817_SYS_CFG(3),
+						 RK817_SLPPOL_MSK,
+						 RK817_SLPPOL_L);
+			if (ret) {
+				dev_err(dev, "resume: config RK817_SLPPOL_L error!\n");
+				return ret;
+			}
+
+			/* pmic need the SCL clock to synchronize register */
+			regmap_read(rk808->regmap, RK817_SYS_STS, &value);
+			mdelay(2);
+			ret = pinctrl_select_state(rk808->pins->p, rk808->pins->reset);
+			if (ret)
+				dev_dbg(dev, "failed to act reset pinctrl state\n");
+		}
 		break;
 	default:
 		break;
@@ -712,7 +1505,7 @@ static int __maybe_unused rk8xx_resume(struct device *dev)
 
 	return ret;
 }
-static SIMPLE_DEV_PM_OPS(rk8xx_pm_ops, rk8xx_suspend, rk8xx_resume);
+SIMPLE_DEV_PM_OPS(rk8xx_pm_ops, rk8xx_suspend, rk8xx_resume);
 
 static struct i2c_driver rk808_i2c_driver = {
 	.driver = {
@@ -722,10 +1515,23 @@ static struct i2c_driver rk808_i2c_driver = {
 	},
 	.probe    = rk808_probe,
 	.remove   = rk808_remove,
-	.shutdown = rk8xx_shutdown,
 };
 
+#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT
+static int __init rk808_i2c_driver_init(void)
+{
+	return i2c_add_driver(&rk808_i2c_driver);
+}
+subsys_initcall(rk808_i2c_driver_init);
+
+static void __exit rk808_i2c_driver_exit(void)
+{
+	i2c_del_driver(&rk808_i2c_driver);
+}
+module_exit(rk808_i2c_driver_exit);
+#else
 module_i2c_driver(rk808_i2c_driver);
+#endif
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index 6622e3262..99cf93f58 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -47,10 +47,13 @@
 
 #include <linux/uaccess.h>
 
+#include <trace/hooks/mmc_core.h>
+
 #include "queue.h"
 #include "block.h"
 #include "core.h"
 #include "card.h"
+#include "../../../vendor/include/linux/crypto.h"
 #include "host.h"
 #include "bus.h"
 #include "mmc_ops.h"
@@ -541,7 +544,6 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
 		return mmc_sanitize(card);
 
 	mmc_wait_for_req(card->host, &mrq);
-	memcpy(&idata->ic.response, cmd.resp, sizeof(cmd.resp));
 
 	if (cmd.error) {
 		dev_err(mmc_dev(card->host), "%s: cmd error %d\n",
@@ -591,6 +593,8 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
 	if (idata->ic.postsleep_min_us)
 		usleep_range(idata->ic.postsleep_min_us, idata->ic.postsleep_max_us);
 
+	memcpy(&(idata->ic.response), cmd.resp, sizeof(cmd.resp));
+
 	if (idata->rpmb || (cmd.flags & MMC_RSP_R1B) == MMC_RSP_R1B) {
 		/*
 		 * Ensure RPMB/R1B command has completed by polling CMD13
@@ -961,6 +965,11 @@ static int mmc_blk_reset(struct mmc_blk_data *md, struct mmc_host *host,
 		struct mmc_blk_data *main_md =
 			dev_get_drvdata(&host->card->dev);
 		int part_err;
+		bool allow = true;
+
+		trace_android_vh_mmc_blk_reset(host, err, &allow);
+		if (!allow)
+			return -ENODEV;
 
 		main_md->part_curr = main_md->part_type;
 		part_err = mmc_blk_part_switch(host->card, md->part_type);
@@ -1271,6 +1280,8 @@ static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq,
 
 	memset(brq, 0, sizeof(struct mmc_blk_request));
 
+	mmc_crypto_prepare_req(mqrq);
+
 	brq->mrq.data = &brq->data;
 	brq->mrq.tag = req->tag;
 
@@ -1804,6 +1815,7 @@ static void mmc_blk_mq_rw_recovery(struct mmc_queue *mq, struct request *req)
 	    err && mmc_blk_reset(md, card->host, type)) {
 		pr_err("%s: recovery failed!\n", req->rq_disk->disk_name);
 		mqrq->retries = MMC_NO_RETRIES;
+		trace_android_vh_mmc_blk_mq_rw_recovery(card);
 		return;
 	}
 
@@ -2897,6 +2909,9 @@ static void mmc_blk_remove_debugfs(struct mmc_card *card,
 
 #endif /* CONFIG_DEBUG_FS */
 
+struct mmc_card *this_card;
+EXPORT_SYMBOL(this_card);
+
 static int mmc_blk_probe(struct mmc_card *card)
 {
 	struct mmc_blk_data *md, *part_md;
@@ -2932,6 +2947,11 @@ static int mmc_blk_probe(struct mmc_card *card)
 
 	dev_set_drvdata(&card->dev, md);
 
+#if defined(CONFIG_MMC_DW_ROCKCHIP) || defined(CONFIG_MMC_SDHCI_OF_ARASAN)
+	if (card->type == MMC_TYPE_MMC)
+		this_card = card;
+#endif
+
 	if (mmc_add_disk(md))
 		goto out;
 
@@ -2968,6 +2988,12 @@ static void mmc_blk_remove(struct mmc_card *card)
 	struct mmc_blk_data *md = dev_get_drvdata(&card->dev);
 
 	mmc_blk_remove_debugfs(card, md);
+
+	#if defined(CONFIG_MMC_DW_ROCKCHIP)
+	if (card->type == MMC_TYPE_MMC)
+		this_card = NULL;
+	#endif
+
 	mmc_blk_remove_parts(card, md);
 	pm_runtime_get_sync(&card->dev);
 	if (md->part_curr != md->part_type) {
diff --git a/drivers/mmc/core/block.h b/drivers/mmc/core/block.h
index 31153f656..0ce5d97d4 100644
--- a/drivers/mmc/core/block.h
+++ b/drivers/mmc/core/block.h
@@ -17,4 +17,6 @@ struct work_struct;
 
 void mmc_blk_mq_complete_work(struct work_struct *work);
 
+extern struct mmc_card *this_card;
+
 #endif
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 8f2465394..34b406fd7 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -37,6 +37,7 @@
 
 #include "core.h"
 #include "card.h"
+#include "../../../vendor/include/linux/crypto.h"
 #include "bus.h"
 #include "host.h"
 #include "sdio_bus.h"
@@ -50,8 +51,6 @@
 #define MMC_ERASE_TIMEOUT_MS	(60 * 1000) /* 60 s */
 #define SD_DISCARD_TIMEOUT_MS	(250)
 
-static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
-
 /*
  * Enabling software CRCs on the data blocks can be a significant (30%)
  * performance cost, and for other reasons may not always be desired.
@@ -916,6 +915,7 @@ void mmc_set_clock(struct mmc_host *host, unsigned int hz)
 	host->ios.clock = hz;
 	mmc_set_ios(host);
 }
+EXPORT_SYMBOL_GPL(mmc_set_clock);
 
 int mmc_execute_tuning(struct mmc_card *card)
 {
@@ -995,7 +995,10 @@ void mmc_set_initial_state(struct mmc_host *host)
 		host->ops->hs400_enhanced_strobe(host, &host->ios);
 
 	mmc_set_ios(host);
+
+	mmc_crypto_set_initial_state(host);
 }
+EXPORT_SYMBOL_GPL(mmc_set_initial_state);
 
 /**
  * mmc_vdd_to_ocrbitnum - Convert a voltage to the OCR bit number
@@ -1265,6 +1268,7 @@ void mmc_set_timing(struct mmc_host *host, unsigned int timing)
 	host->ios.timing = timing;
 	mmc_set_ios(host);
 }
+EXPORT_SYMBOL_GPL(mmc_set_timing);
 
 /*
  * Select appropriate driver type for host.
@@ -2068,6 +2072,7 @@ int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)
 }
 EXPORT_SYMBOL(mmc_set_blocklen);
 
+#ifndef CONFIG_ROCKCHIP_THUNDER_BOOT
 static void mmc_hw_reset_for_init(struct mmc_host *host)
 {
 	mmc_pwrseq_reset(host);
@@ -2076,6 +2081,7 @@ static void mmc_hw_reset_for_init(struct mmc_host *host)
 		return;
 	host->ops->hw_reset(host);
 }
+#endif
 
 /**
  * mmc_hw_reset - reset the card in hardware
@@ -2148,7 +2154,9 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
 	 * Some eMMCs (with VCCQ always on) may not be reset after power up, so
 	 * do a hardware reset if possible.
 	 */
+#ifndef CONFIG_ROCKCHIP_THUNDER_BOOT
 	mmc_hw_reset_for_init(host);
+#endif
 
 	/*
 	 * sdio_reset sends CMD52 to reset card.  Since we do not know
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index a6c814fdb..61284786a 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -17,6 +17,8 @@ struct mmc_request;
 
 #define MMC_CMD_RETRIES        3
 
+static const unsigned int freqs[] = { 400000, 300000, 200000, 100000 };
+
 struct mmc_bus_ops {
 	void (*remove)(struct mmc_host *);
 	void (*detect)(struct mmc_host *);
@@ -30,6 +32,8 @@ struct mmc_bus_ops {
 	int (*hw_reset)(struct mmc_host *);
 	int (*sw_reset)(struct mmc_host *);
 	bool (*cache_enabled)(struct mmc_host *);
+
+	//ANDROID_VENDOR_DATA_ARRAY(1, 2);
 };
 
 void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
@@ -41,7 +45,7 @@ struct device_node *mmc_of_find_child_device(struct mmc_host *host,
 void mmc_init_erase(struct mmc_card *card);
 
 void mmc_set_chip_select(struct mmc_host *host, int mode);
-void mmc_set_clock(struct mmc_host *host, unsigned int hz);
+extern void mmc_set_clock(struct mmc_host *host, unsigned int hz);
 void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
 void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
 u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
@@ -81,6 +85,8 @@ int mmc_attach_mmc(struct mmc_host *host);
 int mmc_attach_sd(struct mmc_host *host);
 int mmc_attach_sdio(struct mmc_host *host);
 
+int sdio_reset_comm(struct mmc_card *card);
+
 /* Module parameters */
 extern bool use_spi_crc;
 
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 87807ef01..6da97f26f 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -9,13 +9,15 @@
 
 #include <linux/err.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/slab.h>
 #include <linux/stat.h>
 #include <linux/pm_runtime.h>
-
+#include <linux/mm.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/card.h>
 #include <linux/mmc/mmc.h>
+#include <linux/resource.h>
 
 #include "core.h"
 #include "card.h"
@@ -65,6 +67,7 @@ static const unsigned int taac_mant[] = {
 /*
  * Given the decoded CSD structure, decode the raw CID to our CID structure.
  */
+#ifndef CONFIG_ROCKCHIP_THUNDER_BOOT
 static int mmc_decode_cid(struct mmc_card *card)
 {
 	u32 *resp = card->raw_cid;
@@ -116,6 +119,7 @@ static int mmc_decode_cid(struct mmc_card *card)
 
 	return 0;
 }
+#endif
 
 static void mmc_set_erase_size(struct mmc_card *card)
 {
@@ -662,14 +666,72 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
 	return err;
 }
 
+#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT
+static void *mmc_tb_map_ecsd(phys_addr_t start, size_t len)
+{
+	int i;
+	void *vaddr;
+	pgprot_t pgprot = PAGE_KERNEL;
+	phys_addr_t phys;
+	int npages = PAGE_ALIGN(len) / PAGE_SIZE;
+	struct page **p = vmalloc(sizeof(struct page *) * npages);
+
+	if (!p)
+		return NULL;
+
+	phys = start;
+	for (i = 0; i < npages; i++) {
+		p[i] = phys_to_page(phys);
+		phys += PAGE_SIZE;
+	}
+
+	vaddr = vmap(p, npages, VM_MAP, pgprot);
+	vfree(p);
+
+	return vaddr;
+}
+#endif
+
 static int mmc_read_ext_csd(struct mmc_card *card)
 {
 	u8 *ext_csd;
 	int err;
-
+#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT
+	void *ecsd;
+	bool valid_ecsd = false;
+	struct device_node *mem;
+	struct resource reg;
+	struct device *dev = card->host->parent;
+#endif
 	if (!mmc_can_ext_csd(card))
 		return 0;
 
+#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT
+	mem = of_parse_phandle(dev->of_node, "memory-region-ecsd", 0);
+	if (mem) {
+		err = of_address_to_resource(mem, 0, &reg);
+		if (err < 0) {
+			dev_err(dev, "fail to get resource\n");
+			goto get_ecsd;
+		}
+
+		ecsd = mmc_tb_map_ecsd(reg.start, resource_size(&reg));
+		if (!ecsd)
+			goto get_ecsd;
+
+		if (readl(ecsd + SZ_512) == 0x55aa55aa) {
+			ext_csd = ecsd;
+			valid_ecsd = true;
+			goto decode;
+		} else {
+			dev_dbg(dev, "invalid ecsd tag!");
+		}
+	} else {
+		dev_info(dev, "not find \"memory-region\" property\n");
+	}
+
+get_ecsd:
+#endif
 	err = mmc_get_ext_csd(card, &ext_csd);
 	if (err) {
 		/* If the host or the card can't do the switch,
@@ -694,12 +756,22 @@ static int mmc_read_ext_csd(struct mmc_card *card)
 
 		return err;
 	}
-
+#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT
+decode:
+#endif
 	err = mmc_decode_ext_csd(card, ext_csd);
+#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT
+	if (!valid_ecsd)
+		kfree(ext_csd);
+	else
+		vunmap(ecsd);
+#else
 	kfree(ext_csd);
+#endif
 	return err;
 }
 
+#ifndef CONFIG_ROCKCHIP_THUNDER_BOOT
 static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width)
 {
 	u8 *bw_ext_csd;
@@ -772,6 +844,7 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width)
 	kfree(bw_ext_csd);
 	return err;
 }
+#endif
 
 MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
 	card->raw_cid[2], card->raw_cid[3]);
@@ -972,7 +1045,7 @@ static int mmc_select_powerclass(struct mmc_card *card)
 /*
  * Set the bus speed for the selected speed mode.
  */
-static void mmc_set_bus_speed(struct mmc_card *card)
+void mmc_set_bus_speed(struct mmc_card *card)
 {
 	unsigned int max_dtr = (unsigned int)-1;
 
@@ -992,7 +1065,7 @@ static void mmc_set_bus_speed(struct mmc_card *card)
  * If the bus width is changed successfully, return the selected width value.
  * Zero is returned instead of error value if the wide width is not supported.
  */
-static int mmc_select_bus_width(struct mmc_card *card)
+int mmc_select_bus_width(struct mmc_card *card)
 {
 	static unsigned ext_csd_bits[] = {
 		EXT_CSD_BUS_WIDTH_8,
@@ -1041,11 +1114,12 @@ static int mmc_select_bus_width(struct mmc_card *card)
 		 * compare ext_csd previously read in 1 bit mode
 		 * against ext_csd at new bus width
 		 */
+#ifndef CONFIG_ROCKCHIP_THUNDER_BOOT
 		if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST))
 			err = mmc_compare_ext_csds(card, bus_width);
 		else
 			err = mmc_bus_test(card, bus_width);
-
+#endif
 		if (!err) {
 			err = bus_width;
 			break;
@@ -1057,11 +1131,12 @@ static int mmc_select_bus_width(struct mmc_card *card)
 
 	return err;
 }
+EXPORT_SYMBOL_GPL(mmc_select_bus_width);
 
 /*
  * Switch to the high-speed mode
  */
-static int mmc_select_hs(struct mmc_card *card)
+int mmc_select_hs(struct mmc_card *card)
 {
 	int err;
 
@@ -1075,11 +1150,12 @@ static int mmc_select_hs(struct mmc_card *card)
 
 	return err;
 }
+EXPORT_SYMBOL_GPL(mmc_select_hs);
 
 /*
  * Activate wide bus and DDR if supported.
  */
-static int mmc_select_hs_ddr(struct mmc_card *card)
+int mmc_select_hs_ddr(struct mmc_card *card)
 {
 	struct mmc_host *host = card->host;
 	u32 bus_width, ext_csd_bits;
@@ -1148,8 +1224,9 @@ static int mmc_select_hs_ddr(struct mmc_card *card)
 
 	return err;
 }
+EXPORT_SYMBOL_GPL(mmc_select_hs_ddr);
 
-static int mmc_select_hs400(struct mmc_card *card)
+int mmc_select_hs400(struct mmc_card *card)
 {
 	struct mmc_host *host = card->host;
 	unsigned int max_dtr;
@@ -1235,6 +1312,7 @@ static int mmc_select_hs400(struct mmc_card *card)
 	       __func__, err);
 	return err;
 }
+EXPORT_SYMBOL_GPL(mmc_select_hs400);
 
 int mmc_hs200_to_hs400(struct mmc_card *card)
 {
@@ -1378,12 +1456,9 @@ static int mmc_select_hs400es(struct mmc_card *card)
 		goto out_err;
 	}
 
-	/*
-	 * Bump to HS timing and frequency. Some cards don't handle
-	 * SEND_STATUS reliably at the initial frequency.
-	 */
 	mmc_set_timing(host, MMC_TIMING_MMC_HS);
-	mmc_set_bus_speed(card);
+	/* Set clock immediately after changing timing */
+	mmc_set_clock(host, card->ext_csd.hs_max_dtr);
 
 	err = mmc_switch_status(card, true);
 	if (err)
@@ -1446,7 +1521,7 @@ static int mmc_select_hs400es(struct mmc_card *card)
 static int mmc_select_hs200(struct mmc_card *card)
 {
 	struct mmc_host *host = card->host;
-	unsigned int old_timing, old_signal_voltage, old_clock;
+	unsigned int old_timing, old_signal_voltage;
 	int err = -EINVAL;
 	u8 val;
 
@@ -1477,17 +1552,8 @@ static int mmc_select_hs200(struct mmc_card *card)
 				   false, true);
 		if (err)
 			goto err;
-
-		/*
-		 * Bump to HS timing and frequency. Some cards don't handle
-		 * SEND_STATUS reliably at the initial frequency.
-		 * NB: We can't move to full (HS200) speeds until after we've
-		 * successfully switched over.
-		 */
 		old_timing = host->ios.timing;
-		old_clock = host->ios.clock;
 		mmc_set_timing(host, MMC_TIMING_MMC_HS200);
-		mmc_set_clock(card->host, card->ext_csd.hs_max_dtr);
 
 		/*
 		 * For HS200, CRC errors are not a reliable way to know the
@@ -1500,10 +1566,8 @@ static int mmc_select_hs200(struct mmc_card *card)
 		 * mmc_select_timing() assumes timing has not changed if
 		 * it is a switch error.
 		 */
-		if (err == -EBADMSG) {
-			mmc_set_clock(host, old_clock);
+		if (err == -EBADMSG)
 			mmc_set_timing(host, old_timing);
-		}
 	}
 err:
 	if (err) {
@@ -1520,7 +1584,7 @@ static int mmc_select_hs200(struct mmc_card *card)
 /*
  * Activate High Speed, HS200 or HS400ES mode if supported.
  */
-static int mmc_select_timing(struct mmc_card *card)
+int mmc_select_timing(struct mmc_card *card)
 {
 	int err = 0;
 
@@ -1545,12 +1609,13 @@ static int mmc_select_timing(struct mmc_card *card)
 	mmc_set_bus_speed(card);
 	return 0;
 }
+EXPORT_SYMBOL_GPL(mmc_select_timing);
 
 /*
  * Execute tuning sequence to seek the proper bus operating
  * conditions for HS200 and HS400, which sends CMD21 to the device.
  */
-static int mmc_hs200_tuning(struct mmc_card *card)
+int mmc_hs200_tuning(struct mmc_card *card)
 {
 	struct mmc_host *host = card->host;
 
@@ -1565,6 +1630,7 @@ static int mmc_hs200_tuning(struct mmc_card *card)
 
 	return mmc_execute_tuning(card);
 }
+EXPORT_SYMBOL_GPL(mmc_hs200_tuning);
 
 /*
  * Handle the detection and initialisation of a card.
@@ -1593,7 +1659,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
 	 * respond.
 	 * mmc_go_idle is needed for eMMC that are asleep
 	 */
+#ifndef CONFIG_ROCKCHIP_THUNDER_BOOT
 	mmc_go_idle(host);
+#endif
 
 	/* The extra bit indicates that we support high capacity */
 	err = mmc_send_op_cond(host, ocr | (1 << 30), &rocr);
@@ -1638,7 +1706,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
 		card->ocr = ocr;
 		card->type = MMC_TYPE_MMC;
 		card->rca = 1;
+#ifndef CONFIG_ROCKCHIP_THUNDER_BOOT
 		memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
+#endif
 	}
 
 	/*
@@ -1669,9 +1739,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
 		err = mmc_decode_csd(card);
 		if (err)
 			goto free_card;
+#ifndef CONFIG_ROCKCHIP_THUNDER_BOOT
 		err = mmc_decode_cid(card);
 		if (err)
 			goto free_card;
+#endif
 	}
 
 	/*
@@ -1814,6 +1886,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
 	/*
 	 * Enable HPI feature (if supported)
 	 */
+#ifndef CONFIG_ROCKCHIP_THUNDER_BOOT
 	if (card->ext_csd.hpi) {
 		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
 				EXT_CSD_HPI_MGMT, 1,
@@ -1829,7 +1902,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
 			card->ext_csd.hpi_en = 1;
 		}
 	}
-
+#endif
 	/*
 	 * If cache size is higher than 0, this indicates the existence of cache
 	 * and it can be turned on. Note that some eMMCs from Micron has been
@@ -2110,14 +2183,29 @@ static int mmc_suspend(struct mmc_host *host)
 static int _mmc_resume(struct mmc_host *host)
 {
 	int err = 0;
+	int i;
 
 	mmc_claim_host(host);
 
 	if (!mmc_card_suspended(host->card))
 		goto out;
 
-	mmc_power_up(host, host->card->ocr);
-	err = mmc_init_card(host, host->card->ocr, host->card);
+	/*
+	 * Let's try to fallback the host->f_init
+	 * if failing to init mmc card after resume.
+	 */
+	for (i = 0; i < ARRAY_SIZE(freqs); i++) {
+		if (host->f_init < max(freqs[i], host->f_min))
+			continue;
+		else
+			host->f_init = max(freqs[i], host->f_min);
+
+		mmc_power_up(host, host->card->ocr);
+		err = mmc_init_card(host, host->card->ocr, host->card);
+		if (!err)
+			break;
+	}
+
 	mmc_card_clr_suspended(host->card);
 
 out:
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index ebad70e44..2715e5031 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -145,24 +145,26 @@ int mmc_go_idle(struct mmc_host *host)
 	 * rules that must accommodate non-MMC slaves which this layer
 	 * won't even know about.
 	 */
+#ifndef CONFIG_ROCKCHIP_THUNDER_BOOT
 	if (!mmc_host_is_spi(host)) {
 		mmc_set_chip_select(host, MMC_CS_HIGH);
 		mmc_delay(1);
 	}
-
+#endif
 	cmd.opcode = MMC_GO_IDLE_STATE;
 	cmd.arg = 0;
 	cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
 
 	err = mmc_wait_for_cmd(host, &cmd, 0);
 
+#ifndef CONFIG_ROCKCHIP_THUNDER_BOOT
 	mmc_delay(1);
 
 	if (!mmc_host_is_spi(host)) {
 		mmc_set_chip_select(host, MMC_CS_DONTCARE);
 		mmc_delay(1);
 	}
-
+#endif
 	host->use_spi_crc = 0;
 
 	return err;
@@ -182,6 +184,15 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
 		if (err)
 			break;
 
+		/*
+		 * According to eMMC specification v5.1 section A6.1, the R3
+		 * response value should be 0x00FF8080, 0x40FF8080, 0x80FF8080
+		 * or 0xC0FF8080. The EMMC device may be abnormal if a wrong
+		 * OCR data is configured.
+		 */
+		if ((cmd.resp[0] & 0xFFFFFF) != 0x00FF8080)
+			continue;
+
 		/* wait until reset completes */
 		if (mmc_host_is_spi(host)) {
 			if (!(cmd.resp[0] & R1_SPI_IDLE))
@@ -193,8 +204,6 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
 
 		err = -ETIMEDOUT;
 
-		mmc_delay(10);
-
 		/*
 		 * According to eMMC specification v5.1 section 6.4.3, we
 		 * should issue CMD1 repeatedly in the idle state until
@@ -204,6 +213,11 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
 		 */
 		if (!ocr && !mmc_host_is_spi(host))
 			cmd.arg = cmd.resp[0] | BIT(30);
+#ifndef CONFIG_ROCKCHIP_THUNDER_BOOT
+		mmc_delay(1);
+#else
+		udelay(1);
+#endif
 	}
 
 	if (rocr && !mmc_host_is_spi(host))
@@ -989,9 +1003,10 @@ int mmc_flush_cache(struct mmc_card *card)
 	int err = 0;
 
 	if (mmc_cache_enabled(card->host)) {
-		err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
-				 EXT_CSD_FLUSH_CACHE, 1,
-				 MMC_CACHE_FLUSH_TIMEOUT_MS);
+		err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+				   EXT_CSD_FLUSH_CACHE, 1,
+				   MMC_CACHE_FLUSH_TIMEOUT_MS, 0,
+				   false, false);
 		if (err)
 			pr_err("%s: cache flush error %d\n",
 					mmc_hostname(card->host), err);
diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c
index 002426e3c..2e2db41be 100644
--- a/drivers/mmc/core/queue.c
+++ b/drivers/mmc/core/queue.c
@@ -19,6 +19,7 @@
 #include "block.h"
 #include "core.h"
 #include "card.h"
+#include "../../../vendor/include/linux/crypto.h"
 #include "host.h"
 
 #define MMC_DMA_MAP_MERGE_SEGMENTS	512
@@ -70,6 +71,7 @@ enum mmc_issue_type mmc_issue_type(struct mmc_queue *mq, struct request *req)
 
 	return MMC_ISSUE_SYNC;
 }
+EXPORT_SYMBOL_GPL(mmc_issue_type);
 
 static void __mmc_cqe_recovery_notifier(struct mmc_queue *mq)
 {
@@ -407,6 +409,8 @@ static void mmc_setup_queue(struct mmc_queue *mq, struct mmc_card *card)
 	mutex_init(&mq->complete_lock);
 
 	init_waitqueue_head(&mq->wait);
+
+	mmc_crypto_setup_queue(mq->queue, host);
 }
 
 static inline bool mmc_merge_capable(struct mmc_host *host)
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 868b121ce..e7f2eebd8 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -18,6 +18,8 @@
 #include <linux/mmc/mmc.h>
 #include <linux/mmc/sd.h>
 
+#include <trace/hooks/mmc_core.h>
+
 #include "core.h"
 #include "card.h"
 #include "host.h"
@@ -462,6 +464,8 @@ static void sd_update_bus_speed_mode(struct mmc_card *card)
 		    SD_MODE_UHS_SDR12)) {
 			card->sd_bus_speed = UHS_SDR12_BUS_SPEED;
 	}
+
+	trace_android_vh_sd_update_bus_speed_mode(card);
 }
 
 static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
@@ -1228,6 +1232,49 @@ static int _mmc_sd_suspend(struct mmc_host *host)
 	return err;
 }
 
+static int _mmc_sd_shutdown(struct mmc_host *host)
+{
+	int err = 0;
+
+	if (WARN_ON(!host) || WARN_ON(!host->card))
+		return 0;
+
+	mmc_claim_host(host);
+
+	if (mmc_card_suspended(host->card))
+		goto out;
+
+	if (!mmc_host_is_spi(host))
+		err = mmc_deselect_cards(host);
+
+	if (!err) {
+		mmc_power_off(host);
+		mmc_card_set_suspended(host->card);
+	}
+
+	host->ios.signal_voltage = MMC_SIGNAL_VOLTAGE_330;
+	host->ios.vdd = fls(host->ocr_avail) - 1;
+	mmc_regulator_set_vqmmc(host, &host->ios);
+	pr_info("Set signal voltage to initial state\n");
+
+out:
+	mmc_release_host(host);
+	return err;
+}
+
+static int mmc_sd_shutdown(struct mmc_host *host)
+{
+	int err;
+
+	err = _mmc_sd_shutdown(host);
+	if (!err) {
+		pm_runtime_disable(&host->card->dev);
+		pm_runtime_set_suspended(&host->card->dev);
+	}
+
+	return err;
+}
+
 /*
  * Callback for suspend
  */
@@ -1322,7 +1369,7 @@ static const struct mmc_bus_ops mmc_sd_ops = {
 	.suspend = mmc_sd_suspend,
 	.resume = mmc_sd_resume,
 	.alive = mmc_sd_alive,
-	.shutdown = mmc_sd_suspend,
+	.shutdown = mmc_sd_shutdown,
 	.hw_reset = mmc_sd_hw_reset,
 };
 
@@ -1396,5 +1443,7 @@ int mmc_attach_sd(struct mmc_host *host)
 	pr_err("%s: error %d whilst initialising SD card\n",
 		mmc_hostname(host), err);
 
+	trace_android_vh_mmc_attach_sd(host, ocr, err);
+
 	return err;
 }
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 99a4ce68d..108b0f2ae 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -1308,3 +1308,49 @@ int mmc_attach_sdio(struct mmc_host *host)
 	return err;
 }
 
+int sdio_reset_comm(struct mmc_card *card)
+{
+	struct mmc_host *host = card->host;
+	u32 ocr;
+	u32 rocr;
+	int err;
+
+#ifdef CONFIG_SDIO_KEEPALIVE
+	if (host->chip_alive)
+		host->chip_alive = 0;
+#endif
+
+	printk("%s():\n", __func__);
+	mmc_claim_host(host);
+
+	mmc_retune_disable(host);
+
+	mmc_power_cycle(host, host->card->ocr);
+	mmc_go_idle(host);
+
+	mmc_set_clock(host, host->f_min);
+
+	err = mmc_send_io_op_cond(host, 0, &ocr);
+	if (err)
+		goto err;
+
+	rocr = mmc_select_voltage(host, ocr);
+	if (!rocr) {
+		err = -EINVAL;
+		goto err;
+	}
+
+	err = mmc_sdio_init_card(host, rocr, card);
+	if (err)
+		goto err;
+
+	mmc_release_host(host);
+	return 0;
+err:
+	printk("%s: Error resetting SDIO communications (%d)\n",
+	       mmc_hostname(host), err);
+	mmc_release_host(host);
+	return err;
+	return 0;
+}
+EXPORT_SYMBOL(sdio_reset_comm);
diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c
index 05e907451..298877a50 100644
--- a/drivers/mmc/core/slot-gpio.c
+++ b/drivers/mmc/core/slot-gpio.c
@@ -14,6 +14,8 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 
+#include <trace/hooks/mmc_core.h>
+
 #include "slot-gpio.h"
 
 struct mmc_gpio {
@@ -30,6 +32,11 @@ static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
 	/* Schedule a card detection after a debounce timeout */
 	struct mmc_host *host = dev_id;
 	struct mmc_gpio *ctx = host->slot.handler_priv;
+	bool allow = true;
+
+	trace_android_vh_mmc_gpio_cd_irqt(host, &allow);
+	if (!allow)
+		return IRQ_HANDLED;
 
 	host->trigger_card_event = true;
 	mmc_detect_change(host, msecs_to_jiffies(ctx->cd_debounce_delay_ms));
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 82e1fbd6b..c73238181 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -544,6 +544,7 @@ config MMC_SDHCI_MSM
 	depends on MMC_SDHCI_PLTFM
 	select MMC_SDHCI_IO_ACCESSORS
 	select MMC_CQHCI
+	select QCOM_SCM if MMC_CRYPTO && ARCH_QCOM
 	help
 	  This selects the Secure Digital Host Controller Interface (SDHCI)
 	  support present in Qualcomm SOCs. The controller supports
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 451c25fc2..d4fec19a1 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -103,7 +103,7 @@ obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32)	+= sdhci-pic32.o
 obj-$(CONFIG_MMC_SDHCI_BRCMSTB)		+= sdhci-brcmstb.o
 obj-$(CONFIG_MMC_SDHCI_OMAP)		+= sdhci-omap.o
 obj-$(CONFIG_MMC_SDHCI_SPRD)		+= sdhci-sprd.o
-obj-$(CONFIG_MMC_CQHCI)			+= cqhci.o
+#obj-$(CONFIG_MMC_CQHCI)			+= cqhci.o
 obj-$(CONFIG_MMC_HSQ)			+= mmc_hsq.o
 
 ifeq ($(CONFIG_CB710_DEBUG),y)
diff --git a/drivers/mmc/host/cqhci.h b/drivers/mmc/host/cqhci.h
index 89bf6adbc..ba9387ed9 100644
--- a/drivers/mmc/host/cqhci.h
+++ b/drivers/mmc/host/cqhci.h
@@ -22,10 +22,13 @@
 
 /* capabilities */
 #define CQHCI_CAP			0x04
+#define CQHCI_CAP_CS			0x10000000 /* Crypto Support */
+
 /* configuration */
 #define CQHCI_CFG			0x08
 #define CQHCI_DCMD			0x00001000
 #define CQHCI_TASK_DESC_SZ		0x00000100
+#define CQHCI_CRYPTO_GENERAL_ENABLE	0x00000002
 #define CQHCI_ENABLE			0x00000001
 
 /* control */
@@ -39,8 +42,11 @@
 #define CQHCI_IS_TCC			BIT(1)
 #define CQHCI_IS_RED			BIT(2)
 #define CQHCI_IS_TCL			BIT(3)
+#define CQHCI_IS_GCE			BIT(4) /* General Crypto Error */
+#define CQHCI_IS_ICCE			BIT(5) /* Invalid Crypto Config Error */
 
-#define CQHCI_IS_MASK (CQHCI_IS_TCC | CQHCI_IS_RED)
+#define CQHCI_IS_MASK (CQHCI_IS_TCC | CQHCI_IS_RED | \
+		       CQHCI_IS_GCE | CQHCI_IS_ICCE)
 
 /* interrupt status enable */
 #define CQHCI_ISTE			0x14
@@ -78,6 +84,9 @@
 /* task clear */
 #define CQHCI_TCLR			0x38
 
+/* task descriptor processing error */
+#define CQHCI_TDPE			0x3c
+
 /* send status config 1 */
 #define CQHCI_SSC1			0x40
 #define CQHCI_SSC1_CBC_MASK		GENMASK(19, 16)
@@ -107,6 +116,10 @@
 /* command response argument */
 #define CQHCI_CRA			0x5C
 
+/* crypto capabilities */
+#define CQHCI_CCAP			0x100
+#define CQHCI_CRYPTOCAP			0x104
+
 #define CQHCI_INT_ALL			0xF
 #define CQHCI_IC_DEFAULT_ICCTH		31
 #define CQHCI_IC_DEFAULT_ICTOVAL	1
@@ -133,11 +146,70 @@
 #define CQHCI_CMD_TIMING(x)		(((x) & 1) << 22)
 #define CQHCI_RESP_TYPE(x)		(((x) & 0x3) << 23)
 
+/* crypto task descriptor fields (for bits 64-127 of task descriptor) */
+#define CQHCI_CRYPTO_ENABLE_BIT		(1ULL << 47)
+#define CQHCI_CRYPTO_KEYSLOT(x)		((u64)(x) << 32)
+
 /* transfer descriptor fields */
 #define CQHCI_DAT_LENGTH(x)		(((x) & 0xFFFF) << 16)
 #define CQHCI_DAT_ADDR_LO(x)		(((x) & 0xFFFFFFFF) << 32)
 #define CQHCI_DAT_ADDR_HI(x)		(((x) & 0xFFFFFFFF) << 0)
 
+/* CCAP - Crypto Capability 100h */
+union cqhci_crypto_capabilities {
+	__le32 reg_val;
+	struct {
+		u8 num_crypto_cap;
+		u8 config_count;
+		u8 reserved;
+		u8 config_array_ptr;
+	};
+};
+
+enum cqhci_crypto_key_size {
+	CQHCI_CRYPTO_KEY_SIZE_INVALID	= 0,
+	CQHCI_CRYPTO_KEY_SIZE_128	= 1,
+	CQHCI_CRYPTO_KEY_SIZE_192	= 2,
+	CQHCI_CRYPTO_KEY_SIZE_256	= 3,
+	CQHCI_CRYPTO_KEY_SIZE_512	= 4,
+};
+
+enum cqhci_crypto_alg {
+	CQHCI_CRYPTO_ALG_AES_XTS		= 0,
+	CQHCI_CRYPTO_ALG_BITLOCKER_AES_CBC	= 1,
+	CQHCI_CRYPTO_ALG_AES_ECB		= 2,
+	CQHCI_CRYPTO_ALG_ESSIV_AES_CBC		= 3,
+};
+
+/* x-CRYPTOCAP - Crypto Capability X */
+union cqhci_crypto_cap_entry {
+	__le32 reg_val;
+	struct {
+		u8 algorithm_id;
+		u8 sdus_mask; /* Supported data unit size mask */
+		u8 key_size;
+		u8 reserved;
+	};
+};
+
+#define CQHCI_CRYPTO_CONFIGURATION_ENABLE (1 << 7)
+#define CQHCI_CRYPTO_KEY_MAX_SIZE 64
+/* x-CRYPTOCFG - Crypto Configuration X */
+union cqhci_crypto_cfg_entry {
+	__le32 reg_val[32];
+	struct {
+		u8 crypto_key[CQHCI_CRYPTO_KEY_MAX_SIZE];
+		u8 data_unit_size;
+		u8 crypto_cap_idx;
+		u8 reserved_1;
+		u8 config_enable;
+		u8 reserved_multi_host;
+		u8 reserved_2;
+		u8 vsb[2];
+		u8 reserved_3[56];
+	};
+};
+
 struct cqhci_host_ops;
 struct mmc_host;
 struct mmc_request;
@@ -196,6 +268,12 @@ struct cqhci_host {
 	struct completion halt_comp;
 	wait_queue_head_t wait_queue;
 	struct cqhci_slot *slot;
+
+#ifdef CONFIG_MMC_CRYPTO
+	union cqhci_crypto_capabilities crypto_capabilities;
+	union cqhci_crypto_cap_entry *crypto_cap_array;
+	u32 crypto_cfg_register;
+#endif
 };
 
 struct cqhci_host_ops {
@@ -208,6 +286,10 @@ struct cqhci_host_ops {
 				 u64 *data);
 	void (*pre_enable)(struct mmc_host *mmc);
 	void (*post_disable)(struct mmc_host *mmc);
+#ifdef CONFIG_MMC_CRYPTO
+	int (*program_key)(struct cqhci_host *cq_host,
+			   const union cqhci_crypto_cfg_entry *cfg, int slot);
+#endif
 };
 
 static inline void cqhci_writel(struct cqhci_host *host, u32 val, int reg)
diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c
index 753502ce3..4a788d4c4 100644
--- a/drivers/mmc/host/dw_mmc-rockchip.c
+++ b/drivers/mmc/host/dw_mmc-rockchip.c
@@ -22,6 +22,9 @@ struct dw_mci_rockchip_priv_data {
 	struct clk		*sample_clk;
 	int			default_sample_phase;
 	int			num_phases;
+	bool			use_v2_tuning;
+	int			last_degree;
+	u32			f_min;
 };
 
 static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
@@ -43,6 +46,11 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
 	 * Note: div can only be 0 or 1, but div must be set to 1 for eMMC
 	 * DDR52 8-bit mode.
 	 */
+	if (ios->clock < priv->f_min) {
+		ios->clock = priv->f_min;
+		host->slot->clock = ios->clock;
+	}
+
 	if (ios->bus_width == MMC_BUS_WIDTH_8 &&
 	    ios->timing == MMC_TIMING_MMC_DDR52)
 		cclkin = 2 * ios->clock * RK3288_CLKGEN_DIV;
@@ -61,7 +69,7 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
 	}
 
 	/* Make sure we use phases which we can enumerate with */
-	if (!IS_ERR(priv->sample_clk))
+	if (!IS_ERR(priv->sample_clk) && ios->timing <= MMC_TIMING_SD_HS)
 		clk_set_phase(priv->sample_clk, priv->default_sample_phase);
 
 	/*
@@ -132,6 +140,49 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
 #define TUNING_ITERATION_TO_PHASE(i, num_phases) \
 		(DIV_ROUND_UP((i) * 360, num_phases))
 
+static int dw_mci_v2_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
+{
+	struct dw_mci *host = slot->host;
+	struct dw_mci_rockchip_priv_data *priv = host->priv;
+	struct mmc_host *mmc = slot->mmc;
+	u32 degrees[4] = {0, 90, 180, 270}, degree;
+	int i;
+	static bool inherit = true;
+
+	if (inherit) {
+		inherit = false;
+		i = clk_get_phase(priv->sample_clk) / 90;
+		degree = degrees[i];
+		goto done;
+	}
+
+	/*
+	 * v2 only support 4 degrees in theory.
+	 * First we inherit sample phases from firmware, which should
+	 * be able work fine, at least in the first place.
+	 * If retune is needed, we search forward to pick the last
+	 * one phase from degree list and loop around until we get one.
+	 * It's impossible all 4 fixed phase won't be able to work.
+	 */
+	for (i = 0; i < ARRAY_SIZE(degrees); i++) {
+		degree = degrees[i] + priv->last_degree;
+		degree = degree % 360;
+		clk_set_phase(priv->sample_clk, degree);
+		if (!mmc_send_tuning(mmc, opcode, NULL))
+			break;
+	}
+
+	if (i == ARRAY_SIZE(degrees)) {
+		dev_warn(host->dev, "All phases bad!");
+		return -EIO;
+	}
+
+done:
+	dev_info(host->dev, "Successfully tuned phase to %d\n", degrees[i]);
+	priv->last_degree = degree;
+	return 0;
+}
+
 static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
 {
 	struct dw_mci *host = slot->host;
@@ -148,13 +199,20 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
 	unsigned int range_count = 0;
 	int longest_range_len = -1;
 	int longest_range = -1;
-	int middle_phase;
+	int middle_phase, real_middle_phase;
 
 	if (IS_ERR(priv->sample_clk)) {
 		dev_err(host->dev, "Tuning clock (sample_clk) not defined.\n");
 		return -EIO;
 	}
 
+	if (priv->use_v2_tuning) {
+		ret = dw_mci_v2_execute_tuning(slot, opcode);
+		if (!ret)
+			return 0;
+		/* Otherwise we continue using fine tuning */
+	}
+
 	ranges = kmalloc_array(priv->num_phases / 2 + 1,
 			       sizeof(*ranges), GFP_KERNEL);
 	if (!ranges)
@@ -162,6 +220,9 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
 
 	/* Try each phase and extract good ranges */
 	for (i = 0; i < priv->num_phases; ) {
+		/* Cannot guarantee any phases larger than 270 would work well */
+		if (TUNING_ITERATION_TO_PHASE(i, priv->num_phases) > 270)
+			break;
 		clk_set_phase(priv->sample_clk,
 			      TUNING_ITERATION_TO_PHASE(i, priv->num_phases));
 
@@ -246,12 +307,30 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode)
 
 	middle_phase = ranges[longest_range].start + longest_range_len / 2;
 	middle_phase %= priv->num_phases;
+	real_middle_phase = TUNING_ITERATION_TO_PHASE(middle_phase, priv->num_phases);
+
+	/*
+	 * Since we cut out 270 ~ 360, the original algorithm
+	 * still rolling ranges before and after 270 together
+	 * in some corner cases, we should adjust it to avoid
+	 * using any middle phase located between 270 and 360.
+	 * By calculatiion, it happends due to the bad phases
+	 * lay between 90 ~ 180. So others are all fine to chose.
+	 * Pick 270 is a better choice in those cases. In case of
+	 * bad phases exceed 180, the middle phase of rollback
+	 * would be bigger than 315, so we chose 360.
+	 */
+	if (real_middle_phase > 270) {
+		if (real_middle_phase < 315)
+			real_middle_phase = 270;
+		else
+			real_middle_phase = 360;
+	}
+
 	dev_info(host->dev, "Successfully tuned phase to %d\n",
-		 TUNING_ITERATION_TO_PHASE(middle_phase, priv->num_phases));
+		 real_middle_phase);
 
-	clk_set_phase(priv->sample_clk,
-		      TUNING_ITERATION_TO_PHASE(middle_phase,
-						priv->num_phases));
+	clk_set_phase(priv->sample_clk, real_middle_phase);
 
 free:
 	kfree(ranges);
@@ -267,6 +346,17 @@ static int dw_mci_rk3288_parse_dt(struct dw_mci *host)
 	if (!priv)
 		return -ENOMEM;
 
+	/*
+	 * RK356X SoCs only support 375KHz for ID mode, so any clk request
+	 * that less than 1.6MHz(2 * 400KHz * RK3288_CLKGEN_DIV) should be
+	 * wrapped  into 375KHz
+	 */
+	if (of_device_is_compatible(host->dev->of_node,
+				    "rockchip,rk3568-dw-mshc"))
+		priv->f_min = 375000;
+	else
+		priv->f_min = 100000;
+
 	if (of_property_read_u32(np, "rockchip,desired-num-phases",
 					&priv->num_phases))
 		priv->num_phases = 360;
@@ -275,6 +365,9 @@ static int dw_mci_rk3288_parse_dt(struct dw_mci *host)
 					&priv->default_sample_phase))
 		priv->default_sample_phase = 0;
 
+	if (of_property_read_bool(np, "rockchip,use-v2-tuning"))
+		priv->use_v2_tuning = true;
+
 	priv->drv_clk = devm_clk_get(host->dev, "ciu-drive");
 	if (IS_ERR(priv->drv_clk))
 		dev_dbg(host->dev, "ciu-drive not available\n");
@@ -297,6 +390,7 @@ static int dw_mci_rockchip_init(struct dw_mci *host)
 				    "rockchip,rk3288-dw-mshc"))
 		host->bus_hz /= RK3288_CLKGEN_DIV;
 
+	host->need_xfer_timer = true;
 	return 0;
 }
 
@@ -335,28 +429,43 @@ static int dw_mci_rockchip_probe(struct platform_device *pdev)
 	const struct dw_mci_drv_data *drv_data;
 	const struct of_device_id *match;
 	int ret;
+	bool use_rpm = true;
 
 	if (!pdev->dev.of_node)
 		return -ENODEV;
 
+	if (!device_property_read_bool(&pdev->dev, "non-removable") &&
+	    !device_property_read_bool(&pdev->dev, "cd-gpios"))
+		use_rpm = false;
+
 	match = of_match_node(dw_mci_rockchip_match, pdev->dev.of_node);
 	drv_data = match->data;
 
+	/*
+	 * increase rpm usage count in order to make
+	 * pm_runtime_force_resume calls rpm resume callback
+	 */
 	pm_runtime_get_noresume(&pdev->dev);
 	pm_runtime_set_active(&pdev->dev);
-	pm_runtime_enable(&pdev->dev);
-	pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
-	pm_runtime_use_autosuspend(&pdev->dev);
+
+	if (use_rpm) {
+		pm_runtime_enable(&pdev->dev);
+		pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
+		pm_runtime_use_autosuspend(&pdev->dev);
+	}
 
 	ret = dw_mci_pltfm_register(pdev, drv_data);
 	if (ret) {
-		pm_runtime_disable(&pdev->dev);
-		pm_runtime_set_suspended(&pdev->dev);
+		if (use_rpm) {
+			pm_runtime_disable(&pdev->dev);
+			pm_runtime_set_suspended(&pdev->dev);
+		}
 		pm_runtime_put_noidle(&pdev->dev);
 		return ret;
 	}
 
-	pm_runtime_put_autosuspend(&pdev->dev);
+	if (use_rpm)
+		pm_runtime_put_autosuspend(&pdev->dev);
 
 	return 0;
 }
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index a6170f80b..338fac33f 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -18,6 +18,7 @@
 #include <linux/iopoll.h>
 #include <linux/ioport.h>
 #include <linux/module.h>
+#include <linux/of_address.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/seq_file.h>
@@ -35,6 +36,7 @@
 #include <linux/of.h>
 #include <linux/of_gpio.h>
 #include <linux/mmc/slot-gpio.h>
+#include <linux/soc/rockchip/rockchip_decompress.h>
 
 #include "dw_mmc.h"
 
@@ -208,6 +210,7 @@ static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset)
 static void dw_mci_wait_while_busy(struct dw_mci *host, u32 cmd_flags)
 {
 	u32 status;
+	u32 delay = 10;
 
 	/*
 	 * Databook says that before issuing a new data transfer command
@@ -217,12 +220,17 @@ static void dw_mci_wait_while_busy(struct dw_mci *host, u32 cmd_flags)
 	 * ...also allow sending for SDMMC_CMD_VOLT_SWITCH where busy is
 	 * expected.
 	 */
+#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT
+	if (host->slot->mmc->caps2 & MMC_CAP2_NO_SD &&
+	    host->slot->mmc->caps2 & MMC_CAP2_NO_SDIO)
+		delay = 0;
+#endif
 	if ((cmd_flags & SDMMC_CMD_PRV_DAT_WAIT) &&
 	    !(cmd_flags & SDMMC_CMD_VOLT_SWITCH)) {
 		if (readl_poll_timeout_atomic(host->regs + SDMMC_STATUS,
 					      status,
 					      !(status & SDMMC_STATUS_BUSY),
-					      10, 500 * USEC_PER_MSEC))
+					      delay, 500 * USEC_PER_MSEC))
 			dev_err(host->dev, "Busy; trying anyway\n");
 	}
 }
@@ -491,6 +499,10 @@ static void dw_mci_dmac_complete_dma(void *arg)
 		set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
 		tasklet_schedule(&host->tasklet);
 	}
+
+	if (host->need_xfer_timer &&
+	    host->dir_status == DW_MCI_RECV_STATUS)
+		del_timer(&host->xfer_timer);
 }
 
 static int dw_mci_idmac_init(struct dw_mci *host)
@@ -1923,6 +1935,30 @@ static void dw_mci_set_drto(struct dw_mci *host)
 	spin_unlock_irqrestore(&host->irq_lock, irqflags);
 }
 
+static void dw_mci_set_xfer_timeout(struct dw_mci *host)
+{
+	unsigned int xfer_clks;
+	unsigned int xfer_div;
+	unsigned int xfer_ms;
+	unsigned long irqflags;
+
+	xfer_clks = mci_readl(host, TMOUT) >> 8;
+	xfer_div = (mci_readl(host, CLKDIV) & 0xff) * 2;
+	if (xfer_div == 0)
+		xfer_div = 1;
+	xfer_ms = DIV_ROUND_UP_ULL((u64)MSEC_PER_SEC * xfer_clks * xfer_div,
+				   host->bus_hz);
+
+	/* add a bit spare time */
+	xfer_ms += 100;
+
+	spin_lock_irqsave(&host->irq_lock, irqflags);
+	if (!test_bit(EVENT_XFER_COMPLETE, &host->pending_events))
+		mod_timer(&host->xfer_timer,
+			  jiffies + msecs_to_jiffies(xfer_ms));
+	spin_unlock_irqrestore(&host->irq_lock, irqflags);
+}
+
 static bool dw_mci_clear_pending_cmd_complete(struct dw_mci *host)
 {
 	if (!test_bit(EVENT_CMD_COMPLETE, &host->pending_events))
@@ -2061,6 +2097,9 @@ static void dw_mci_tasklet_func(unsigned long priv)
 				 */
 				if (host->dir_status == DW_MCI_RECV_STATUS)
 					dw_mci_set_drto(host);
+				if (host->need_xfer_timer &&
+				    host->dir_status == DW_MCI_RECV_STATUS)
+					dw_mci_set_xfer_timeout(host);
 				break;
 			}
 
@@ -2535,6 +2574,8 @@ static void dw_mci_read_data_pio(struct dw_mci *host, bool dto)
 	host->sg = NULL;
 	smp_wmb(); /* drain writebuffer */
 	set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
+	if (host->need_xfer_timer)
+		del_timer(&host->xfer_timer);
 }
 
 static void dw_mci_write_data_pio(struct dw_mci *host)
@@ -2647,6 +2688,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
 			del_timer(&host->cto_timer);
 			mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
 			host->cmd_status = pending;
+			if ((host->need_xfer_timer) &&
+			     host->dir_status == DW_MCI_RECV_STATUS)
+				del_timer(&host->xfer_timer);
 			smp_wmb(); /* drain writebuffer */
 			set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
 
@@ -3032,6 +3076,36 @@ static void dw_mci_cto_timer(struct timer_list *t)
 	spin_unlock_irqrestore(&host->irq_lock, irqflags);
 }
 
+static void dw_mci_xfer_timer(struct timer_list *t)
+{
+	struct dw_mci *host = from_timer(host, t, xfer_timer);
+	unsigned long irqflags;
+
+	spin_lock_irqsave(&host->irq_lock, irqflags);
+
+	if (test_bit(EVENT_XFER_COMPLETE, &host->pending_events)) {
+		/* Presumably interrupt handler couldn't delete the timer */
+		dev_warn(host->dev, "xfer when already completed\n");
+		goto exit;
+	}
+
+	switch (host->state) {
+	case STATE_SENDING_DATA:
+		host->data_status = SDMMC_INT_DRTO;
+		set_bit(EVENT_DATA_ERROR, &host->pending_events);
+		set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
+		tasklet_schedule(&host->tasklet);
+		break;
+	default:
+		dev_warn(host->dev, "Unexpected xfer timeout, state %d\n",
+			 host->state);
+		break;
+	}
+
+exit:
+	spin_unlock_irqrestore(&host->irq_lock, irqflags);
+}
+
 static void dw_mci_dto_timer(struct timer_list *t)
 {
 	struct dw_mci *host = from_timer(host, t, dto_timer);
@@ -3161,6 +3235,60 @@ int dw_mci_probe(struct dw_mci *host)
 	int width, i, ret = 0;
 	u32 fifo_size;
 
+#if defined(CONFIG_ROCKCHIP_THUNDER_BOOT) && defined(CONFIG_ROCKCHIP_HW_DECOMPRESS)
+	struct resource idmac, ramdisk_src, ramdisk_dst;
+	struct device_node *dma, *rds, *rdd;
+	struct device *dev = host->dev;
+	u32 intr;
+
+	if (host->slot->mmc->caps2 & MMC_CAP2_NO_SD &&
+	    host->slot->mmc->caps2 & MMC_CAP2_NO_SDIO) {
+		if (readl_poll_timeout(host->regs + SDMMC_STATUS,
+				fifo_size,
+				!(fifo_size & (BIT(10) | GENMASK(7, 4))),
+				0, 500 * USEC_PER_MSEC))
+			dev_err(dev, "Controller is occupied!\n");
+
+		if (readl_poll_timeout(host->regs + SDMMC_IDSTS,
+				fifo_size, !(fifo_size & GENMASK(16, 13)),
+				0, 500 * USEC_PER_MSEC))
+			dev_err(dev, "DMA is still running!\n");
+
+		intr = mci_readl(host, RINTSTS);
+		if (intr & DW_MCI_CMD_ERROR_FLAGS || intr & DW_MCI_DATA_ERROR_FLAGS) {
+			WARN_ON(1);
+			return -EINVAL;
+		}
+
+		/* Release idmac descriptor */
+		dma = of_parse_phandle(dev->of_node, "memory-region-idamc", 0);
+		if (dma) {
+			ret = of_address_to_resource(dma, 0, &idmac);
+			if (ret >= 0)
+				free_reserved_area(phys_to_virt(idmac.start),
+					phys_to_virt(idmac.start) + resource_size(&idmac),
+					-1, NULL);
+		}
+
+		/* Parse ramdisk addr and help start decompressing */
+		rds = of_parse_phandle(dev->of_node, "memory-region-src", 0);
+		rdd = of_parse_phandle(dev->of_node, "memory-region-dst", 0);
+		if (rds && rdd) {
+			if (of_address_to_resource(rds, 0, &ramdisk_src) >= 0 &&
+				of_address_to_resource(rdd, 0, &ramdisk_dst) >= 0)
+				/*
+				 * Decompress HW driver will free reserved area of
+				 * memory-region-src.
+				 */
+				ret = rk_decom_start(GZIP_MOD, ramdisk_src.start,
+						     ramdisk_dst.start,
+						     resource_size(&ramdisk_dst));
+			if (ret < 0)
+				dev_err(dev, "fail to start decom\n");
+		}
+	}
+#endif
+
 	if (!host->pdata) {
 		host->pdata = dw_mci_parse_dt(host);
 		if (IS_ERR(host->pdata))
@@ -3225,6 +3353,8 @@ int dw_mci_probe(struct dw_mci *host)
 	timer_setup(&host->cmd11_timer, dw_mci_cmd11_timer, 0);
 	timer_setup(&host->cto_timer, dw_mci_cto_timer, 0);
 	timer_setup(&host->dto_timer, dw_mci_dto_timer, 0);
+	if (host->need_xfer_timer)
+		timer_setup(&host->xfer_timer, dw_mci_xfer_timer, 0);
 
 	spin_lock_init(&host->lock);
 	spin_lock_init(&host->irq_lock);
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index da5923a92..0b53484e9 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -230,6 +230,8 @@ struct dw_mci {
 	struct timer_list       cmd11_timer;
 	struct timer_list       cto_timer;
 	struct timer_list       dto_timer;
+	bool			need_xfer_timer;
+	struct timer_list       xfer_timer;
 };
 
 /* DMA ops for Internal/External DMAC interface */
diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c
index 59d8d96ce..e38844c2d 100644
--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
+++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
@@ -9,9 +9,13 @@
 
 #include <linux/clk.h>
 #include <linux/dma-mapping.h>
+#include <linux/iopoll.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
 #include <linux/sizes.h>
 
 #include "sdhci-pltfm.h"
@@ -21,11 +25,74 @@
 /* DWCMSHC specific Mode Select value */
 #define DWCMSHC_CTRL_HS400		0x7
 
+#define DWCMSHC_VER_ID			0x500
+#define DWCMSHC_VER_TYPE		0x504
+#define DWCMSHC_HOST_CTRL3		0x508
+#define DWCMSHC_EMMC_CONTROL		0x52c
+#define DWCMSHC_EMMC_ATCTRL		0x540
+
+/* Rockchip specific Registers */
+#define DWCMSHC_EMMC_DLL_CTRL		0x800
+#define DWCMSHC_EMMC_DLL_RXCLK		0x804
+#define DWCMSHC_EMMC_DLL_TXCLK		0x808
+#define DWCMSHC_EMMC_DLL_STRBIN		0x80c
+#define DECMSHC_EMMC_DLL_CMDOUT		0x810
+#define DWCMSHC_EMMC_DLL_STATUS0	0x840
+
+#define DWCMSHC_EMMC_DLL_START		BIT(0)
+#define DWCMSHC_EMMC_DLL_LOCKED		BIT(8)
+#define DWCMSHC_EMMC_DLL_TIMEOUT	BIT(9)
+#define DWCMSHC_EMMC_DLL_START_POINT	16
+#define DWCMSHC_EMMC_DLL_INC		8
+#define DWCMSHC_EMMC_DLL_DLYENA		BIT(27)
+
+#define DLL_TXCLK_TAPNUM_DEFAULT	0x10
+#define DLL_TXCLK_TAPNUM_90_DEGREES	0xA
+#define DLL_TXCLK_TAPNUM_FROM_SW	BIT(24)
+#define DLL_TXCLK_NO_INVERTER		BIT(29)
+
+#define DLL_STRBIN_TAPNUM_DEFAULT	0x8
+#define DLL_STRBIN_TAPNUM_FROM_SW	BIT(24)
+#define DLL_STRBIN_DELAY_NUM_SEL	BIT(26)
+#define DLL_STRBIN_DELAY_NUM_OFFSET	16
+#define DLL_STRBIN_DELAY_NUM_DEFAULT	0x16
+
+#define DLL_RXCLK_NO_INVERTER		BIT(29)
+
+#define DWCMSHC_CARD_IS_EMMC		BIT(0)
+#define DWCMSHC_ENHANCED_STROBE		BIT(8)
+
+#define DLL_CMDOUT_TAPNUM_90_DEGREES	0x8
+#define DLL_CMDOUT_TAPNUM_FROM_SW	BIT(24)
+#define DLL_CMDOUT_SRC_CLK_NEG		BIT(28)
+#define DLL_CMDOUT_EN_SRC_CLK_NEG	BIT(29)
+
+#define DLL_LOCK_WO_TMOUT(x) \
+	((((x) & DWCMSHC_EMMC_DLL_LOCKED) == DWCMSHC_EMMC_DLL_LOCKED) && \
+	(((x) & DWCMSHC_EMMC_DLL_TIMEOUT) == 0))
+#define ROCKCHIP_MAX_CLKS		3
+
 #define BOUNDARY_OK(addr, len) \
 	((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1)))
 
 struct dwcmshc_priv {
 	struct clk	*bus_clk;
+	u32 cclk_rate;
+
+	/* Rockchip specified optional clocks */
+	struct clk_bulk_data rockchip_clks[ROCKCHIP_MAX_CLKS];
+	struct reset_control *reset;
+	int txclk_tapnum;
+	unsigned int actual_clk;
+	u32 flags;
+};
+
+struct dwcmshc_driver_data {
+	const struct sdhci_pltfm_data *pdata;
+	u32 flags;
+#define RK_PLATFROM		BIT(0)
+#define RK_DLL_CMD_OUT		BIT(1)
+#define RK_RXCLK_NO_INVERTER	BIT(2)
 };
 
 /*
@@ -77,7 +144,7 @@ static void dwcmshc_request(struct mmc_host *mmc, struct mmc_request *mrq)
 static void dwcmshc_set_uhs_signaling(struct sdhci_host *host,
 				      unsigned int timing)
 {
-	u16 ctrl_2;
+	u16 ctrl_2, ctrl;
 
 	ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
 	/* Select Bus Speed Mode for host */
@@ -95,11 +162,157 @@ static void dwcmshc_set_uhs_signaling(struct sdhci_host *host,
 	else if ((timing == MMC_TIMING_UHS_DDR50) ||
 		 (timing == MMC_TIMING_MMC_DDR52))
 		ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
-	else if (timing == MMC_TIMING_MMC_HS400)
+	else if (timing == MMC_TIMING_MMC_HS400) {
+		/* set CARD_IS_EMMC bit to enable Data Strobe for HS400 */
+		ctrl = sdhci_readw(host, DWCMSHC_EMMC_CONTROL);
+		ctrl |= DWCMSHC_CARD_IS_EMMC;
+		sdhci_writew(host, ctrl, DWCMSHC_EMMC_CONTROL);
+
 		ctrl_2 |= DWCMSHC_CTRL_HS400;
+	}
+
 	sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
 }
 
+static void dwcmshc_hs400_enhanced_strobe(struct mmc_host *mmc,
+					  struct mmc_ios *ios)
+{
+	u32 vendor;
+	struct sdhci_host *host = mmc_priv(mmc);
+
+	vendor = sdhci_readl(host, DWCMSHC_EMMC_CONTROL);
+	if (ios->enhanced_strobe)
+		vendor |= DWCMSHC_ENHANCED_STROBE;
+	else
+		vendor &= ~DWCMSHC_ENHANCED_STROBE;
+
+	sdhci_writel(host, vendor, DWCMSHC_EMMC_CONTROL);
+}
+
+static void dwcmshc_rk_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	u32 txclk_tapnum, extra;
+	int err;
+
+	host->mmc->actual_clock = 0;
+
+	if (clock == 0) {
+		/* Disable interface clock at initial state. */
+		sdhci_set_clock(host, clock);
+		return;
+	}
+
+	/* Rockchip platform only support 375KHz for identify mode */
+	if (clock <= 400000)
+		clock = 375000;
+
+	err = clk_set_rate(pltfm_host->clk, clock);
+	if (err)
+		dev_err(mmc_dev(host->mmc), "fail to set clock %d", clock);
+
+	sdhci_set_clock(host, clock);
+
+	/* Disable cmd conflict check */
+	extra = sdhci_readl(host, DWCMSHC_HOST_CTRL3);
+	extra &= ~BIT(0);
+	sdhci_writel(host, extra, DWCMSHC_HOST_CTRL3);
+
+	if (clock <= 52000000) {
+		/* Disable DLL and reset both of sample and drive clock */
+		sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL);
+		sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_RXCLK);
+		sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK);
+		sdhci_writel(host, 0, DECMSHC_EMMC_DLL_CMDOUT);
+		/*
+		 * Before switching to hs400es mode, the driver will enable
+		 * enhanced strobe first. PHY needs to configure the parameters
+		 * of enhanced strobe first.
+		 */
+		extra = DWCMSHC_EMMC_DLL_DLYENA |
+			DLL_STRBIN_DELAY_NUM_SEL |
+			DLL_STRBIN_DELAY_NUM_DEFAULT << DLL_STRBIN_DELAY_NUM_OFFSET;
+		sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN);
+		return;
+	}
+
+	/* Reset DLL */
+	sdhci_writel(host, BIT(1), DWCMSHC_EMMC_DLL_CTRL);
+	udelay(1);
+	sdhci_writel(host, 0x0, DWCMSHC_EMMC_DLL_CTRL);
+
+	/*
+	 * We shouldn't set DLL_RXCLK_NO_INVERTER for identify mode but
+	 * we must set it in higher speed mode.
+	 */
+	extra = DWCMSHC_EMMC_DLL_DLYENA;
+	if (priv->flags & RK_RXCLK_NO_INVERTER)
+		extra |= DLL_RXCLK_NO_INVERTER;
+	sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK);
+
+	/* Init DLL settings */
+	extra = 0x5 << DWCMSHC_EMMC_DLL_START_POINT |
+		0x2 << DWCMSHC_EMMC_DLL_INC |
+		DWCMSHC_EMMC_DLL_START;
+	sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_CTRL);
+	err = readl_poll_timeout(host->ioaddr + DWCMSHC_EMMC_DLL_STATUS0,
+				 extra, DLL_LOCK_WO_TMOUT(extra), 1,
+				 500 * USEC_PER_MSEC);
+	if (err) {
+		dev_err(mmc_dev(host->mmc), "DLL lock timeout!\n");
+		return;
+	}
+
+	extra = 0x1 << 16 | /* tune clock stop en */
+		0x2 << 17 | /* pre-change delay */
+		0x3 << 19;  /* post-change delay */
+	sdhci_writel(host, extra, DWCMSHC_EMMC_ATCTRL);
+
+	txclk_tapnum = priv->txclk_tapnum;
+
+	if ((priv->flags & RK_DLL_CMD_OUT) &&
+	    host->mmc->ios.timing == MMC_TIMING_MMC_HS400) {
+		txclk_tapnum = DLL_TXCLK_TAPNUM_90_DEGREES;
+
+		extra = DLL_CMDOUT_SRC_CLK_NEG |
+			DLL_CMDOUT_EN_SRC_CLK_NEG |
+			DWCMSHC_EMMC_DLL_DLYENA |
+			DLL_CMDOUT_TAPNUM_90_DEGREES |
+			DLL_CMDOUT_TAPNUM_FROM_SW;
+		sdhci_writel(host, extra, DECMSHC_EMMC_DLL_CMDOUT);
+	}
+
+	extra = DWCMSHC_EMMC_DLL_DLYENA |
+		DLL_TXCLK_TAPNUM_FROM_SW |
+		DLL_RXCLK_NO_INVERTER |
+		txclk_tapnum;
+	sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_TXCLK);
+
+	extra = DWCMSHC_EMMC_DLL_DLYENA |
+		DLL_STRBIN_TAPNUM_DEFAULT |
+		DLL_STRBIN_TAPNUM_FROM_SW;
+	sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN);
+}
+
+static void rockchip_sdhci_reset(struct sdhci_host *host, u8 mask)
+{
+	struct sdhci_pltfm_host *pltfm_host;
+	struct dwcmshc_priv *priv;
+
+	if (mask & SDHCI_RESET_ALL) {
+		pltfm_host = sdhci_priv(host);
+		priv = sdhci_pltfm_priv(pltfm_host);
+		if (!IS_ERR_OR_NULL(priv->reset)) {
+			reset_control_assert(priv->reset);
+			udelay(1);
+			reset_control_deassert(priv->reset);
+		}
+	}
+
+	sdhci_reset(host, mask);
+}
+
 static const struct sdhci_ops sdhci_dwcmshc_ops = {
 	.set_clock		= sdhci_set_clock,
 	.set_bus_width		= sdhci_set_bus_width,
@@ -109,21 +322,118 @@ static const struct sdhci_ops sdhci_dwcmshc_ops = {
 	.adma_write_desc	= dwcmshc_adma_write_desc,
 };
 
+static const struct sdhci_ops sdhci_dwcmshc_rk_ops = {
+	.set_clock		= dwcmshc_rk_set_clock,
+	.set_bus_width		= sdhci_set_bus_width,
+	.set_uhs_signaling	= dwcmshc_set_uhs_signaling,
+	.get_max_clock		= sdhci_pltfm_clk_get_max_clock,
+	.reset			= rockchip_sdhci_reset,
+	.adma_write_desc	= dwcmshc_adma_write_desc,
+};
+
 static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = {
 	.ops = &sdhci_dwcmshc_ops,
 	.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
 	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
 };
 
+static const struct sdhci_pltfm_data sdhci_dwcmshc_rk_pdata = {
+	.ops = &sdhci_dwcmshc_rk_ops,
+	.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+		  SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
+	.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+		   SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
+};
+
+static const struct dwcmshc_driver_data dwcmshc_drvdata = {
+	.pdata = &sdhci_dwcmshc_pdata,
+	.flags = 0,
+};
+
+static const struct dwcmshc_driver_data rk3568_drvdata = {
+	.pdata = &sdhci_dwcmshc_rk_pdata,
+	.flags = RK_PLATFROM | RK_RXCLK_NO_INVERTER,
+};
+
+static const struct dwcmshc_driver_data rk3588_drvdata = {
+	.pdata = &sdhci_dwcmshc_rk_pdata,
+	.flags = RK_PLATFROM | RK_DLL_CMD_OUT,
+};
+
+static int rockchip_pltf_init(struct sdhci_host *host, struct dwcmshc_priv *priv)
+{
+	int err;
+
+	priv->rockchip_clks[0].id = "axi";
+	priv->rockchip_clks[1].id = "block";
+	priv->rockchip_clks[2].id = "timer";
+	err = devm_clk_bulk_get_optional(mmc_dev(host->mmc), ROCKCHIP_MAX_CLKS,
+					 priv->rockchip_clks);
+	if (err) {
+		dev_err(mmc_dev(host->mmc), "failed to get clocks %d\n", err);
+		return err;
+	}
+
+	err = clk_bulk_prepare_enable(ROCKCHIP_MAX_CLKS, priv->rockchip_clks);
+	if (err) {
+		dev_err(mmc_dev(host->mmc), "failed to enable clocks %d\n", err);
+		return err;
+	}
+
+	if (of_property_read_u32(mmc_dev(host->mmc)->of_node, "rockchip,txclk-tapnum",
+				 &priv->txclk_tapnum))
+		priv->txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT;
+
+	/* Disable cmd conflict check */
+	sdhci_writel(host, 0x0, DWCMSHC_HOST_CTRL3);
+	/* Reset previous settings */
+	sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK);
+	sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_STRBIN);
+
+	/*
+	 * Don't support highspeed bus mode with low clk speed as we
+	 * cannot use DLL for this condition.
+	 */
+	if (host->mmc->f_max <= 52000000) {
+		host->mmc->caps2 &= ~(MMC_CAP2_HS200 | MMC_CAP2_HS400);
+		host->mmc->caps &= ~(MMC_CAP_3_3V_DDR | MMC_CAP_1_8V_DDR);
+	}
+
+	return 0;
+}
+
+static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
+	{
+		.compatible = "snps,dwcmshc-sdhci",
+		.data = &dwcmshc_drvdata,
+	},
+	{
+		.compatible = "rockchip,dwcmshc-sdhci",
+		.data = &rk3568_drvdata,
+	},
+	{
+		.compatible = "rockchip,rk3588-dwcmshc",
+		.data = &rk3588_drvdata,
+	},
+	{},
+};
+
 static int dwcmshc_probe(struct platform_device *pdev)
 {
 	struct sdhci_pltfm_host *pltfm_host;
 	struct sdhci_host *host;
 	struct dwcmshc_priv *priv;
+	const struct dwcmshc_driver_data *drv_data;
 	int err;
 	u32 extra;
 
-	host = sdhci_pltfm_init(pdev, &sdhci_dwcmshc_pdata,
+	drv_data = of_device_get_match_data(&pdev->dev);
+	if (!drv_data) {
+		dev_err(&pdev->dev, "Error: No device match data found\n");
+		return -ENODEV;
+	}
+
+	host = sdhci_pltfm_init(pdev, drv_data->pdata,
 				sizeof(struct dwcmshc_priv));
 	if (IS_ERR(host))
 		return PTR_ERR(host);
@@ -139,6 +449,8 @@ static int dwcmshc_probe(struct platform_device *pdev)
 	pltfm_host = sdhci_priv(host);
 	priv = sdhci_pltfm_priv(pltfm_host);
 
+	priv->reset = devm_reset_control_array_get_exclusive(&pdev->dev);
+
 	pltfm_host->clk = devm_clk_get(&pdev->dev, "core");
 	if (IS_ERR(pltfm_host->clk)) {
 		err = PTR_ERR(pltfm_host->clk);
@@ -160,16 +472,33 @@ static int dwcmshc_probe(struct platform_device *pdev)
 	sdhci_get_of_property(pdev);
 
 	host->mmc_host_ops.request = dwcmshc_request;
+	host->mmc_host_ops.hs400_enhanced_strobe =
+		dwcmshc_hs400_enhanced_strobe;
 
 	err = sdhci_add_host(host);
 	if (err)
 		goto err_clk;
 
+	priv->flags = drv_data->flags;
+	if (drv_data->flags & RK_PLATFROM) {
+		err = rockchip_pltf_init(host, priv);
+		if (err)
+			goto err_clk;
+	}
+
+	pm_runtime_get_noresume(&pdev->dev);
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
+	pm_runtime_use_autosuspend(&pdev->dev);
+	pm_runtime_put_autosuspend(&pdev->dev);
+
 	return 0;
 
 err_clk:
 	clk_disable_unprepare(pltfm_host->clk);
 	clk_disable_unprepare(priv->bus_clk);
+	clk_bulk_disable_unprepare(ROCKCHIP_MAX_CLKS, priv->rockchip_clks);
 free_pltfm:
 	sdhci_pltfm_free(pdev);
 	return err;
@@ -185,6 +514,7 @@ static int dwcmshc_remove(struct platform_device *pdev)
 
 	clk_disable_unprepare(pltfm_host->clk);
 	clk_disable_unprepare(priv->bus_clk);
+	clk_bulk_disable_unprepare(ROCKCHIP_MAX_CLKS, priv->rockchip_clks);
 
 	sdhci_pltfm_free(pdev);
 
@@ -207,6 +537,7 @@ static int dwcmshc_suspend(struct device *dev)
 	if (!IS_ERR(priv->bus_clk))
 		clk_disable_unprepare(priv->bus_clk);
 
+	clk_bulk_disable_unprepare(ROCKCHIP_MAX_CLKS, priv->rockchip_clks);
 	return ret;
 }
 
@@ -227,15 +558,51 @@ static int dwcmshc_resume(struct device *dev)
 			return ret;
 	}
 
+	ret = clk_bulk_prepare_enable(ROCKCHIP_MAX_CLKS, priv->rockchip_clks);
+	if (ret)
+		return ret;
+
 	return sdhci_resume_host(host);
 }
-#endif
 
-static SIMPLE_DEV_PM_OPS(dwcmshc_pmops, dwcmshc_suspend, dwcmshc_resume);
+static int dwcmshc_runtime_suspend(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
 
-static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
-	{ .compatible = "snps,dwcmshc-sdhci" },
-	{}
+	priv->actual_clk = host->mmc->actual_clock;
+	sdhci_set_clock(host, 0);
+	priv->cclk_rate = clk_get_rate(pltfm_host->clk);
+	clk_set_rate(pltfm_host->clk, 24000000);
+	clk_bulk_disable_unprepare(ROCKCHIP_MAX_CLKS, priv->rockchip_clks);
+
+	return 0;
+}
+
+static int dwcmshc_runtime_resume(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
+	int ret = 0;
+
+	clk_set_rate(pltfm_host->clk, priv->cclk_rate);
+	sdhci_set_clock(host, priv->actual_clk);
+	ret = clk_bulk_prepare_enable(ROCKCHIP_MAX_CLKS, priv->rockchip_clks);
+	/*
+	 * DLL will not LOCK after frequency reduction,
+	 * and it needs to be reconfigured.
+	 */
+	dwcmshc_rk_set_clock(host, priv->cclk_rate);
+
+	return ret;
+}
+#endif
+
+static const struct dev_pm_ops dwcmshc_pmops = {
+	SET_SYSTEM_SLEEP_PM_OPS(dwcmshc_suspend, dwcmshc_resume)
+	SET_RUNTIME_PM_OPS(dwcmshc_runtime_suspend, dwcmshc_runtime_resume, NULL)
 };
 MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids);
 
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c
index b3365b34c..fad503820 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c
@@ -71,7 +71,6 @@ static int dwmac_generic_probe(struct platform_device *pdev)
 
 static const struct of_device_id dwmac_generic_match[] = {
 	{ .compatible = "st,spear600-gmac"},
-	{ .compatible = "snps,dwmac-3.40a"},
 	{ .compatible = "snps,dwmac-3.50a"},
 	{ .compatible = "snps,dwmac-3.610"},
 	{ .compatible = "snps,dwmac-3.70a"},
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
index e7fbc9b30..4a6f95200 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
@@ -11,6 +11,7 @@
 #include <linux/bitops.h>
 #include <linux/clk.h>
 #include <linux/phy.h>
+#include <linux/phy/phy.h>
 #include <linux/of_net.h>
 #include <linux/gpio.h>
 #include <linux/module.h>
@@ -22,22 +23,30 @@
 #include <linux/mfd/syscon.h>
 #include <linux/regmap.h>
 #include <linux/pm_runtime.h>
-
+#include <linux/soc/rockchip/rk_vendor_storage.h>
 #include "stmmac_platform.h"
+#include <linux/dwmac-rk-tool.h>
+
+#define MAX_ETH		2
 
 struct rk_priv_data;
 struct rk_gmac_ops {
 	void (*set_to_rgmii)(struct rk_priv_data *bsp_priv,
 			     int tx_delay, int rx_delay);
 	void (*set_to_rmii)(struct rk_priv_data *bsp_priv);
+	void (*set_to_sgmii)(struct rk_priv_data *bsp_priv);
+	void (*set_to_qsgmii)(struct rk_priv_data *bsp_priv);
 	void (*set_rgmii_speed)(struct rk_priv_data *bsp_priv, int speed);
 	void (*set_rmii_speed)(struct rk_priv_data *bsp_priv, int speed);
+	void (*set_clock_selection)(struct rk_priv_data *bsp_priv, bool input,
+				    bool enable);
 	void (*integrated_phy_powerup)(struct rk_priv_data *bsp_priv);
 };
 
 struct rk_priv_data {
 	struct platform_device *pdev;
 	phy_interface_t phy_iface;
+	int bus_id;
 	struct regulator *regulator;
 	bool suspended;
 	const struct rk_gmac_ops *ops;
@@ -56,6 +65,7 @@ struct rk_priv_data {
 	struct clk *aclk_mac;
 	struct clk *pclk_mac;
 	struct clk *clk_phy;
+	struct clk *pclk_xpcs;
 
 	struct reset_control *phy_reset;
 
@@ -63,8 +73,132 @@ struct rk_priv_data {
 	int rx_delay;
 
 	struct regmap *grf;
+	struct regmap *php_grf;
+	struct regmap *xpcs;
 };
 
+/* XPCS */
+#define XPCS_APB_INCREMENT		(0x4)
+#define XPCS_APB_MASK			GENMASK_ULL(20, 0)
+
+#define SR_MII_BASE			(0x1F0000)
+#define SR_MII1_BASE			(0x1A0000)
+
+#define VR_MII_DIG_CTRL1		(0x8000)
+#define VR_MII_AN_CTRL			(0x8001)
+#define VR_MII_AN_INTR_STS		(0x8002)
+#define VR_MII_LINK_TIMER_CTRL		(0x800A)
+
+#define SR_MII_CTRL_AN_ENABLE		\
+	(BMCR_ANENABLE | BMCR_ANRESTART | BMCR_FULLDPLX | BMCR_SPEED1000)
+#define MII_MAC_AUTO_SW			(0x0200)
+#define PCS_MODE_OFFSET			(0x1)
+#define MII_AN_INTR_EN			(0x1)
+#define PCS_SGMII_MODE			(0x2 << PCS_MODE_OFFSET)
+#define PCS_QSGMII_MODE			(0X3 << PCS_MODE_OFFSET)
+#define VR_MII_CTRL_SGMII_AN_EN		(PCS_SGMII_MODE | MII_AN_INTR_EN)
+#define VR_MII_CTRL_QSGMII_AN_EN	(PCS_QSGMII_MODE | MII_AN_INTR_EN)
+
+#define SR_MII_OFFSET(_x) ({		\
+	typeof(_x) (x) = (_x); \
+	(((x) == 0) ? SR_MII_BASE : (SR_MII1_BASE + ((x) - 1) * 0x10000)); \
+}) \
+
+static int xpcs_read(void *priv, int reg)
+{
+	struct rk_priv_data *bsp_priv = (struct rk_priv_data *)priv;
+	int ret, val;
+
+	ret = regmap_read(bsp_priv->xpcs,
+			  (u32)(reg * XPCS_APB_INCREMENT) & XPCS_APB_MASK,
+			  &val);
+	if (ret)
+		return ret;
+
+	return val;
+}
+
+static int xpcs_write(void *priv, int reg, u16 value)
+{
+	struct rk_priv_data *bsp_priv = (struct rk_priv_data *)priv;
+
+	return regmap_write(bsp_priv->xpcs,
+			    (reg * XPCS_APB_INCREMENT) & XPCS_APB_MASK, value);
+}
+
+static int xpcs_poll_reset(struct rk_priv_data *bsp_priv, int dev)
+{
+	/* Poll until the reset bit clears (50ms per retry == 0.6 sec) */
+	unsigned int retries = 12;
+	int ret;
+
+	do {
+		msleep(50);
+		ret = xpcs_read(bsp_priv, SR_MII_OFFSET(dev) + MDIO_CTRL1);
+		if (ret < 0)
+			return ret;
+	} while (ret & MDIO_CTRL1_RESET && --retries);
+
+	return (ret & MDIO_CTRL1_RESET) ? -ETIMEDOUT : 0;
+}
+
+static int xpcs_soft_reset(struct rk_priv_data *bsp_priv, int dev)
+{
+	int ret;
+
+	ret = xpcs_write(bsp_priv, SR_MII_OFFSET(dev) + MDIO_CTRL1,
+			 MDIO_CTRL1_RESET);
+	if (ret < 0)
+		return ret;
+
+	return xpcs_poll_reset(bsp_priv, dev);
+}
+
+static int xpcs_setup(struct rk_priv_data *bsp_priv, int mode)
+{
+	int ret, i, id = bsp_priv->bus_id;
+	u32 val;
+
+	if (mode == PHY_INTERFACE_MODE_QSGMII && id > 0)
+		return 0;
+
+	ret = xpcs_soft_reset(bsp_priv, id);
+	if (ret) {
+		dev_err(&bsp_priv->pdev->dev, "xpcs_soft_reset fail %d\n", ret);
+		return ret;
+	}
+
+	xpcs_write(bsp_priv, SR_MII_OFFSET(0) + VR_MII_AN_INTR_STS, 0x0);
+	xpcs_write(bsp_priv, SR_MII_OFFSET(0) + VR_MII_LINK_TIMER_CTRL, 0x1);
+
+	if (mode == PHY_INTERFACE_MODE_SGMII)
+		xpcs_write(bsp_priv, SR_MII_OFFSET(0) + VR_MII_AN_CTRL,
+			   VR_MII_CTRL_SGMII_AN_EN);
+	else
+		xpcs_write(bsp_priv, SR_MII_OFFSET(0) + VR_MII_AN_CTRL,
+			   VR_MII_CTRL_QSGMII_AN_EN);
+
+	if (mode == PHY_INTERFACE_MODE_QSGMII) {
+		for (i = 0; i < 4; i++) {
+			val = xpcs_read(bsp_priv,
+					SR_MII_OFFSET(i) + VR_MII_DIG_CTRL1);
+			xpcs_write(bsp_priv,
+				   SR_MII_OFFSET(i) + VR_MII_DIG_CTRL1,
+				   val | MII_MAC_AUTO_SW);
+			xpcs_write(bsp_priv, SR_MII_OFFSET(i) + MII_BMCR,
+				   SR_MII_CTRL_AN_ENABLE);
+		}
+	} else {
+		val = xpcs_read(bsp_priv, SR_MII_OFFSET(id) + VR_MII_DIG_CTRL1);
+		xpcs_write(bsp_priv, SR_MII_OFFSET(id) + VR_MII_DIG_CTRL1,
+			   val | MII_MAC_AUTO_SW);
+		xpcs_write(bsp_priv, SR_MII_OFFSET(id) + MII_BMCR,
+			   SR_MII_CTRL_AN_ENABLE);
+	}
+
+	return ret;
+}
+
 #define HIWORD_UPDATE(val, mask, shift) \
 		((val) << (shift) | (mask) << ((shift) + 16))
 
@@ -72,8 +206,16 @@ struct rk_priv_data {
 #define GRF_CLR_BIT(nr)	(BIT(nr+16))
 
 #define DELAY_ENABLE(soc, tx, rx) \
-	(((tx) ? soc##_GMAC_TXCLK_DLY_ENABLE : soc##_GMAC_TXCLK_DLY_DISABLE) | \
-	 ((rx) ? soc##_GMAC_RXCLK_DLY_ENABLE : soc##_GMAC_RXCLK_DLY_DISABLE))
+	((((tx) >= 0) ? soc##_GMAC_TXCLK_DLY_ENABLE : soc##_GMAC_TXCLK_DLY_DISABLE) | \
+	 (((rx) >= 0) ? soc##_GMAC_RXCLK_DLY_ENABLE : soc##_GMAC_RXCLK_DLY_DISABLE))
+
+#define DELAY_ENABLE_BY_ID(soc, tx, rx, id) \
+	((((tx) >= 0) ? soc##_GMAC_TXCLK_DLY_ENABLE(id) : soc##_GMAC_TXCLK_DLY_DISABLE(id)) | \
+	 (((rx) >= 0) ? soc##_GMAC_RXCLK_DLY_ENABLE(id) : soc##_GMAC_RXCLK_DLY_DISABLE(id)))
+
+#define DELAY_VALUE(soc, tx, rx) \
+	((((tx) >= 0) ? soc##_GMAC_CLK_TX_DL_CFG(tx) : 0) | \
+	 (((rx) >= 0) ? soc##_GMAC_CLK_RX_DL_CFG(rx) : 0))
 
 #define PX30_GRF_GMAC_CON1		0x0904
 
@@ -133,6 +275,127 @@ static const struct rk_gmac_ops px30_ops = {
 	.set_rmii_speed = px30_set_rmii_speed,
 };
 
+#define RK1808_GRF_GMAC_CON0		0X0900
+#define RK1808_GRF_GMAC_CON1		0X0904
+
+/* RK1808_GRF_GMAC_CON0 */
+#define RK1808_GMAC_CLK_RX_DL_CFG(val)	HIWORD_UPDATE(val, 0x7F, 8)
+#define RK1808_GMAC_CLK_TX_DL_CFG(val)	HIWORD_UPDATE(val, 0x7F, 0)
+
+/* RK1808_GRF_GMAC_CON1 */
+#define RK1808_GMAC_PHY_INTF_SEL_RGMII	\
+		(GRF_BIT(4) | GRF_CLR_BIT(5) | GRF_CLR_BIT(6))
+#define RK1808_GMAC_PHY_INTF_SEL_RMII	\
+		(GRF_CLR_BIT(4) | GRF_CLR_BIT(5) | GRF_BIT(6))
+#define RK1808_GMAC_FLOW_CTRL		GRF_BIT(3)
+#define RK1808_GMAC_FLOW_CTRL_CLR	GRF_CLR_BIT(3)
+#define RK1808_GMAC_SPEED_10M		GRF_CLR_BIT(2)
+#define RK1808_GMAC_SPEED_100M		GRF_BIT(2)
+#define RK1808_GMAC_RXCLK_DLY_ENABLE	GRF_BIT(1)
+#define RK1808_GMAC_RXCLK_DLY_DISABLE	GRF_CLR_BIT(1)
+#define RK1808_GMAC_TXCLK_DLY_ENABLE	GRF_BIT(0)
+#define RK1808_GMAC_TXCLK_DLY_DISABLE	GRF_CLR_BIT(0)
+
+static void rk1808_set_to_rgmii(struct rk_priv_data *bsp_priv,
+				int tx_delay, int rx_delay)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+
+	if (IS_ERR(bsp_priv->grf)) {
+		dev_err(dev, "Missing rockchip,grf property\n");
+		return;
+	}
+
+	regmap_write(bsp_priv->grf, RK1808_GRF_GMAC_CON1,
+		     RK1808_GMAC_PHY_INTF_SEL_RGMII |
+		     DELAY_ENABLE(RK1808, tx_delay, rx_delay));
+
+	regmap_write(bsp_priv->grf, RK1808_GRF_GMAC_CON0,
+		     DELAY_VALUE(RK1808, tx_delay, rx_delay));
+}
+
+static void rk1808_set_to_rmii(struct rk_priv_data *bsp_priv)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+
+	if (IS_ERR(bsp_priv->grf)) {
+		dev_err(dev, "%s: Missing rockchip,grf property\n", __func__);
+		return;
+	}
+
+	regmap_write(bsp_priv->grf, RK1808_GRF_GMAC_CON1,
+		     RK1808_GMAC_PHY_INTF_SEL_RMII);
+}
+
+static void rk1808_set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+	int ret;
+
+	if (IS_ERR(bsp_priv->grf)) {
+		dev_err(dev, "Missing rockchip,grf property\n");
+		return;
+	}
+
+	if (speed == 10) {
+		ret = clk_set_rate(bsp_priv->clk_mac_speed, 2500000);
+		if (ret)
+			dev_err(dev, "%s: set clk_mac_speed rate 2500000 failed: %d\n",
+				__func__, ret);
+	} else if (speed == 100) {
+		ret = clk_set_rate(bsp_priv->clk_mac_speed, 25000000);
+		if (ret)
+			dev_err(dev, "%s: set clk_mac_speed rate 25000000 failed: %d\n",
+				__func__, ret);
+	} else if (speed == 1000) {
+		ret = clk_set_rate(bsp_priv->clk_mac_speed, 125000000);
+		if (ret)
+			dev_err(dev, "%s: set clk_mac_speed rate 125000000 failed: %d\n",
+				__func__, ret);
+	} else {
+		dev_err(dev, "unknown speed value for RGMII! speed=%d", speed);
+	}
+}
+
+static void rk1808_set_rmii_speed(struct rk_priv_data *bsp_priv, int speed)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+	int ret;
+
+	if (IS_ERR(bsp_priv->clk_mac_speed)) {
+		dev_err(dev, "%s: Missing clk_mac_speed clock\n", __func__);
+		return;
+	}
+
+	if (speed == 10) {
+		regmap_write(bsp_priv->grf, RK1808_GRF_GMAC_CON1,
+			     RK1808_GMAC_SPEED_10M);
+
+		ret = clk_set_rate(bsp_priv->clk_mac_speed, 2500000);
+		if (ret)
+			dev_err(dev, "%s: set clk_mac_speed rate 2500000 failed: %d\n",
+				__func__, ret);
+	} else if (speed == 100) {
+		regmap_write(bsp_priv->grf, RK1808_GRF_GMAC_CON1,
+			     RK1808_GMAC_SPEED_100M);
+
+		ret = clk_set_rate(bsp_priv->clk_mac_speed, 25000000);
+		if (ret)
+			dev_err(dev, "%s: set clk_mac_speed rate 25000000 failed: %d\n",
+				__func__, ret);
+
+	} else {
+		dev_err(dev, "unknown speed value for RMII! speed=%d", speed);
+	}
+}
+
+static const struct rk_gmac_ops rk1808_ops = {
+	.set_to_rgmii = rk1808_set_to_rgmii,
+	.set_to_rmii = rk1808_set_to_rmii,
+	.set_rgmii_speed = rk1808_set_rgmii_speed,
+	.set_rmii_speed = rk1808_set_rmii_speed,
+};
+
 #define RK3128_GRF_MAC_CON0	0x0168
 #define RK3128_GRF_MAC_CON1	0x016c
 
@@ -176,8 +439,7 @@ static void rk3128_set_to_rgmii(struct rk_priv_data *bsp_priv,
 		     RK3128_GMAC_RMII_MODE_CLR);
 	regmap_write(bsp_priv->grf, RK3128_GRF_MAC_CON0,
 		     DELAY_ENABLE(RK3128, tx_delay, rx_delay) |
-		     RK3128_GMAC_CLK_RX_DL_CFG(rx_delay) |
-		     RK3128_GMAC_CLK_TX_DL_CFG(tx_delay));
+		     DELAY_VALUE(RK3128, tx_delay, rx_delay));
 }
 
 static void rk3128_set_to_rmii(struct rk_priv_data *bsp_priv)
@@ -293,8 +555,7 @@ static void rk3228_set_to_rgmii(struct rk_priv_data *bsp_priv,
 		     DELAY_ENABLE(RK3228, tx_delay, rx_delay));
 
 	regmap_write(bsp_priv->grf, RK3228_GRF_MAC_CON0,
-		     RK3228_GMAC_CLK_RX_DL_CFG(rx_delay) |
-		     RK3228_GMAC_CLK_TX_DL_CFG(tx_delay));
+		     DELAY_VALUE(RK3128, tx_delay, rx_delay));
 }
 
 static void rk3228_set_to_rmii(struct rk_priv_data *bsp_priv)
@@ -414,8 +675,7 @@ static void rk3288_set_to_rgmii(struct rk_priv_data *bsp_priv,
 		     RK3288_GMAC_RMII_MODE_CLR);
 	regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3,
 		     DELAY_ENABLE(RK3288, tx_delay, rx_delay) |
-		     RK3288_GMAC_CLK_RX_DL_CFG(rx_delay) |
-		     RK3288_GMAC_CLK_TX_DL_CFG(tx_delay));
+		     DELAY_VALUE(RK3288, tx_delay, rx_delay));
 }
 
 static void rk3288_set_to_rmii(struct rk_priv_data *bsp_priv)
@@ -482,6 +742,64 @@ static const struct rk_gmac_ops rk3288_ops = {
 	.set_rmii_speed = rk3288_set_rmii_speed,
 };
 
+#define RK3308_GRF_MAC_CON0		0x04a0
+
+/* Rk3308_GRF_MAC_CON1 */
+#define RK3308_MAC_PHY_INTF_SEL_RMII	(GRF_CLR_BIT(2) | GRF_CLR_BIT(3) | \
+					GRF_BIT(4))
+#define RK3308_MAC_SPEED_10M		GRF_CLR_BIT(0)
+#define Rk3308_MAC_SPEED_100M		GRF_BIT(0)
+
+static void rk3308_set_to_rmii(struct rk_priv_data *bsp_priv)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+
+	if (IS_ERR(bsp_priv->grf)) {
+		dev_err(dev, "%s: Missing rockchip,grf property\n", __func__);
+		return;
+	}
+
+	regmap_write(bsp_priv->grf, RK3308_GRF_MAC_CON0,
+		     RK3308_MAC_PHY_INTF_SEL_RMII);
+}
+
+static void rk3308_set_rmii_speed(struct rk_priv_data *bsp_priv, int speed)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+	int ret;
+
+	if (IS_ERR(bsp_priv->clk_mac_speed)) {
+		dev_err(dev, "%s: Missing clk_mac_speed clock\n", __func__);
+		return;
+	}
+
+	if (speed == 10) {
+		regmap_write(bsp_priv->grf, RK3308_GRF_MAC_CON0,
+			     RK3308_MAC_SPEED_10M);
+
+		ret = clk_set_rate(bsp_priv->clk_mac_speed, 2500000);
+		if (ret)
+			dev_err(dev, "%s: set clk_mac_speed rate 2500000 failed: %d\n",
+				__func__, ret);
+	} else if (speed == 100) {
+		regmap_write(bsp_priv->grf, RK3308_GRF_MAC_CON0,
+			     Rk3308_MAC_SPEED_100M);
+
+		ret = clk_set_rate(bsp_priv->clk_mac_speed, 25000000);
+		if (ret)
+			dev_err(dev, "%s: set clk_mac_speed rate 25000000 failed: %d\n",
+				__func__, ret);
+
+	} else {
+		dev_err(dev, "unknown speed value for RMII! speed=%d", speed);
+	}
+}
+
+static const struct rk_gmac_ops rk3308_ops = {
+	.set_to_rmii = rk3308_set_to_rmii,
+	.set_rmii_speed = rk3308_set_rmii_speed,
+};
+
 #define RK3328_GRF_MAC_CON0	0x0900
 #define RK3328_GRF_MAC_CON1	0x0904
 #define RK3328_GRF_MAC_CON2	0x0908
@@ -528,12 +846,10 @@ static void rk3328_set_to_rgmii(struct rk_priv_data *bsp_priv,
 	regmap_write(bsp_priv->grf, RK3328_GRF_MAC_CON1,
 		     RK3328_GMAC_PHY_INTF_SEL_RGMII |
 		     RK3328_GMAC_RMII_MODE_CLR |
-		     RK3328_GMAC_RXCLK_DLY_ENABLE |
-		     RK3328_GMAC_TXCLK_DLY_ENABLE);
+		     DELAY_ENABLE(RK3328, tx_delay, rx_delay));
 
 	regmap_write(bsp_priv->grf, RK3328_GRF_MAC_CON0,
-		     RK3328_GMAC_CLK_RX_DL_CFG(rx_delay) |
-		     RK3328_GMAC_CLK_TX_DL_CFG(tx_delay));
+		     DELAY_VALUE(RK3328, tx_delay, rx_delay));
 }
 
 static void rk3328_set_to_rmii(struct rk_priv_data *bsp_priv)
@@ -658,8 +974,7 @@ static void rk3366_set_to_rgmii(struct rk_priv_data *bsp_priv,
 		     RK3366_GMAC_RMII_MODE_CLR);
 	regmap_write(bsp_priv->grf, RK3366_GRF_SOC_CON7,
 		     DELAY_ENABLE(RK3366, tx_delay, rx_delay) |
-		     RK3366_GMAC_CLK_RX_DL_CFG(rx_delay) |
-		     RK3366_GMAC_CLK_TX_DL_CFG(tx_delay));
+		     DELAY_VALUE(RK3366, tx_delay, rx_delay));
 }
 
 static void rk3366_set_to_rmii(struct rk_priv_data *bsp_priv)
@@ -769,8 +1084,7 @@ static void rk3368_set_to_rgmii(struct rk_priv_data *bsp_priv,
 		     RK3368_GMAC_RMII_MODE_CLR);
 	regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON16,
 		     DELAY_ENABLE(RK3368, tx_delay, rx_delay) |
-		     RK3368_GMAC_CLK_RX_DL_CFG(rx_delay) |
-		     RK3368_GMAC_CLK_TX_DL_CFG(tx_delay));
+		     DELAY_VALUE(RK3368, tx_delay, rx_delay));
 }
 
 static void rk3368_set_to_rmii(struct rk_priv_data *bsp_priv)
@@ -880,8 +1194,7 @@ static void rk3399_set_to_rgmii(struct rk_priv_data *bsp_priv,
 		     RK3399_GMAC_RMII_MODE_CLR);
 	regmap_write(bsp_priv->grf, RK3399_GRF_SOC_CON6,
 		     DELAY_ENABLE(RK3399, tx_delay, rx_delay) |
-		     RK3399_GMAC_CLK_RX_DL_CFG(rx_delay) |
-		     RK3399_GMAC_CLK_TX_DL_CFG(tx_delay));
+		     DELAY_VALUE(RK3399, tx_delay, rx_delay));
 }
 
 static void rk3399_set_to_rmii(struct rk_priv_data *bsp_priv)
@@ -948,6 +1261,283 @@ static const struct rk_gmac_ops rk3399_ops = {
 	.set_rmii_speed = rk3399_set_rmii_speed,
 };
 
+#define RK3568_GRF_GMAC0_CON0		0X0380
+#define RK3568_GRF_GMAC0_CON1		0X0384
+#define RK3568_GRF_GMAC1_CON0		0X0388
+#define RK3568_GRF_GMAC1_CON1		0X038c
+
+/* RK3568_GRF_GMAC0_CON1 && RK3568_GRF_GMAC1_CON1 */
+#define RK3568_GMAC_GMII_MODE			GRF_BIT(7)
+#define RK3568_GMAC_PHY_INTF_SEL_RGMII	\
+		(GRF_BIT(4) | GRF_CLR_BIT(5) | GRF_CLR_BIT(6))
+#define RK3568_GMAC_PHY_INTF_SEL_RMII	\
+		(GRF_CLR_BIT(4) | GRF_CLR_BIT(5) | GRF_BIT(6))
+#define RK3568_GMAC_FLOW_CTRL			GRF_BIT(3)
+#define RK3568_GMAC_FLOW_CTRL_CLR		GRF_CLR_BIT(3)
+#define RK3568_GMAC_RXCLK_DLY_ENABLE		GRF_BIT(1)
+#define RK3568_GMAC_RXCLK_DLY_DISABLE		GRF_CLR_BIT(1)
+#define RK3568_GMAC_TXCLK_DLY_ENABLE		GRF_BIT(0)
+#define RK3568_GMAC_TXCLK_DLY_DISABLE		GRF_CLR_BIT(0)
+
+/* RK3568_GRF_GMAC0_CON0 && RK3568_GRF_GMAC1_CON0 */
+#define RK3568_GMAC_CLK_RX_DL_CFG(val)	HIWORD_UPDATE(val, 0x7F, 8)
+#define RK3568_GMAC_CLK_TX_DL_CFG(val)	HIWORD_UPDATE(val, 0x7F, 0)
+
+#define RK3568_PIPE_GRF_XPCS_CON0	0X0040
+
+#define RK3568_PIPE_GRF_XPCS_QGMII_MAC_SEL	GRF_BIT(0)
+#define RK3568_PIPE_GRF_XPCS_SGMII_MAC_SEL	GRF_BIT(1)
+#define RK3568_PIPE_GRF_XPCS_PHY_READY		GRF_BIT(2)
+
+static void rk3568_set_to_sgmii(struct rk_priv_data *bsp_priv)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+	u32 offset_con1;
+
+	if (IS_ERR(bsp_priv->grf)) {
+		dev_err(dev, "%s: Missing rockchip,grfs property\n", __func__);
+		return;
+	}
+
+	offset_con1 = bsp_priv->bus_id == 1 ? RK3568_GRF_GMAC1_CON1 :
+					      RK3568_GRF_GMAC0_CON1;
+	regmap_write(bsp_priv->grf, offset_con1, RK3568_GMAC_GMII_MODE);
+
+	xpcs_setup(bsp_priv, PHY_INTERFACE_MODE_SGMII);
+}
+
+static void rk3568_set_to_qsgmii(struct rk_priv_data *bsp_priv)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+	u32 offset_con1;
+
+	if (IS_ERR(bsp_priv->grf)) {
+		dev_err(dev, "%s: Missing rockchip,grfs property\n", __func__);
+		return;
+	}
+
+	offset_con1 = bsp_priv->bus_id == 1 ? RK3568_GRF_GMAC1_CON1 :
+					      RK3568_GRF_GMAC0_CON1;
+	regmap_write(bsp_priv->grf, offset_con1, RK3568_GMAC_GMII_MODE);
+
+	xpcs_setup(bsp_priv, PHY_INTERFACE_MODE_QSGMII);
+}
+
+static void rk3568_set_to_rgmii(struct rk_priv_data *bsp_priv,
+				int tx_delay, int rx_delay)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+	u32 offset_con0, offset_con1;
+
+	if (IS_ERR(bsp_priv->grf)) {
+		dev_err(dev, "Missing rockchip,grf property\n");
+		return;
+	}
+
+	offset_con0 = (bsp_priv->bus_id == 1) ? RK3568_GRF_GMAC1_CON0 :
+						RK3568_GRF_GMAC0_CON0;
+	offset_con1 = (bsp_priv->bus_id == 1) ? RK3568_GRF_GMAC1_CON1 :
+						RK3568_GRF_GMAC0_CON1;
+
+	regmap_write(bsp_priv->grf, offset_con1,
+		     RK3568_GMAC_PHY_INTF_SEL_RGMII |
+		     DELAY_ENABLE(RK3568, tx_delay, rx_delay));
+
+	regmap_write(bsp_priv->grf, offset_con0,
+		     DELAY_VALUE(RK3568, tx_delay, rx_delay));
+}
+
+static void rk3568_set_to_rmii(struct rk_priv_data *bsp_priv)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+	u32 offset_con1;
+
+	if (IS_ERR(bsp_priv->grf)) {
+		dev_err(dev, "%s: Missing rockchip,grf property\n", __func__);
+		return;
+	}
+
+	offset_con1 = (bsp_priv->bus_id == 1) ? RK3568_GRF_GMAC1_CON1 :
+						RK3568_GRF_GMAC0_CON1;
+
+	regmap_write(bsp_priv->grf, offset_con1, RK3568_GMAC_PHY_INTF_SEL_RMII);
+}
+
+static void rk3568_set_gmac_speed(struct rk_priv_data *bsp_priv, int speed)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+	unsigned long rate;
+	int ret;
+
+	switch (speed) {
+	case 10:
+		rate = 2500000;
+		break;
+	case 100:
+		rate = 25000000;
+		break;
+	case 1000:
+		rate = 125000000;
+		break;
+	default:
+		dev_err(dev, "unknown speed value for GMAC speed=%d", speed);
+		return;
+	}
+
+	ret = clk_set_rate(bsp_priv->clk_mac_speed, rate);
+	if (ret)
+		dev_err(dev, "%s: set clk_mac_speed rate %ld failed %d\n",
+			__func__, rate, ret);
+}
+
+static const struct rk_gmac_ops rk3568_ops = {
+	.set_to_rgmii = rk3568_set_to_rgmii,
+	.set_to_rmii = rk3568_set_to_rmii,
+	.set_to_sgmii = rk3568_set_to_sgmii,
+	.set_to_qsgmii = rk3568_set_to_qsgmii,
+	.set_rgmii_speed = rk3568_set_gmac_speed,
+	.set_rmii_speed = rk3568_set_gmac_speed,
+};
+
+/* sys_grf */
+#define RK3588_GRF_GMAC_CON7			0X031c
+#define RK3588_GRF_GMAC_CON8			0X0320
+#define RK3588_GRF_GMAC_CON9			0X0324
+
+#define RK3588_GMAC_RXCLK_DLY_ENABLE(id)	GRF_BIT(2 * (id) + 3)
+#define RK3588_GMAC_RXCLK_DLY_DISABLE(id)	GRF_CLR_BIT(2 * (id) + 3)
+#define RK3588_GMAC_TXCLK_DLY_ENABLE(id)	GRF_BIT(2 * (id) + 2)
+#define RK3588_GMAC_TXCLK_DLY_DISABLE(id)	GRF_CLR_BIT(2 * (id) + 2)
+
+#define RK3588_GMAC_CLK_RX_DL_CFG(val)		HIWORD_UPDATE(val, 0xFF, 8)
+#define RK3588_GMAC_CLK_TX_DL_CFG(val)		HIWORD_UPDATE(val, 0xFF, 0)
+
+/* php_grf */
+#define RK3588_GRF_GMAC_CON0			0X0008
+#define RK3588_GRF_CLK_CON1			0X0070
+
+#define RK3588_GMAC_PHY_INTF_SEL_RGMII(id)	\
+	(GRF_BIT(3 + (id) * 6) | GRF_CLR_BIT(4 + (id) * 6) | GRF_CLR_BIT(5 + (id) * 6))
+#define RK3588_GMAC_PHY_INTF_SEL_RMII(id)	\
+	(GRF_CLR_BIT(3 + (id) * 6) | GRF_CLR_BIT(4 + (id) * 6) | GRF_BIT(5 + (id) * 6))
+
+#define RK3588_GMAC_CLK_RMII_MODE(id)		GRF_BIT(5 * (id))
+#define RK3588_GMAC_CLK_RGMII_MODE(id)		GRF_CLR_BIT(5 * (id))
+
+#define RK3588_GMAC_CLK_SELET_CRU(id)		GRF_BIT(5 * (id) + 4)
+#define RK3588_GMAC_CLK_SELET_IO(id)		GRF_CLR_BIT(5 * (id) + 4)
+
+#define RK3588_GMA_CLK_RMII_DIV2(id)		GRF_BIT(5 * (id) + 2)
+#define RK3588_GMA_CLK_RMII_DIV20(id)		GRF_CLR_BIT(5 * (id) + 2)
+
+#define RK3588_GMAC_CLK_RGMII_DIV1(id)		\
+			(GRF_CLR_BIT(5 * (id) + 2) | GRF_CLR_BIT(5 * (id) + 3))
+#define RK3588_GMAC_CLK_RGMII_DIV5(id)		\
+			(GRF_BIT(5 * (id) + 2) | GRF_BIT(5 * (id) + 3))
+#define RK3588_GMAC_CLK_RGMII_DIV50(id)		\
+			(GRF_CLR_BIT(5 * (id) + 2) | GRF_BIT(5 * (id) + 3))
+
+#define RK3588_GMAC_CLK_RMII_GATE(id)		GRF_BIT(5 * (id) + 1)
+#define RK3588_GMAC_CLK_RMII_NOGATE(id)		GRF_CLR_BIT(5 * (id) + 1)
+
+static void rk3588_set_to_rgmii(struct rk_priv_data *bsp_priv,
+				int tx_delay, int rx_delay)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+	u32 offset_con, id = bsp_priv->bus_id;
+
+	if (IS_ERR(bsp_priv->grf) || IS_ERR(bsp_priv->php_grf)) {
+		dev_err(dev, "Missing rockchip,grf or rockchip,php_grf property\n");
+		return;
+	}
+
+	offset_con = bsp_priv->bus_id == 1 ? RK3588_GRF_GMAC_CON9 :
+					     RK3588_GRF_GMAC_CON8;
+
+	regmap_write(bsp_priv->php_grf, RK3588_GRF_GMAC_CON0,
+		     RK3588_GMAC_PHY_INTF_SEL_RGMII(id));
+
+	regmap_write(bsp_priv->php_grf, RK3588_GRF_CLK_CON1,
+		     RK3588_GMAC_CLK_RGMII_MODE(id));
+
+	regmap_write(bsp_priv->grf, RK3588_GRF_GMAC_CON7,
+		     DELAY_ENABLE_BY_ID(RK3588, tx_delay, rx_delay, id));
+
+	regmap_write(bsp_priv->grf, offset_con,
+		     DELAY_VALUE(RK3588, tx_delay, rx_delay));
+}
+
+static void rk3588_set_to_rmii(struct rk_priv_data *bsp_priv)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+
+	if (IS_ERR(bsp_priv->php_grf)) {
+		dev_err(dev, "%s: Missing rockchip,php_grf property\n", __func__);
+		return;
+	}
+
+	regmap_write(bsp_priv->php_grf, RK3588_GRF_GMAC_CON0,
+		     RK3588_GMAC_PHY_INTF_SEL_RMII(bsp_priv->bus_id));
+
+	regmap_write(bsp_priv->php_grf, RK3588_GRF_CLK_CON1,
+		     RK3588_GMAC_CLK_RMII_MODE(bsp_priv->bus_id));
+}
+
+static void rk3588_set_gmac_speed(struct rk_priv_data *bsp_priv, int speed)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+	unsigned int val = 0, id = bsp_priv->bus_id;
+
+	switch (speed) {
+	case 10:
+		if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII)
+			val = RK3588_GMA_CLK_RMII_DIV20(id);
+		else
+			val = RK3588_GMAC_CLK_RGMII_DIV50(id);
+		break;
+	case 100:
+		if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII)
+			val = RK3588_GMA_CLK_RMII_DIV2(id);
+		else
+			val = RK3588_GMAC_CLK_RGMII_DIV5(id);
+		break;
+	case 1000:
+		if (bsp_priv->phy_iface != PHY_INTERFACE_MODE_RMII)
+			val = RK3588_GMAC_CLK_RGMII_DIV1(id);
+		else
+			goto err;
+		break;
+	default:
+		goto err;
+	}
+
+	regmap_write(bsp_priv->php_grf, RK3588_GRF_CLK_CON1, val);
+
+	return;
+err:
+	dev_err(dev, "unknown speed value for GMAC speed=%d", speed);
+}
+
+static void rk3588_set_clock_selection(struct rk_priv_data *bsp_priv, bool input,
+				       bool enable)
+{
+	unsigned int val = input ? RK3588_GMAC_CLK_SELET_IO(bsp_priv->bus_id) :
+				   RK3588_GMAC_CLK_SELET_CRU(bsp_priv->bus_id);
+
+	val |= enable ? RK3588_GMAC_CLK_RMII_NOGATE(bsp_priv->bus_id) :
+			RK3588_GMAC_CLK_RMII_GATE(bsp_priv->bus_id);
+
+	regmap_write(bsp_priv->php_grf, RK3588_GRF_CLK_CON1, val);
+}
+
+static const struct rk_gmac_ops rk3588_ops = {
+	.set_to_rgmii = rk3588_set_to_rgmii,
+	.set_to_rmii = rk3588_set_to_rmii,
+	.set_rgmii_speed = rk3588_set_gmac_speed,
+	.set_rmii_speed = rk3588_set_gmac_speed,
+	.set_clock_selection = rk3588_set_clock_selection,
+};
+
 #define RV1108_GRF_GMAC_CON0		0X0900
 
 /* RV1108_GRF_GMAC_CON0 */
@@ -1000,6 +1590,123 @@ static const struct rk_gmac_ops rv1108_ops = {
 	.set_rmii_speed = rv1108_set_rmii_speed,
 };
 
+#define RV1126_GRF_GMAC_CON0		0X0070
+#define RV1126_GRF_GMAC_CON1		0X0074
+#define RV1126_GRF_GMAC_CON2		0X0078
+
+/* RV1126_GRF_GMAC_CON0 */
+#define RV1126_GMAC_PHY_INTF_SEL_RGMII	\
+		(GRF_BIT(4) | GRF_CLR_BIT(5) | GRF_CLR_BIT(6))
+#define RV1126_GMAC_PHY_INTF_SEL_RMII	\
+		(GRF_CLR_BIT(4) | GRF_CLR_BIT(5) | GRF_BIT(6))
+#define RV1126_GMAC_FLOW_CTRL			GRF_BIT(7)
+#define RV1126_GMAC_FLOW_CTRL_CLR		GRF_CLR_BIT(7)
+#define RV1126_M0_GMAC_RXCLK_DLY_ENABLE		GRF_BIT(1)
+#define RV1126_M0_GMAC_RXCLK_DLY_DISABLE	GRF_CLR_BIT(1)
+#define RV1126_M0_GMAC_TXCLK_DLY_ENABLE		GRF_BIT(0)
+#define RV1126_M0_GMAC_TXCLK_DLY_DISABLE	GRF_CLR_BIT(0)
+#define RV1126_M1_GMAC_RXCLK_DLY_ENABLE		GRF_BIT(3)
+#define RV1126_M1_GMAC_RXCLK_DLY_DISABLE	GRF_CLR_BIT(3)
+#define RV1126_M1_GMAC_TXCLK_DLY_ENABLE		GRF_BIT(2)
+#define RV1126_M1_GMAC_TXCLK_DLY_DISABLE	GRF_CLR_BIT(2)
+
+/* RV1126_GRF_GMAC_CON1 && RV1126_GRF_GMAC_CON2 */
+#define RV1126_GMAC_CLK_RX_DL_CFG(val)	HIWORD_UPDATE(val, 0x7F, 8)
+#define RV1126_GMAC_CLK_TX_DL_CFG(val)	HIWORD_UPDATE(val, 0x7F, 0)
+
+static void rv1126_set_to_rgmii(struct rk_priv_data *bsp_priv,
+				int tx_delay, int rx_delay)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+
+	if (IS_ERR(bsp_priv->grf)) {
+		dev_err(dev, "Missing rockchip,grf property\n");
+		return;
+	}
+
+	regmap_write(bsp_priv->grf, RV1126_GRF_GMAC_CON0,
+		     RV1126_GMAC_PHY_INTF_SEL_RGMII |
+		     DELAY_ENABLE(RV1126_M0, tx_delay, rx_delay) |
+		     DELAY_ENABLE(RV1126_M1, tx_delay, rx_delay));
+
+	regmap_write(bsp_priv->grf, RV1126_GRF_GMAC_CON1,
+		     DELAY_VALUE(RV1126, tx_delay, rx_delay));
+
+	regmap_write(bsp_priv->grf, RV1126_GRF_GMAC_CON2,
+		     DELAY_VALUE(RV1126, tx_delay, rx_delay));
+}
+
+static void rv1126_set_to_rmii(struct rk_priv_data *bsp_priv)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+
+	if (IS_ERR(bsp_priv->grf)) {
+		dev_err(dev, "%s: Missing rockchip,grf property\n", __func__);
+		return;
+	}
+
+	regmap_write(bsp_priv->grf, RV1126_GRF_GMAC_CON0,
+		     RV1126_GMAC_PHY_INTF_SEL_RMII);
+}
+
+static void rv1126_set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+	unsigned long rate;
+	int ret;
+
+	switch (speed) {
+	case 10:
+		rate = 2500000;
+		break;
+	case 100:
+		rate = 25000000;
+		break;
+	case 1000:
+		rate = 125000000;
+		break;
+	default:
+		dev_err(dev, "unknown speed value for RGMII speed=%d", speed);
+		return;
+	}
+
+	ret = clk_set_rate(bsp_priv->clk_mac_speed, rate);
+	if (ret)
+		dev_err(dev, "%s: set clk_mac_speed rate %ld failed %d\n",
+			__func__, rate, ret);
+}
+
+static void rv1126_set_rmii_speed(struct rk_priv_data *bsp_priv, int speed)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+	unsigned long rate;
+	int ret;
+
+	switch (speed) {
+	case 10:
+		rate = 2500000;
+		break;
+	case 100:
+		rate = 25000000;
+		break;
+	default:
+		dev_err(dev, "unknown speed value for RGMII speed=%d", speed);
+		return;
+	}
+
+	ret = clk_set_rate(bsp_priv->clk_mac_speed, rate);
+	if (ret)
+		dev_err(dev, "%s: set clk_mac_speed rate %ld failed %d\n",
+			__func__, rate, ret);
+}
+
+static const struct rk_gmac_ops rv1126_ops = {
+	.set_to_rgmii = rv1126_set_to_rgmii,
+	.set_to_rmii = rv1126_set_to_rmii,
+	.set_rgmii_speed = rv1126_set_rgmii_speed,
+	.set_rmii_speed = rv1126_set_rmii_speed,
+};
+
 #define RK_GRF_MACPHY_CON0		0xb00
 #define RK_GRF_MACPHY_CON1		0xb04
 #define RK_GRF_MACPHY_CON2		0xb08
@@ -1090,6 +1797,12 @@ static int rk_gmac_clk_init(struct plat_stmmacenet_data *plat)
 				dev_err(dev, "cannot get clock %s\n",
 					"clk_mac_refout");
 		}
+	} else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_SGMII ||
+		   bsp_priv->phy_iface == PHY_INTERFACE_MODE_QSGMII) {
+		bsp_priv->pclk_xpcs = devm_clk_get(dev, "pclk_xpcs");
+		if (IS_ERR(bsp_priv->pclk_xpcs))
+			dev_err(dev, "cannot get clock %s\n",
+				"pclk_xpcs");
 	}
 
 	bsp_priv->clk_mac_speed = devm_clk_get(dev, "clk_mac_speed");
@@ -1103,14 +1816,17 @@ static int rk_gmac_clk_init(struct plat_stmmacenet_data *plat)
 			clk_set_rate(bsp_priv->clk_mac, 50000000);
 	}
 
-	if (plat->phy_node && bsp_priv->integrated_phy) {
+	if (plat->phy_node) {
 		bsp_priv->clk_phy = of_clk_get(plat->phy_node, 0);
-		if (IS_ERR(bsp_priv->clk_phy)) {
-			ret = PTR_ERR(bsp_priv->clk_phy);
-			dev_err(dev, "Cannot get PHY clock: %d\n", ret);
-			return -EINVAL;
+		/* If it is not integrated_phy, clk_phy is optional */
+		if (bsp_priv->integrated_phy) {
+			if (IS_ERR(bsp_priv->clk_phy)) {
+				ret = PTR_ERR(bsp_priv->clk_phy);
+				dev_err(dev, "Cannot get PHY clock: %d\n", ret);
+				return -EINVAL;
+			}
+			clk_set_rate(bsp_priv->clk_phy, 50000000);
 		}
-		clk_set_rate(bsp_priv->clk_phy, 50000000);
 	}
 
 	return 0;
@@ -1151,11 +1867,18 @@ static int gmac_clk_enable(struct rk_priv_data *bsp_priv, bool enable)
 			if (!IS_ERR(bsp_priv->clk_mac_speed))
 				clk_prepare_enable(bsp_priv->clk_mac_speed);
 
+			if (!IS_ERR(bsp_priv->pclk_xpcs))
+				clk_prepare_enable(bsp_priv->pclk_xpcs);
+
+			if (bsp_priv->ops && bsp_priv->ops->set_clock_selection)
+				bsp_priv->ops->set_clock_selection(bsp_priv,
+					       bsp_priv->clock_input, true);
+
 			/**
 			 * if (!IS_ERR(bsp_priv->clk_mac))
 			 *	clk_prepare_enable(bsp_priv->clk_mac);
 			 */
-			mdelay(5);
+			usleep_range(100, 200);
 			bsp_priv->clk_enabled = true;
 		}
 	} else {
@@ -1177,6 +1900,12 @@ static int gmac_clk_enable(struct rk_priv_data *bsp_priv, bool enable)
 			clk_disable_unprepare(bsp_priv->mac_clk_tx);
 
 			clk_disable_unprepare(bsp_priv->clk_mac_speed);
+
+			clk_disable_unprepare(bsp_priv->pclk_xpcs);
+
+			if (bsp_priv->ops && bsp_priv->ops->set_clock_selection)
+				bsp_priv->ops->set_clock_selection(bsp_priv,
+					      bsp_priv->clock_input, false);
 			/**
 			 * if (!IS_ERR(bsp_priv->clk_mac))
 			 *	clk_disable_unprepare(bsp_priv->clk_mac);
@@ -1188,7 +1917,7 @@ static int gmac_clk_enable(struct rk_priv_data *bsp_priv, bool enable)
 	return 0;
 }
 
-static int phy_power_on(struct rk_priv_data *bsp_priv, bool enable)
+static int rk_gmac_phy_power_on(struct rk_priv_data *bsp_priv, bool enable)
 {
 	struct regulator *ldo = bsp_priv->regulator;
 	int ret;
@@ -1226,6 +1955,7 @@ static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev,
 
 	of_get_phy_mode(dev->of_node, &bsp_priv->phy_iface);
 	bsp_priv->ops = ops;
+	bsp_priv->bus_id = plat->bus_id;
 
 	bsp_priv->regulator = devm_regulator_get_optional(dev, "phy");
 	if (IS_ERR(bsp_priv->regulator)) {
@@ -1252,7 +1982,7 @@ static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev,
 
 	ret = of_property_read_u32(dev->of_node, "tx_delay", &value);
 	if (ret) {
-		bsp_priv->tx_delay = 0x30;
+		bsp_priv->tx_delay = -1;
 		dev_err(dev, "Can not read property: tx_delay.");
 		dev_err(dev, "set tx_delay to 0x%x\n",
 			bsp_priv->tx_delay);
@@ -1263,7 +1993,7 @@ static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev,
 
 	ret = of_property_read_u32(dev->of_node, "rx_delay", &value);
 	if (ret) {
-		bsp_priv->rx_delay = 0x10;
+		bsp_priv->rx_delay = -1;
 		dev_err(dev, "Can not read property: rx_delay.");
 		dev_err(dev, "set rx_delay to 0x%x\n",
 			bsp_priv->rx_delay);
@@ -1274,6 +2004,20 @@ static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev,
 
 	bsp_priv->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
 							"rockchip,grf");
+	bsp_priv->php_grf = syscon_regmap_lookup_by_phandle(dev->of_node,
+							    "rockchip,php_grf");
+	bsp_priv->xpcs = syscon_regmap_lookup_by_phandle(dev->of_node,
+							 "rockchip,xpcs");
+	if (!IS_ERR(bsp_priv->xpcs)) {
+		struct phy *comphy;
+
+		comphy = devm_of_phy_get(&pdev->dev, dev->of_node, NULL);
+		if (IS_ERR(comphy))
+			dev_err(dev, "devm_of_phy_get error\n");
+		ret = phy_init(comphy);
+		if (ret)
+			dev_err(dev, "phy_init error\n");
+	}
 
 	if (plat->phy_node) {
 		bsp_priv->integrated_phy = of_property_read_bool(plat->phy_node,
@@ -1307,30 +2051,45 @@ static int rk_gmac_powerup(struct rk_priv_data *bsp_priv)
 	switch (bsp_priv->phy_iface) {
 	case PHY_INTERFACE_MODE_RGMII:
 		dev_info(dev, "init for RGMII\n");
-		bsp_priv->ops->set_to_rgmii(bsp_priv, bsp_priv->tx_delay,
-					    bsp_priv->rx_delay);
+		if (bsp_priv->ops && bsp_priv->ops->set_to_rgmii)
+			bsp_priv->ops->set_to_rgmii(bsp_priv, bsp_priv->tx_delay,
+						    bsp_priv->rx_delay);
 		break;
 	case PHY_INTERFACE_MODE_RGMII_ID:
 		dev_info(dev, "init for RGMII_ID\n");
-		bsp_priv->ops->set_to_rgmii(bsp_priv, 0, 0);
+		if (bsp_priv->ops && bsp_priv->ops->set_to_rgmii)
+			bsp_priv->ops->set_to_rgmii(bsp_priv, -1, -1);
 		break;
 	case PHY_INTERFACE_MODE_RGMII_RXID:
 		dev_info(dev, "init for RGMII_RXID\n");
-		bsp_priv->ops->set_to_rgmii(bsp_priv, bsp_priv->tx_delay, 0);
+		if (bsp_priv->ops && bsp_priv->ops->set_to_rgmii)
+			bsp_priv->ops->set_to_rgmii(bsp_priv, bsp_priv->tx_delay, -1);
 		break;
 	case PHY_INTERFACE_MODE_RGMII_TXID:
 		dev_info(dev, "init for RGMII_TXID\n");
-		bsp_priv->ops->set_to_rgmii(bsp_priv, 0, bsp_priv->rx_delay);
+		if (bsp_priv->ops && bsp_priv->ops->set_to_rgmii)
+			bsp_priv->ops->set_to_rgmii(bsp_priv, -1, bsp_priv->rx_delay);
 		break;
 	case PHY_INTERFACE_MODE_RMII:
 		dev_info(dev, "init for RMII\n");
-		bsp_priv->ops->set_to_rmii(bsp_priv);
+		if (bsp_priv->ops && bsp_priv->ops->set_to_rmii)
+			bsp_priv->ops->set_to_rmii(bsp_priv);
+		break;
+	case PHY_INTERFACE_MODE_SGMII:
+		dev_info(dev, "init for SGMII\n");
+		if (bsp_priv->ops && bsp_priv->ops->set_to_sgmii)
+			bsp_priv->ops->set_to_sgmii(bsp_priv);
+		break;
+	case PHY_INTERFACE_MODE_QSGMII:
+		dev_info(dev, "init for QSGMII\n");
+		if (bsp_priv->ops && bsp_priv->ops->set_to_qsgmii)
+			bsp_priv->ops->set_to_qsgmii(bsp_priv);
 		break;
 	default:
 		dev_err(dev, "NO interface defined!\n");
 	}
 
-	ret = phy_power_on(bsp_priv, true);
+	ret = rk_gmac_phy_power_on(bsp_priv, true);
 	if (ret) {
 		gmac_clk_enable(bsp_priv, false);
 		return ret;
@@ -1351,7 +2110,7 @@ static void rk_gmac_powerdown(struct rk_priv_data *gmac)
 
 	pm_runtime_put_sync(&gmac->pdev->dev);
 
-	phy_power_on(gmac, false);
+	rk_gmac_phy_power_on(gmac, false);
 	gmac_clk_enable(gmac, false);
 }
 
@@ -1365,16 +2124,96 @@ static void rk_fix_speed(void *priv, unsigned int speed)
 	case PHY_INTERFACE_MODE_RGMII_ID:
 	case PHY_INTERFACE_MODE_RGMII_RXID:
 	case PHY_INTERFACE_MODE_RGMII_TXID:
-		bsp_priv->ops->set_rgmii_speed(bsp_priv, speed);
+		if (bsp_priv->ops && bsp_priv->ops->set_rgmii_speed)
+			bsp_priv->ops->set_rgmii_speed(bsp_priv, speed);
 		break;
 	case PHY_INTERFACE_MODE_RMII:
-		bsp_priv->ops->set_rmii_speed(bsp_priv, speed);
+		if (bsp_priv->ops && bsp_priv->ops->set_rmii_speed)
+			bsp_priv->ops->set_rmii_speed(bsp_priv, speed);
+		break;
+	case PHY_INTERFACE_MODE_SGMII:
+	case PHY_INTERFACE_MODE_QSGMII:
 		break;
 	default:
 		dev_err(dev, "unsupported interface %d", bsp_priv->phy_iface);
 	}
 }
 
+void dwmac_rk_set_rgmii_delayline(struct stmmac_priv *priv,
+				  int tx_delay, int rx_delay)
+{
+	struct rk_priv_data *bsp_priv = priv->plat->bsp_priv;
+
+	if (bsp_priv->ops->set_to_rgmii) {
+		bsp_priv->ops->set_to_rgmii(bsp_priv, tx_delay, rx_delay);
+		bsp_priv->tx_delay = tx_delay;
+		bsp_priv->rx_delay = rx_delay;
+	}
+}
+EXPORT_SYMBOL(dwmac_rk_set_rgmii_delayline);
+
+void dwmac_rk_get_rgmii_delayline(struct stmmac_priv *priv,
+				  int *tx_delay, int *rx_delay)
+{
+	struct rk_priv_data *bsp_priv = priv->plat->bsp_priv;
+
+	if (!bsp_priv->ops->set_to_rgmii)
+		return;
+
+	*tx_delay = bsp_priv->tx_delay;
+	*rx_delay = bsp_priv->rx_delay;
+}
+EXPORT_SYMBOL(dwmac_rk_get_rgmii_delayline);
+
+int dwmac_rk_get_phy_interface(struct stmmac_priv *priv)
+{
+	struct rk_priv_data *bsp_priv = priv->plat->bsp_priv;
+
+	return bsp_priv->phy_iface;
+}
+EXPORT_SYMBOL(dwmac_rk_get_phy_interface);
+
+static void rk_get_eth_addr(void *priv, unsigned char *addr)
+{
+	struct rk_priv_data *bsp_priv = priv;
+	struct device *dev = &bsp_priv->pdev->dev;
+	unsigned char ethaddr[ETH_ALEN * MAX_ETH] = {0};
+	int ret, id = bsp_priv->bus_id;
+
+	if (is_valid_ether_addr(addr))
+		goto out;
+
+	if (id < 0 || id >= MAX_ETH) {
+		dev_err(dev, "%s: Invalid ethernet bus id %d\n", __func__, id);
+		return;
+	}
+
+	ret = rk_vendor_read(LAN_MAC_ID, ethaddr, ETH_ALEN * MAX_ETH);
+	if (ret <= 0 ||
+	    !is_valid_ether_addr(&ethaddr[id * ETH_ALEN])) {
+		dev_err(dev, "%s: rk_vendor_read eth mac address failed (%d)\n",
+			__func__, ret);
+		random_ether_addr(&ethaddr[id * ETH_ALEN]);
+		memcpy(addr, &ethaddr[id * ETH_ALEN], ETH_ALEN);
+		dev_err(dev, "%s: generate random eth mac address: %pM\n", __func__, addr);
+
+		ret = rk_vendor_write(LAN_MAC_ID, ethaddr, ETH_ALEN * MAX_ETH);
+		if (ret != 0)
+			dev_err(dev, "%s: rk_vendor_write eth mac address failed (%d)\n",
+				__func__, ret);
+
+		ret = rk_vendor_read(LAN_MAC_ID, ethaddr, ETH_ALEN * MAX_ETH);
+		if (ret != ETH_ALEN * MAX_ETH)
+			dev_err(dev, "%s: id: %d rk_vendor_read eth mac address failed (%d)\n",
+				__func__, id, ret);
+	} else {
+		memcpy(addr, &ethaddr[id * ETH_ALEN], ETH_ALEN);
+	}
+
+out:
+	dev_err(dev, "%s: mac address: %pM\n", __func__, addr);
+}
+
 static int rk_gmac_probe(struct platform_device *pdev)
 {
 	struct plat_stmmacenet_data *plat_dat;
@@ -1396,8 +2235,11 @@ static int rk_gmac_probe(struct platform_device *pdev)
 	if (IS_ERR(plat_dat))
 		return PTR_ERR(plat_dat);
 
-	plat_dat->has_gmac = true;
+	if (!of_device_is_compatible(pdev->dev.of_node, "snps,dwmac-4.20a"))
+		plat_dat->has_gmac = true;
+
 	plat_dat->fix_mac_speed = rk_fix_speed;
+	plat_dat->get_eth_addr = rk_get_eth_addr;
 
 	plat_dat->bsp_priv = rk_gmac_setup(pdev, plat_dat, data);
 	if (IS_ERR(plat_dat->bsp_priv)) {
@@ -1417,6 +2259,10 @@ static int rk_gmac_probe(struct platform_device *pdev)
 	if (ret)
 		goto err_gmac_powerdown;
 
+	ret = dwmac_rk_create_loopback_sysfs(&pdev->dev);
+	if (ret)
+		goto err_gmac_powerdown;
+
 	return 0;
 
 err_gmac_powerdown:
@@ -1433,6 +2279,7 @@ static int rk_gmac_remove(struct platform_device *pdev)
 	int ret = stmmac_dvr_remove(&pdev->dev);
 
 	rk_gmac_powerdown(bsp_priv);
+	dwmac_rk_remove_loopback_sysfs(&pdev->dev);
 
 	return ret;
 }
@@ -1470,14 +2317,19 @@ static SIMPLE_DEV_PM_OPS(rk_gmac_pm_ops, rk_gmac_suspend, rk_gmac_resume);
 
 static const struct of_device_id rk_gmac_dwmac_match[] = {
 	{ .compatible = "rockchip,px30-gmac",	.data = &px30_ops   },
+	{ .compatible = "rockchip,rk1808-gmac", .data = &rk1808_ops },
 	{ .compatible = "rockchip,rk3128-gmac", .data = &rk3128_ops },
 	{ .compatible = "rockchip,rk3228-gmac", .data = &rk3228_ops },
 	{ .compatible = "rockchip,rk3288-gmac", .data = &rk3288_ops },
+	{ .compatible = "rockchip,rk3308-mac",  .data = &rk3308_ops },
 	{ .compatible = "rockchip,rk3328-gmac", .data = &rk3328_ops },
 	{ .compatible = "rockchip,rk3366-gmac", .data = &rk3366_ops },
 	{ .compatible = "rockchip,rk3368-gmac", .data = &rk3368_ops },
 	{ .compatible = "rockchip,rk3399-gmac", .data = &rk3399_ops },
+	{ .compatible = "rockchip,rk3568-gmac", .data = &rk3568_ops },
+	{ .compatible = "rockchip,rk3588-gmac", .data = &rk3588_ops },
 	{ .compatible = "rockchip,rv1108-gmac", .data = &rv1108_ops },
+	{ .compatible = "rockchip,rv1126-gmac", .data = &rv1126_ops },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, rk_gmac_dwmac_match);
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
index fbf2deafe..2bac49b49 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
@@ -218,18 +218,11 @@ static void dwmac1000_dump_dma_regs(void __iomem *ioaddr, u32 *reg_space)
 				readl(ioaddr + DMA_BUS_MODE + i * 4);
 }
 
-static int dwmac1000_get_hw_feature(void __iomem *ioaddr,
-				    struct dma_features *dma_cap)
+static void dwmac1000_get_hw_feature(void __iomem *ioaddr,
+				     struct dma_features *dma_cap)
 {
 	u32 hw_cap = readl(ioaddr + DMA_HW_FEATURE);
 
-	if (!hw_cap) {
-		/* 0x00000000 is the value read on old hardware that does not
-		 * implement this register
-		 */
-		return -EOPNOTSUPP;
-	}
-
 	dma_cap->mbps_10_100 = (hw_cap & DMA_HW_FEAT_MIISEL);
 	dma_cap->mbps_1000 = (hw_cap & DMA_HW_FEAT_GMIISEL) >> 1;
 	dma_cap->half_duplex = (hw_cap & DMA_HW_FEAT_HDSEL) >> 2;
@@ -259,8 +252,6 @@ static int dwmac1000_get_hw_feature(void __iomem *ioaddr,
 	dma_cap->number_tx_channel = (hw_cap & DMA_HW_FEAT_TXCHCNT) >> 22;
 	/* Alternate (enhanced) DESC mode */
 	dma_cap->enh_desc = (hw_cap & DMA_HW_FEAT_ENHDESSEL) >> 24;
-
-	return 0;
 }
 
 static void dwmac1000_rx_watchdog(void __iomem *ioaddr, u32 riwt,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
index 935510cdc..a7249e407 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
@@ -337,8 +337,8 @@ static void dwmac4_dma_tx_chan_op_mode(void __iomem *ioaddr, int mode,
 	writel(mtl_tx_op, ioaddr +  MTL_CHAN_TX_OP_MODE(channel));
 }
 
-static int dwmac4_get_hw_feature(void __iomem *ioaddr,
-				 struct dma_features *dma_cap)
+static void dwmac4_get_hw_feature(void __iomem *ioaddr,
+				  struct dma_features *dma_cap)
 {
 	u32 hw_cap = readl(ioaddr + GMAC_HW_FEATURE0);
 
@@ -425,8 +425,6 @@ static int dwmac4_get_hw_feature(void __iomem *ioaddr,
 	dma_cap->frpbs = (hw_cap & GMAC_HW_FEAT_FRPBS) >> 11;
 	dma_cap->frpsel = (hw_cap & GMAC_HW_FEAT_FRPSEL) >> 10;
 	dma_cap->dvlan = (hw_cap & GMAC_HW_FEAT_DVLAN) >> 5;
-
-	return 0;
 }
 
 /* Enable/disable TSO feature and set MSS */
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
index 71e50751e..e40e312c0 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_lib.c
@@ -22,7 +22,7 @@ int dwmac4_dma_reset(void __iomem *ioaddr)
 
 	return readl_poll_timeout(ioaddr + DMA_BUS_MODE, value,
 				 !(value & DMA_BUS_MODE_SFT_RESET),
-				 10000, 1000000);
+				 500, 1000000);
 }
 
 void dwmac4_set_rx_tail_ptr(void __iomem *ioaddr, u32 tail_ptr, u32 chan)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
index a5583d706..77308c5c5 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_dma.c
@@ -365,8 +365,8 @@ static int dwxgmac2_dma_interrupt(void __iomem *ioaddr,
 	return ret;
 }
 
-static int dwxgmac2_get_hw_feature(void __iomem *ioaddr,
-				   struct dma_features *dma_cap)
+static void dwxgmac2_get_hw_feature(void __iomem *ioaddr,
+				    struct dma_features *dma_cap)
 {
 	u32 hw_cap;
 
@@ -439,8 +439,6 @@ static int dwxgmac2_get_hw_feature(void __iomem *ioaddr,
 	dma_cap->frpes = (hw_cap & XGMAC_HWFEAT_FRPES) >> 11;
 	dma_cap->frpbs = (hw_cap & XGMAC_HWFEAT_FRPPB) >> 9;
 	dma_cap->frpsel = (hw_cap & XGMAC_HWFEAT_FRPSEL) >> 3;
-
-	return 0;
 }
 
 static void dwxgmac2_rx_watchdog(void __iomem *ioaddr, u32 riwt, u32 nchan)
diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h
index 8b7ec2457..b0b84244e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/hwif.h
+++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h
@@ -203,8 +203,8 @@ struct stmmac_dma_ops {
 	int (*dma_interrupt) (void __iomem *ioaddr,
 			      struct stmmac_extra_stats *x, u32 chan);
 	/* If supported then get the optional core features */
-	int (*get_hw_feature)(void __iomem *ioaddr,
-			      struct dma_features *dma_cap);
+	void (*get_hw_feature)(void __iomem *ioaddr,
+			       struct dma_features *dma_cap);
 	/* Program the HW RX Watchdog */
 	void (*rx_watchdog)(void __iomem *ioaddr, u32 riwt, u32 number_chan);
 	void (*set_tx_ring_len)(void __iomem *ioaddr, u32 len, u32 chan);
@@ -255,7 +255,7 @@ struct stmmac_dma_ops {
 #define stmmac_dma_interrupt_status(__priv, __args...) \
 	stmmac_do_callback(__priv, dma, dma_interrupt, __args)
 #define stmmac_get_hw_feature(__priv, __args...) \
-	stmmac_do_callback(__priv, dma, get_hw_feature, __args)
+	stmmac_do_void_callback(__priv, dma, get_hw_feature, __args)
 #define stmmac_rx_watchdog(__priv, __args...) \
 	stmmac_do_void_callback(__priv, dma, rx_watchdog, __args)
 #define stmmac_set_tx_ring_len(__priv, __args...) \
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 14ea0168b..212621f59 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -236,7 +236,7 @@ static void stmmac_clk_csr_set(struct stmmac_priv *priv)
 {
 	u32 clk_rate;
 
-	clk_rate = clk_get_rate(priv->plat->stmmac_clk);
+	clk_rate = clk_get_rate(priv->plat->pclk);
 
 	/* Platform provided default clk_csr would be assumed valid
 	 * for all other cases except for the below mentioned ones.
@@ -256,7 +256,7 @@ static void stmmac_clk_csr_set(struct stmmac_priv *priv)
 			priv->clk_csr = STMMAC_CSR_100_150M;
 		else if ((clk_rate >= CSR_F_150M) && (clk_rate < CSR_F_250M))
 			priv->clk_csr = STMMAC_CSR_150_250M;
-		else if ((clk_rate >= CSR_F_250M) && (clk_rate <= CSR_F_300M))
+		else if ((clk_rate >= CSR_F_250M) && (clk_rate < CSR_F_300M))
 			priv->clk_csr = STMMAC_CSR_250_300M;
 	}
 
@@ -628,7 +628,7 @@ static int stmmac_hwtstamp_set(struct net_device *dev, struct ifreq *ifr)
 			config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
 			ptp_v2 = PTP_TCR_TSVER2ENA;
 			snap_type_sel = PTP_TCR_SNAPTYPSEL_1;
-			if (priv->synopsys_id < DWMAC_CORE_4_10)
+			if (priv->synopsys_id != DWMAC_CORE_5_10)
 				ts_event_en = PTP_TCR_TSEVNTENA;
 			ptp_over_ipv4_udp = PTP_TCR_TSIPV4ENA;
 			ptp_over_ipv6_udp = PTP_TCR_TSIPV6ENA;
@@ -738,10 +738,19 @@ int stmmac_init_tstamp_counter(struct stmmac_priv *priv, u32 systime_flags)
 	struct timespec64 now;
 	u32 sec_inc = 0;
 	u64 temp = 0;
+	int ret;
 
 	if (!(priv->dma_cap.time_stamp || priv->dma_cap.atime_stamp))
 		return -EOPNOTSUPP;
 
+	ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
+	if (ret < 0) {
+		netdev_warn(priv->dev,
+			    "failed to enable PTP reference clock: %pe\n",
+			    ERR_PTR(ret));
+		return ret;
+	}
+
 	stmmac_config_hw_tstamping(priv, priv->ptpaddr, systime_flags);
 	priv->systime_flags = systime_flags;
 
@@ -807,6 +816,8 @@ static int stmmac_init_ptp(struct stmmac_priv *priv)
 	priv->hwts_tx_en = 0;
 	priv->hwts_rx_en = 0;
 
+	stmmac_ptp_register(priv);
+
 	return 0;
 }
 
@@ -1339,13 +1350,17 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p,
 {
 	struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
 	struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
+	gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);
 
-	buf->page = page_pool_dev_alloc_pages(rx_q->page_pool);
+	if (priv->dma_cap.addr64 <= 32)
+		gfp |= GFP_DMA32;
+
+	buf->page = page_pool_alloc_pages(rx_q->page_pool, gfp);
 	if (!buf->page)
 		return -ENOMEM;
 
 	if (priv->sph) {
-		buf->sec_page = page_pool_dev_alloc_pages(rx_q->page_pool);
+		buf->sec_page = page_pool_alloc_pages(rx_q->page_pool, gfp);
 		if (!buf->sec_page)
 			return -ENOMEM;
 
@@ -2337,6 +2352,9 @@ static void stmmac_check_ether_addr(struct stmmac_priv *priv)
 {
 	if (!is_valid_ether_addr(priv->dev->dev_addr)) {
 		stmmac_get_umac_addr(priv, priv->hw, priv->dev->dev_addr, 0);
+		if (likely(priv->plat->get_eth_addr))
+			priv->plat->get_eth_addr(priv->plat->bsp_priv,
+				priv->dev->dev_addr);
 		if (!is_valid_ether_addr(priv->dev->dev_addr))
 			eth_hw_addr_random(priv->dev);
 		dev_info(priv->device, "device MAC address %pM\n",
@@ -2688,7 +2706,7 @@ static void stmmac_safety_feat_configuration(struct stmmac_priv *priv)
 /**
  * stmmac_hw_setup - setup mac in a usable state.
  *  @dev : pointer to the device structure.
- *  @ptp_register: register PTP if set
+ *  @init_ptp: initialize PTP if set
  *  Description:
  *  this is the main function to setup the HW in a usable state because the
  *  dma engine is reset, the core registers are configured (e.g. AXI,
@@ -2698,7 +2716,7 @@ static void stmmac_safety_feat_configuration(struct stmmac_priv *priv)
  *  0 on success and an appropriate (-)ve integer as defined in errno.h
  *  file on failure.
  */
-static int stmmac_hw_setup(struct net_device *dev, bool ptp_register)
+static int stmmac_hw_setup(struct net_device *dev, bool init_ptp)
 {
 	struct stmmac_priv *priv = netdev_priv(dev);
 	u32 rx_cnt = priv->plat->rx_queues_to_use;
@@ -2754,22 +2772,14 @@ static int stmmac_hw_setup(struct net_device *dev, bool ptp_register)
 
 	stmmac_mmc_setup(priv);
 
-	if (ptp_register) {
-		ret = clk_prepare_enable(priv->plat->clk_ptp_ref);
-		if (ret < 0)
-			netdev_warn(priv->dev,
-				    "failed to enable PTP reference clock: %pe\n",
-				    ERR_PTR(ret));
+	if (init_ptp) {
+		ret = stmmac_init_ptp(priv);
+		if (ret == -EOPNOTSUPP)
+			netdev_warn(priv->dev, "PTP not supported by HW\n");
+		else if (ret)
+			netdev_warn(priv->dev, "PTP init failed\n");
 	}
 
-	ret = stmmac_init_ptp(priv);
-	if (ret == -EOPNOTSUPP)
-		netdev_warn(priv->dev, "PTP not supported by HW\n");
-	else if (ret)
-		netdev_warn(priv->dev, "PTP init failed\n");
-	else if (ptp_register)
-		stmmac_ptp_register(priv);
-
 	priv->eee_tw_timer = STMMAC_DEFAULT_TWT_LS;
 
 	/* Convert the timer from msec to usec */
@@ -3684,6 +3694,10 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
 	struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
 	int len, dirty = stmmac_rx_dirty(priv, queue);
 	unsigned int entry = rx_q->dirty_rx;
+	gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);
+
+	if (priv->dma_cap.addr64 <= 32)
+		gfp |= GFP_DMA32;
 
 	len = DIV_ROUND_UP(priv->dma_buf_sz, PAGE_SIZE) * PAGE_SIZE;
 
@@ -3698,13 +3712,13 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
 			p = rx_q->dma_rx + entry;
 
 		if (!buf->page) {
-			buf->page = page_pool_dev_alloc_pages(rx_q->page_pool);
+			buf->page = page_pool_alloc_pages(rx_q->page_pool, gfp);
 			if (!buf->page)
 				break;
 		}
 
 		if (priv->sph && !buf->sec_page) {
-			buf->sec_page = page_pool_dev_alloc_pages(rx_q->page_pool);
+			buf->sec_page = page_pool_alloc_pages(rx_q->page_pool, gfp);
 			if (!buf->sec_page)
 				break;
 
@@ -5066,7 +5080,7 @@ int stmmac_dvr_probe(struct device *device,
 		dev_info(priv->device, "TSO feature enabled\n");
 	}
 
-	if (priv->dma_cap.sphen && !priv->plat->sph_disable) {
+	if (priv->dma_cap.sphen) {
 		ndev->hw_features |= NETIF_F_GRO;
 		priv->sph = true;
 		dev_info(priv->device, "SPH feature enabled\n");
@@ -5438,7 +5452,7 @@ static int __init stmmac_cmdline_opt(char *str)
 	char *opt;
 
 	if (!str || !*str)
-		return 1;
+		return -EINVAL;
 	while ((opt = strsep(&str, ",")) != NULL) {
 		if (!strncmp(opt, "debug:", 6)) {
 			if (kstrtoint(opt + 6, 0, &debug))
@@ -5469,11 +5483,11 @@ static int __init stmmac_cmdline_opt(char *str)
 				goto err;
 		}
 	}
-	return 1;
+	return 0;
 
 err:
 	pr_err("%s: ERROR broken module parameter conversion", __func__);
-	return 1;
+	return -EINVAL;
 }
 
 __setup("stmmaceth=", stmmac_cmdline_opt);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index 1ed74cfb6..9468a2be6 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -508,14 +508,6 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
 		plat->pmt = 1;
 	}
 
-	if (of_device_is_compatible(np, "snps,dwmac-3.40a")) {
-		plat->has_gmac = 1;
-		plat->enh_desc = 1;
-		plat->tx_coe = 1;
-		plat->bugged_jumbo = 1;
-		plat->pmt = 1;
-	}
-
 	if (of_device_is_compatible(np, "snps,dwmac-4.00") ||
 	    of_device_is_compatible(np, "snps,dwmac-4.10a") ||
 	    of_device_is_compatible(np, "snps,dwmac-4.20a") ||
@@ -586,7 +578,7 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
 		clk_prepare_enable(plat->stmmac_clk);
 	}
 
-	plat->pclk = devm_clk_get(&pdev->dev, "pclk");
+	plat->pclk = devm_clk_get(&pdev->dev, "pclk_mac");
 	if (IS_ERR(plat->pclk)) {
 		if (PTR_ERR(plat->pclk) == -EPROBE_DEFER)
 			goto error_pclk_get;
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 21d89d80d..a8d527cda 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -222,8 +222,6 @@ static umode_t nvmem_bin_attr_is_visible(struct kobject *kobj,
 	struct device *dev = kobj_to_dev(kobj);
 	struct nvmem_device *nvmem = to_nvmem_device(dev);
 
-	attr->size = nvmem->size;
-
 	return nvmem_bin_attr_get_umode(nvmem);
 }
 
@@ -1667,7 +1665,11 @@ static void __exit nvmem_exit(void)
 	bus_unregister(&nvmem_bus_type);
 }
 
+#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT
+arch_initcall_sync(nvmem_init);
+#else
 subsys_initcall(nvmem_init);
+#endif
 module_exit(nvmem_exit);
 
 MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
diff --git a/drivers/nvmem/rockchip-efuse.c b/drivers/nvmem/rockchip-efuse.c
index e4579de5d..f89018376 100644
--- a/drivers/nvmem/rockchip-efuse.c
+++ b/drivers/nvmem/rockchip-efuse.c
@@ -7,6 +7,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/clk-provider.h>
 #include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/io.h>
@@ -16,7 +17,53 @@
 #include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
-
+#include <linux/rockchip/rockchip_sip.h>
+
+#define T_CSB_P_S		0
+#define T_PGENB_P_S		0
+#define T_LOAD_P_S		0
+#define T_ADDR_P_S		0
+#define T_STROBE_P_S		(0 + 110) /* 1.1us */
+#define T_CSB_P_L		(0 + 110 + 1000 + 20) /* 200ns */
+#define T_PGENB_P_L		(0 + 110 + 1000 + 20)
+#define T_LOAD_P_L		(0 + 110 + 1000 + 20)
+#define T_ADDR_P_L		(0 + 110 + 1000 + 20)
+#define T_STROBE_P_L		(0 + 110 + 1000) /* 10us */
+#define T_CSB_R_S		0
+#define T_PGENB_R_S		0
+#define T_LOAD_R_S		0
+#define T_ADDR_R_S		2
+#define T_STROBE_R_S		(2 + 3)
+#define T_CSB_R_L		(2 + 3 + 3 + 3)
+#define T_PGENB_R_L		(2 + 3 + 3 + 3)
+#define T_LOAD_R_L		(2 + 3 + 3 + 3)
+#define T_ADDR_R_L		(2 + 3 + 3 + 2)
+#define T_STROBE_R_L		(2 + 3 + 3)
+
+#define T_CSB_P			0x28
+#define T_PGENB_P		0x2c
+#define T_LOAD_P		0x30
+#define T_ADDR_P		0x34
+#define T_STROBE_P		0x38
+#define T_CSB_R			0x3c
+#define T_PGENB_R		0x40
+#define T_LOAD_R		0x44
+#define T_ADDR_R		0x48
+#define T_STROBE_R		0x4c
+
+#define RK1808_MOD		0x00
+#define RK1808_INT_STATUS	RK3328_INT_STATUS
+#define RK1808_DOUT		RK3328_DOUT
+#define RK1808_AUTO_CTRL	RK3328_AUTO_CTRL
+#define RK1808_USER_MODE	BIT(0)
+#define RK1808_INT_FINISH	RK3328_INT_FINISH
+#define RK1808_AUTO_ENB		RK3328_AUTO_ENB
+#define RK1808_AUTO_RD		RK3328_AUTO_RD
+#define RK1808_A_SHIFT		RK3399_A_SHIFT
+#define RK1808_A_MASK		RK3399_A_MASK
+#define RK1808_NBYTES		RK3399_NBYTES
+
+#define RK3128_A_SHIFT		7
 #define RK3288_A_SHIFT		6
 #define RK3288_A_MASK		0x3ff
 #define RK3288_PGENB		BIT(3)
@@ -49,9 +96,149 @@
 struct rockchip_efuse_chip {
 	struct device *dev;
 	void __iomem *base;
-	struct clk *clk;
+	struct clk_bulk_data *clks;
+	int num_clks;
+	phys_addr_t phys;
+	struct mutex mutex;
 };
 
+static void rk1808_efuse_timing_init(void __iomem *base)
+{
+	/* enable auto mode */
+	writel(readl(base + RK1808_MOD) & (~RK1808_USER_MODE),
+	       base + RK1808_MOD);
+
+	/* setup efuse timing */
+	writel((T_CSB_P_S << 16) | T_CSB_P_L, base + T_CSB_P);
+	writel((T_PGENB_P_S << 16) | T_PGENB_P_L, base + T_PGENB_P);
+	writel((T_LOAD_P_S << 16) | T_LOAD_P_L, base + T_LOAD_P);
+	writel((T_ADDR_P_S << 16) | T_ADDR_P_L, base + T_ADDR_P);
+	writel((T_STROBE_P_S << 16) | T_STROBE_P_L, base + T_STROBE_P);
+	writel((T_CSB_R_S << 16) | T_CSB_R_L, base + T_CSB_R);
+	writel((T_PGENB_R_S << 16) | T_PGENB_R_L, base + T_PGENB_R);
+	writel((T_LOAD_R_S << 16) | T_LOAD_R_L, base + T_LOAD_R);
+	writel((T_ADDR_R_S << 16) | T_ADDR_R_L, base + T_ADDR_R);
+	writel((T_STROBE_R_S << 16) | T_STROBE_R_L, base + T_STROBE_R);
+}
+
+static void rk1808_efuse_timing_deinit(void __iomem *base)
+{
+	/* disable auto mode */
+	writel(readl(base + RK1808_MOD) | RK1808_USER_MODE,
+	       base + RK1808_MOD);
+
+	/* clear efuse timing */
+	writel(0, base + T_CSB_P);
+	writel(0, base + T_PGENB_P);
+	writel(0, base + T_LOAD_P);
+	writel(0, base + T_ADDR_P);
+	writel(0, base + T_STROBE_P);
+	writel(0, base + T_CSB_R);
+	writel(0, base + T_PGENB_R);
+	writel(0, base + T_LOAD_R);
+	writel(0, base + T_ADDR_R);
+	writel(0, base + T_STROBE_R);
+}
+
+static int rockchip_rk1808_efuse_read(void *context, unsigned int offset,
+				      void *val, size_t bytes)
+{
+	struct rockchip_efuse_chip *efuse = context;
+	unsigned int addr_start, addr_end, addr_offset, addr_len;
+	u32 out_value, status;
+	u8 *buf;
+	int ret, i = 0;
+
+	mutex_lock(&efuse->mutex);
+
+	ret = clk_bulk_prepare_enable(efuse->num_clks, efuse->clks);
+	if (ret < 0) {
+		dev_err(efuse->dev, "failed to prepare/enable efuse clk\n");
+		goto out;
+	}
+
+	addr_start = rounddown(offset, RK1808_NBYTES) / RK1808_NBYTES;
+	addr_end = roundup(offset + bytes, RK1808_NBYTES) / RK1808_NBYTES;
+	addr_offset = offset % RK1808_NBYTES;
+	addr_len = addr_end - addr_start;
+
+	buf = kzalloc(sizeof(*buf) * addr_len * RK1808_NBYTES, GFP_KERNEL);
+	if (!buf) {
+		ret = -ENOMEM;
+		goto nomem;
+	}
+
+	rk1808_efuse_timing_init(efuse->base);
+
+	while (addr_len--) {
+		writel(RK1808_AUTO_RD | RK1808_AUTO_ENB |
+		       ((addr_start++ & RK1808_A_MASK) << RK1808_A_SHIFT),
+		       efuse->base + RK1808_AUTO_CTRL);
+		udelay(2);
+		status = readl(efuse->base + RK1808_INT_STATUS);
+		if (!(status & RK1808_INT_FINISH)) {
+			ret = -EIO;
+			goto err;
+		}
+		out_value = readl(efuse->base + RK1808_DOUT);
+		writel(RK1808_INT_FINISH, efuse->base + RK1808_INT_STATUS);
+
+		memcpy(&buf[i], &out_value, RK1808_NBYTES);
+		i += RK1808_NBYTES;
+	}
+	memcpy(val, buf + addr_offset, bytes);
+err:
+	rk1808_efuse_timing_deinit(efuse->base);
+	kfree(buf);
+nomem:
+	rk1808_efuse_timing_deinit(efuse->base);
+	clk_bulk_disable_unprepare(efuse->num_clks, efuse->clks);
+out:
+	mutex_unlock(&efuse->mutex);
+
+	return ret;
+}
+
+static int rockchip_rk3128_efuse_read(void *context, unsigned int offset,
+				      void *val, size_t bytes)
+{
+	struct rockchip_efuse_chip *efuse = context;
+	u8 *buf = val;
+	int ret;
+
+	ret = clk_bulk_prepare_enable(efuse->num_clks, efuse->clks);
+	if (ret < 0) {
+		dev_err(efuse->dev, "failed to prepare/enable efuse clk\n");
+		return ret;
+	}
+
+	writel(RK3288_LOAD | RK3288_PGENB, efuse->base + REG_EFUSE_CTRL);
+	udelay(1);
+	while (bytes--) {
+		writel(readl(efuse->base + REG_EFUSE_CTRL) &
+			     (~(RK3288_A_MASK << RK3128_A_SHIFT)),
+			     efuse->base + REG_EFUSE_CTRL);
+		writel(readl(efuse->base + REG_EFUSE_CTRL) |
+			     ((offset++ & RK3288_A_MASK) << RK3128_A_SHIFT),
+			     efuse->base + REG_EFUSE_CTRL);
+		udelay(1);
+		writel(readl(efuse->base + REG_EFUSE_CTRL) |
+			     RK3288_STROBE, efuse->base + REG_EFUSE_CTRL);
+		udelay(1);
+		*buf++ = readb(efuse->base + REG_EFUSE_DOUT);
+		writel(readl(efuse->base + REG_EFUSE_CTRL) &
+		       (~RK3288_STROBE), efuse->base + REG_EFUSE_CTRL);
+		udelay(1);
+	}
+
+	/* Switch to standby mode */
+	writel(RK3288_PGENB | RK3288_CSB, efuse->base + REG_EFUSE_CTRL);
+
+	clk_bulk_disable_unprepare(efuse->num_clks, efuse->clks);
+
+	return 0;
+}
+
 static int rockchip_rk3288_efuse_read(void *context, unsigned int offset,
 				      void *val, size_t bytes)
 {
@@ -59,7 +246,7 @@ static int rockchip_rk3288_efuse_read(void *context, unsigned int offset,
 	u8 *buf = val;
 	int ret;
 
-	ret = clk_prepare_enable(efuse->clk);
+	ret = clk_bulk_prepare_enable(efuse->num_clks, efuse->clks);
 	if (ret < 0) {
 		dev_err(efuse->dev, "failed to prepare/enable efuse clk\n");
 		return ret;
@@ -87,7 +274,53 @@ static int rockchip_rk3288_efuse_read(void *context, unsigned int offset,
 	/* Switch to standby mode */
 	writel(RK3288_PGENB | RK3288_CSB, efuse->base + REG_EFUSE_CTRL);
 
-	clk_disable_unprepare(efuse->clk);
+	clk_bulk_disable_unprepare(efuse->num_clks, efuse->clks);
+
+	return 0;
+}
+
+static int rockchip_rk3288_efuse_secure_read(void *context,
+					     unsigned int offset,
+					     void *val, size_t bytes)
+{
+	struct rockchip_efuse_chip *efuse = context;
+	u8 *buf = val;
+	u32 wr_val;
+	int ret;
+
+	ret = clk_bulk_prepare_enable(efuse->num_clks, efuse->clks);
+	if (ret < 0) {
+		dev_err(efuse->dev, "failed to prepare/enable efuse clk\n");
+		return ret;
+	}
+
+	sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL,
+				 RK3288_LOAD | RK3288_PGENB);
+	udelay(1);
+	while (bytes--) {
+		wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) &
+			 (~(RK3288_A_MASK << RK3288_A_SHIFT));
+		sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val);
+		wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) |
+			 ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT);
+		sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val);
+		udelay(1);
+		wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) |
+			 RK3288_STROBE;
+		sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val);
+		udelay(1);
+		*buf++ = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_DOUT);
+		wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) &
+			 (~RK3288_STROBE);
+		sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val);
+		udelay(1);
+	}
+
+	/* Switch to standby mode */
+	sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL,
+				 RK3288_PGENB | RK3288_CSB);
+
+	clk_bulk_disable_unprepare(efuse->num_clks, efuse->clks);
 
 	return 0;
 }
@@ -101,7 +334,7 @@ static int rockchip_rk3328_efuse_read(void *context, unsigned int offset,
 	u8 *buf;
 	int ret, i = 0;
 
-	ret = clk_prepare_enable(efuse->clk);
+	ret = clk_bulk_prepare_enable(efuse->num_clks, efuse->clks);
 	if (ret < 0) {
 		dev_err(efuse->dev, "failed to prepare/enable efuse clk\n");
 		return ret;
@@ -142,11 +375,56 @@ static int rockchip_rk3328_efuse_read(void *context, unsigned int offset,
 err:
 	kfree(buf);
 nomem:
-	clk_disable_unprepare(efuse->clk);
+	clk_bulk_disable_unprepare(efuse->num_clks, efuse->clks);
 
 	return ret;
 }
 
+static int rockchip_rk3368_efuse_read(void *context, unsigned int offset,
+				      void *val, size_t bytes)
+{
+	struct rockchip_efuse_chip *efuse = context;
+	u8 *buf = val;
+	u32 wr_val;
+	int ret;
+
+	ret = clk_bulk_prepare_enable(efuse->num_clks, efuse->clks);
+	if (ret < 0) {
+		dev_err(efuse->dev, "failed to prepare/enable efuse clk\n");
+		return ret;
+	}
+
+	sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL,
+				 RK3288_LOAD | RK3288_PGENB);
+	udelay(1);
+	while (bytes--) {
+		wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) &
+			 (~(RK3288_A_MASK << RK3288_A_SHIFT));
+		sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val);
+		wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) |
+			 ((offset++ & RK3288_A_MASK) << RK3288_A_SHIFT);
+		sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val);
+		udelay(1);
+		wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) |
+			 RK3288_STROBE;
+		sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val);
+		udelay(1);
+		*buf++ = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_DOUT);
+		wr_val = sip_smc_secure_reg_read(efuse->phys + REG_EFUSE_CTRL) &
+			 (~RK3288_STROBE);
+		sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL, wr_val);
+		udelay(1);
+	}
+
+	/* Switch to standby mode */
+	sip_smc_secure_reg_write(efuse->phys + REG_EFUSE_CTRL,
+				 RK3288_PGENB | RK3288_CSB);
+
+	clk_bulk_disable_unprepare(efuse->num_clks, efuse->clks);
+
+	return 0;
+}
+
 static int rockchip_rk3399_efuse_read(void *context, unsigned int offset,
 				      void *val, size_t bytes)
 {
@@ -156,7 +434,7 @@ static int rockchip_rk3399_efuse_read(void *context, unsigned int offset,
 	u8 *buf;
 	int ret, i = 0;
 
-	ret = clk_prepare_enable(efuse->clk);
+	ret = clk_bulk_prepare_enable(efuse->num_clks, efuse->clks);
 	if (ret < 0) {
 		dev_err(efuse->dev, "failed to prepare/enable efuse clk\n");
 		return ret;
@@ -170,8 +448,8 @@ static int rockchip_rk3399_efuse_read(void *context, unsigned int offset,
 	buf = kzalloc(array3_size(addr_len, RK3399_NBYTES, sizeof(*buf)),
 		      GFP_KERNEL);
 	if (!buf) {
-		clk_disable_unprepare(efuse->clk);
-		return -ENOMEM;
+		ret = -ENOMEM;
+		goto disable_clks;
 	}
 
 	writel(RK3399_LOAD | RK3399_PGENB | RK3399_STROBSFTSEL | RK3399_RSB,
@@ -198,9 +476,10 @@ static int rockchip_rk3399_efuse_read(void *context, unsigned int offset,
 
 	kfree(buf);
 
-	clk_disable_unprepare(efuse->clk);
+disable_clks:
+	clk_bulk_disable_unprepare(efuse->num_clks, efuse->clks);
 
-	return 0;
+	return ret;
 }
 
 static struct nvmem_config econfig = {
@@ -212,6 +491,10 @@ static struct nvmem_config econfig = {
 
 static const struct of_device_id rockchip_efuse_match[] = {
 	/* deprecated but kept around for dts binding compatibility */
+	{
+		.compatible = "rockchip,rk1808-efuse",
+		.data = (void *)&rockchip_rk1808_efuse_read,
+	},
 	{
 		.compatible = "rockchip,rockchip-efuse",
 		.data = (void *)&rockchip_rk3288_efuse_read,
@@ -220,6 +503,10 @@ static const struct of_device_id rockchip_efuse_match[] = {
 		.compatible = "rockchip,rk3066a-efuse",
 		.data = (void *)&rockchip_rk3288_efuse_read,
 	},
+	{
+		.compatible = "rockchip,rk3128-efuse",
+		.data = (void *)&rockchip_rk3128_efuse_read,
+	},
 	{
 		.compatible = "rockchip,rk3188-efuse",
 		.data = (void *)&rockchip_rk3288_efuse_read,
@@ -233,13 +520,17 @@ static const struct of_device_id rockchip_efuse_match[] = {
 		.data = (void *)&rockchip_rk3288_efuse_read,
 	},
 	{
-		.compatible = "rockchip,rk3368-efuse",
-		.data = (void *)&rockchip_rk3288_efuse_read,
+		.compatible = "rockchip,rk3288-secure-efuse",
+		.data = (void *)&rockchip_rk3288_efuse_secure_read,
 	},
 	{
 		.compatible = "rockchip,rk3328-efuse",
 		.data = (void *)&rockchip_rk3328_efuse_read,
 	},
+	{
+		.compatible = "rockchip,rk3368-efuse",
+		.data = (void *)&rockchip_rk3368_efuse_read,
+	},
 	{
 		.compatible = "rockchip,rk3399-efuse",
 		.data = (void *)&rockchip_rk3399_efuse_read,
@@ -268,13 +559,16 @@ static int rockchip_efuse_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	efuse->phys = res->start;
 	efuse->base = devm_ioremap_resource(dev, res);
 	if (IS_ERR(efuse->base))
 		return PTR_ERR(efuse->base);
 
-	efuse->clk = devm_clk_get(dev, "pclk_efuse");
-	if (IS_ERR(efuse->clk))
-		return PTR_ERR(efuse->clk);
+	efuse->num_clks = devm_clk_bulk_get_all(dev, &efuse->clks);
+	if (efuse->num_clks < 1)
+		return -ENODEV;
+
+	mutex_init(&efuse->mutex);
 
 	efuse->dev = dev;
 	if (of_property_read_u32(dev->of_node, "rockchip,efuse-size",
@@ -296,6 +590,26 @@ static struct platform_driver rockchip_efuse_driver = {
 	},
 };
 
-module_platform_driver(rockchip_efuse_driver);
+static int __init rockchip_efuse_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&rockchip_efuse_driver);
+	if (ret) {
+		pr_err("failed to register efuse driver\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void __exit rockchip_efuse_exit(void)
+{
+	return platform_driver_unregister(&rockchip_efuse_driver);
+}
+
+subsys_initcall(rockchip_efuse_init);
+module_exit(rockchip_efuse_exit);
+
 MODULE_DESCRIPTION("rockchip_efuse driver");
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/nvmem/rockchip-otp.c b/drivers/nvmem/rockchip-otp.c
index 9f53bcce2..7ca6e3873 100644
--- a/drivers/nvmem/rockchip-otp.c
+++ b/drivers/nvmem/rockchip-otp.c
@@ -27,6 +27,7 @@
 #define OTPC_USER_CTRL			0x0100
 #define OTPC_USER_ADDR			0x0104
 #define OTPC_USER_ENABLE		0x0108
+#define OTPC_USER_QP			0x0120
 #define OTPC_USER_Q			0x0124
 #define OTPC_INT_STATUS			0x0304
 #define OTPC_SBPI_CMD0_OFFSET		0x1000
@@ -53,6 +54,84 @@
 #define SBPI_ENABLE_MASK		GENMASK(16, 16)
 
 #define OTPC_TIMEOUT			10000
+#define OTPC_TIMEOUT_PROG		100000
+#define RK3568_NBYTES			2
+
+#define RK3588_OTPC_AUTO_CTRL		0x04
+#define RK3588_OTPC_AUTO_EN		0x08
+#define RK3588_OTPC_INT_ST		0x84
+#define RK3588_OTPC_DOUT0		0x20
+#define RK3588_NO_SECURE_OFFSET		0x300
+#define RK3588_NBYTES			4
+#define RK3588_BURST_NUM		1
+#define RK3588_BURST_SHIFT		8
+#define RK3588_ADDR_SHIFT		16
+#define RK3588_AUTO_EN			BIT(0)
+#define RK3588_RD_DONE			BIT(1)
+
+#define RV1126_OTP_NVM_CEB		0x00
+#define RV1126_OTP_NVM_RSTB		0x04
+#define RV1126_OTP_NVM_ST		0x18
+#define RV1126_OTP_NVM_RADDR		0x1C
+#define RV1126_OTP_NVM_RSTART		0x20
+#define RV1126_OTP_NVM_RDATA		0x24
+#define RV1126_OTP_NVM_TRWH		0x28
+#define RV1126_OTP_READ_ST		0x30
+#define RV1126_OTP_NVM_PRADDR		0x34
+#define RV1126_OTP_NVM_PRLEN		0x38
+#define RV1126_OTP_NVM_PRDATA		0x3c
+#define RV1126_OTP_NVM_FAILTIME		0x40
+#define RV1126_OTP_NVM_PRSTART		0x44
+#define RV1126_OTP_NVM_PRSTATE		0x48
+
+/*
+ * +----------+------------------+--------------------------+
+ * | TYPE     | RANGE(byte)      | NOTE                     |
+ * +----------+------------------+--------------------------+
+ * | system   | 0x000 ~ 0x0ff    | system info, read only   |
+ * +----------+------------------+--------------------------+
+ * | oem      | 0x100 ~ 0x1ef    | for customized           |
+ * +----------+------------------+--------------------------+
+ * | reserved | 0x1f0 ~ 0x1f7    | future extension         |
+ * +----------+------------------+--------------------------+
+ * | wp       | 0x1f8 ~ 0x1ff    | write protection for oem |
+ * +----------+------------------+--------------------------+
+ *
+ * +-----+    +------------------+
+ * | wp  | -- | wp for oem range |
+ * +-----+    +------------------+
+ * | 1f8 |    | 0x100 ~ 0x11f    |
+ * +-----+    +------------------+
+ * | 1f9 |    | 0x120 ~ 0x13f    |
+ * +-----+    +------------------+
+ * | 1fa |    | 0x140 ~ 0x15f    |
+ * +-----+    +------------------+
+ * | 1fb |    | 0x160 ~ 0x17f    |
+ * +-----+    +------------------+
+ * | 1fc |    | 0x180 ~ 0x19f    |
+ * +-----+    +------------------+
+ * | 1fd |    | 0x1a0 ~ 0x1bf    |
+ * +-----+    +------------------+
+ * | 1fe |    | 0x1c0 ~ 0x1df    |
+ * +-----+    +------------------+
+ * | 1ff |    | 0x1e0 ~ 0x1ef    |
+ * +-----+    +------------------+
+ */
+#define RV1126_OTP_OEM_OFFSET		0x100
+#define RV1126_OTP_OEM_SIZE		0xf0
+#define RV1126_OTP_WP_OFFSET		0x1f8
+#define RV1126_OTP_WP_SIZE		0x8
+
+/* magic for enable otp write func */
+#define ROCKCHIP_OTP_WR_MAGIC		0x524F434B
+/* each bit mask 32 bits in OTP NVM */
+#define ROCKCHIP_OTP_WP_MASK_NBITS	64
+
+static unsigned int rockchip_otp_wr_magic;
+module_param(rockchip_otp_wr_magic, uint, 0644);
+MODULE_PARM_DESC(rockchip_otp_wr_magic, "magic for enable otp write func.");
+
+struct rockchip_data;
 
 struct rockchip_otp {
 	struct device *dev;
@@ -60,15 +139,19 @@ struct rockchip_otp {
 	struct clk_bulk_data	*clks;
 	int num_clks;
 	struct reset_control *rst;
-};
-
-/* list of required clocks */
-static const char * const rockchip_otp_clocks[] = {
-	"otp", "apb_pclk", "phy",
+	struct nvmem_config *config;
+	const struct rockchip_data *data;
+	struct mutex mutex;
+	DECLARE_BITMAP(wp_mask, ROCKCHIP_OTP_WP_MASK_NBITS);
 };
 
 struct rockchip_data {
 	int size;
+	const char * const *clocks;
+	int num_clks;
+	nvmem_reg_read_t reg_read;
+	nvmem_reg_write_t reg_write;
+	int (*init)(struct rockchip_otp *otp);
 };
 
 static int rockchip_otp_reset(struct rockchip_otp *otp)
@@ -92,7 +175,7 @@ static int rockchip_otp_reset(struct rockchip_otp *otp)
 	return 0;
 }
 
-static int rockchip_otp_wait_status(struct rockchip_otp *otp, u32 flag)
+static int px30_otp_wait_status(struct rockchip_otp *otp, u32 flag)
 {
 	u32 status = 0;
 	int ret;
@@ -108,7 +191,7 @@ static int rockchip_otp_wait_status(struct rockchip_otp *otp, u32 flag)
 	return 0;
 }
 
-static int rockchip_otp_ecc_enable(struct rockchip_otp *otp, bool enable)
+static int px30_otp_ecc_enable(struct rockchip_otp *otp, bool enable)
 {
 	int ret = 0;
 
@@ -125,15 +208,15 @@ static int rockchip_otp_ecc_enable(struct rockchip_otp *otp, bool enable)
 
 	writel(SBPI_ENABLE_MASK | SBPI_ENABLE, otp->base + OTPC_SBPI_CTRL);
 
-	ret = rockchip_otp_wait_status(otp, OTPC_SBPI_DONE);
+	ret = px30_otp_wait_status(otp, OTPC_SBPI_DONE);
 	if (ret < 0)
 		dev_err(otp->dev, "timeout during ecc_enable\n");
 
 	return ret;
 }
 
-static int rockchip_otp_read(void *context, unsigned int offset,
-			     void *val, size_t bytes)
+static int px30_otp_read(void *context, unsigned int offset, void *val,
+			 size_t bytes)
 {
 	struct rockchip_otp *otp = context;
 	u8 *buf = val;
@@ -151,7 +234,7 @@ static int rockchip_otp_read(void *context, unsigned int offset,
 		goto disable_clks;
 	}
 
-	ret = rockchip_otp_ecc_enable(otp, false);
+	ret = px30_otp_ecc_enable(otp, false);
 	if (ret < 0) {
 		dev_err(otp->dev, "rockchip_otp_ecc_enable err\n");
 		goto disable_clks;
@@ -164,7 +247,7 @@ static int rockchip_otp_read(void *context, unsigned int offset,
 		       otp->base + OTPC_USER_ADDR);
 		writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK,
 		       otp->base + OTPC_USER_ENABLE);
-		ret = rockchip_otp_wait_status(otp, OTPC_USER_DONE);
+		ret = px30_otp_wait_status(otp, OTPC_USER_DONE);
 		if (ret < 0) {
 			dev_err(otp->dev, "timeout during read setup\n");
 			goto read_end;
@@ -180,17 +263,366 @@ static int rockchip_otp_read(void *context, unsigned int offset,
 	return ret;
 }
 
+static int rk3568_otp_read(void *context, unsigned int offset, void *val,
+			   size_t bytes)
+{
+	struct rockchip_otp *otp = context;
+	unsigned int addr_start, addr_end, addr_offset, addr_len;
+	unsigned int otp_qp;
+	u32 out_value;
+	u8 *buf;
+	int ret = 0, i = 0;
+
+	addr_start = rounddown(offset, RK3568_NBYTES) / RK3568_NBYTES;
+	addr_end = roundup(offset + bytes, RK3568_NBYTES) / RK3568_NBYTES;
+	addr_offset = offset % RK3568_NBYTES;
+	addr_len = addr_end - addr_start;
+
+	buf = kzalloc(array3_size(addr_len, RK3568_NBYTES, sizeof(*buf)),
+		      GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = clk_bulk_prepare_enable(otp->num_clks, otp->clks);
+	if (ret < 0) {
+		dev_err(otp->dev, "failed to prepare/enable clks\n");
+		goto out;
+	}
+
+	ret = rockchip_otp_reset(otp);
+	if (ret) {
+		dev_err(otp->dev, "failed to reset otp phy\n");
+		goto disable_clks;
+	}
+
+	ret = px30_otp_ecc_enable(otp, true);
+	if (ret < 0) {
+		dev_err(otp->dev, "rockchip_otp_ecc_enable err\n");
+		goto disable_clks;
+	}
+
+	writel(OTPC_USE_USER | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
+	udelay(5);
+	while (addr_len--) {
+		writel(addr_start++ | OTPC_USER_ADDR_MASK,
+		       otp->base + OTPC_USER_ADDR);
+		writel(OTPC_USER_FSM_ENABLE | OTPC_USER_FSM_ENABLE_MASK,
+		       otp->base + OTPC_USER_ENABLE);
+		ret = px30_otp_wait_status(otp, OTPC_USER_DONE);
+		if (ret < 0) {
+			dev_err(otp->dev, "timeout during read setup\n");
+			goto read_end;
+		}
+		otp_qp = readl(otp->base + OTPC_USER_QP);
+		if (((otp_qp & 0xc0) == 0xc0) || (otp_qp & 0x20)) {
+			ret = -EIO;
+			dev_err(otp->dev, "ecc check error during read setup\n");
+			goto read_end;
+		}
+		out_value = readl(otp->base + OTPC_USER_Q);
+		memcpy(&buf[i], &out_value, RK3568_NBYTES);
+		i += RK3568_NBYTES;
+	}
+
+	memcpy(val, buf + addr_offset, bytes);
+
+read_end:
+	writel(0x0 | OTPC_USE_USER_MASK, otp->base + OTPC_USER_CTRL);
+disable_clks:
+	clk_bulk_disable_unprepare(otp->num_clks, otp->clks);
+out:
+	kfree(buf);
+
+	return ret;
+}
+
+static int rk3588_otp_wait_status(struct rockchip_otp *otp, u32 flag)
+{
+	u32 status = 0;
+	int ret;
+
+	ret = readl_poll_timeout_atomic(otp->base + RK3588_OTPC_INT_ST, status,
+					(status & flag), 1, OTPC_TIMEOUT);
+	if (ret)
+		return ret;
+
+	/* clean int status */
+	writel(flag, otp->base + RK3588_OTPC_INT_ST);
+
+	return 0;
+}
+
+static int rk3588_otp_read(void *context, unsigned int offset, void *val,
+			   size_t bytes)
+{
+	struct rockchip_otp *otp = context;
+	unsigned int addr_start, addr_end, addr_offset, addr_len;
+	int ret = 0, i = 0;
+	u32 out_value;
+	u8 *buf;
+
+	if (offset >= otp->data->size)
+		return -ENOMEM;
+	if (offset + bytes > otp->data->size)
+		bytes = otp->data->size - offset;
+
+	addr_start = rounddown(offset, RK3588_NBYTES) / RK3588_NBYTES;
+	addr_end = roundup(offset + bytes, RK3588_NBYTES) / RK3588_NBYTES;
+	addr_offset = offset % RK3588_NBYTES;
+	addr_len = addr_end - addr_start;
+	addr_start += RK3588_NO_SECURE_OFFSET;
+
+	buf = kzalloc(array3_size(addr_len, RK3588_NBYTES, sizeof(*buf)),
+		      GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = clk_bulk_prepare_enable(otp->num_clks, otp->clks);
+	if (ret < 0) {
+		dev_err(otp->dev, "failed to prepare/enable clks\n");
+		goto out;
+	}
+
+	while (addr_len--) {
+		writel((addr_start << RK3588_ADDR_SHIFT) |
+		       (RK3588_BURST_NUM << RK3588_BURST_SHIFT),
+		       otp->base + RK3588_OTPC_AUTO_CTRL);
+		writel(RK3588_AUTO_EN, otp->base + RK3588_OTPC_AUTO_EN);
+		ret = rk3588_otp_wait_status(otp, RK3588_RD_DONE);
+		if (ret < 0) {
+			dev_err(otp->dev, "timeout during read setup\n");
+			goto read_end;
+		}
+
+		out_value = readl(otp->base + RK3588_OTPC_DOUT0);
+		memcpy(&buf[i], &out_value, RK3588_NBYTES);
+		i += RK3588_NBYTES;
+		addr_start++;
+	}
+
+	memcpy(val, buf + addr_offset, bytes);
+
+read_end:
+	clk_bulk_disable_unprepare(otp->num_clks, otp->clks);
+out:
+	kfree(buf);
+
+	return ret;
+}
+
+static int rv1126_otp_init(struct rockchip_otp *otp)
+{
+	u32 status = 0;
+	int ret;
+
+	writel(0x0, otp->base + RV1126_OTP_NVM_CEB);
+	ret = readl_poll_timeout_atomic(otp->base + RV1126_OTP_NVM_ST, status,
+					status & 0x1, 1, OTPC_TIMEOUT);
+	if (ret < 0) {
+		dev_err(otp->dev, "timeout during set ceb\n");
+		return ret;
+	}
+
+	writel(0x1, otp->base + RV1126_OTP_NVM_RSTB);
+	ret = readl_poll_timeout_atomic(otp->base + RV1126_OTP_NVM_ST, status,
+					status & 0x4, 1, OTPC_TIMEOUT);
+	if (ret < 0) {
+		dev_err(otp->dev, "timeout during set rstb\n");
+		return ret;
+	}
+
+	otp->config->read_only = false;
+
+	return 0;
+}
+
+static int rv1126_otp_read(void *context, unsigned int offset, void *val,
+			   size_t bytes)
+{
+	struct rockchip_otp *otp = context;
+	u32 status = 0;
+	u8 *buf = val;
+	int ret = 0;
+
+	while (bytes--) {
+		writel(offset++, otp->base + RV1126_OTP_NVM_RADDR);
+		writel(0x1, otp->base + RV1126_OTP_NVM_RSTART);
+		ret = readl_poll_timeout_atomic(otp->base + RV1126_OTP_READ_ST,
+						status, status == 0, 1,
+						OTPC_TIMEOUT);
+		if (ret < 0) {
+			dev_err(otp->dev, "timeout during read setup\n");
+			return ret;
+		}
+
+		*buf++ = readb(otp->base + RV1126_OTP_NVM_RDATA);
+	}
+
+	return 0;
+}
+
+static int rv1126_otp_prog(struct rockchip_otp *otp, u32 bit_offset, u32 data,
+			   u32 bit_len)
+{
+	u32 status = 0;
+	int ret = 0;
+
+	if (!data)
+		return 0;
+
+	writel(bit_offset, otp->base + RV1126_OTP_NVM_PRADDR);
+	writel(bit_len - 1, otp->base + RV1126_OTP_NVM_PRLEN);
+	writel(data, otp->base + RV1126_OTP_NVM_PRDATA);
+	writel(1, otp->base + RV1126_OTP_NVM_PRSTART);
+	/* Wait max 100 ms */
+	ret = readl_poll_timeout_atomic(otp->base + RV1126_OTP_NVM_PRSTATE,
+					status, status == 0, 1,
+					OTPC_TIMEOUT_PROG);
+	if (ret < 0)
+		dev_err(otp->dev, "timeout during prog\n");
+
+	return ret;
+}
+
+static int rv1126_otp_write(void *context, unsigned int offset, void *val,
+			    size_t bytes)
+{
+	struct rockchip_otp *otp = context;
+	u8 *buf = val;
+	u8 val_r, val_w;
+	int ret = 0;
+
+	while (bytes--) {
+		ret = rv1126_otp_read(context, offset, &val_r, 1);
+		if (ret)
+			return ret;
+		val_w = *buf & (~val_r);
+		ret = rv1126_otp_prog(otp, offset * 8, val_w, 8);
+		if (ret)
+			return ret;
+		buf++;
+		offset++;
+	}
+
+	return 0;
+}
+
+static int rv1126_otp_wp(void *context, unsigned int offset, size_t bytes)
+{
+	struct rockchip_otp *otp = context;
+
+	bitmap_set(otp->wp_mask, (offset - RV1126_OTP_OEM_OFFSET) / 4, bytes / 4);
+
+	return rv1126_otp_write(context, RV1126_OTP_WP_OFFSET, otp->wp_mask,
+				RV1126_OTP_WP_SIZE);
+}
+
+static int rv1126_otp_oem_write(void *context, unsigned int offset, void *val,
+				size_t bytes)
+{
+	int ret = 0;
+
+	if (offset < RV1126_OTP_OEM_OFFSET ||
+	    offset > (RV1126_OTP_OEM_OFFSET + RV1126_OTP_OEM_SIZE - 1) ||
+	    bytes > RV1126_OTP_OEM_SIZE ||
+	    (offset + bytes) > (RV1126_OTP_OEM_OFFSET + RV1126_OTP_OEM_SIZE))
+		return -EINVAL;
+
+	if (!IS_ALIGNED(offset, 4) || !IS_ALIGNED(bytes, 4))
+		return -EINVAL;
+
+	ret = rv1126_otp_write(context, offset, val, bytes);
+	if (!ret)
+		ret = rv1126_otp_wp(context, offset, bytes);
+
+	return ret;
+}
+
+static int rockchip_otp_read(void *context, unsigned int offset, void *val,
+			     size_t bytes)
+{
+	struct rockchip_otp *otp = context;
+	int ret = -EINVAL;
+
+	mutex_lock(&otp->mutex);
+	if (otp->data && otp->data->reg_read)
+		ret = otp->data->reg_read(context, offset, val, bytes);
+	mutex_unlock(&otp->mutex);
+
+	return ret;
+}
+
+static int rockchip_otp_write(void *context, unsigned int offset, void *val,
+			      size_t bytes)
+{
+	struct rockchip_otp *otp = context;
+	int ret = -EINVAL;
+
+	mutex_lock(&otp->mutex);
+	if (rockchip_otp_wr_magic == ROCKCHIP_OTP_WR_MAGIC &&
+	    otp->data && otp->data->reg_write) {
+		ret = otp->data->reg_write(context, offset, val, bytes);
+		rockchip_otp_wr_magic = 0;
+	}
+	mutex_unlock(&otp->mutex);
+
+	return ret;
+}
+
 static struct nvmem_config otp_config = {
 	.name = "rockchip-otp",
 	.owner = THIS_MODULE,
 	.read_only = true,
+	.reg_read = rockchip_otp_read,
+	.reg_write = rockchip_otp_write,
 	.stride = 1,
 	.word_size = 1,
-	.reg_read = rockchip_otp_read,
+};
+
+static const char * const px30_otp_clocks[] = {
+	"otp", "apb_pclk", "phy",
 };
 
 static const struct rockchip_data px30_data = {
 	.size = 0x40,
+	.clocks = px30_otp_clocks,
+	.num_clks = ARRAY_SIZE(px30_otp_clocks),
+	.reg_read = px30_otp_read,
+};
+
+static const char * const rk3568_otp_clocks[] = {
+	"usr", "sbpi", "apb", "phy",
+};
+
+static const struct rockchip_data rk3568_data = {
+	.size = 0x80,
+	.clocks = rk3568_otp_clocks,
+	.num_clks = ARRAY_SIZE(rk3568_otp_clocks),
+	.reg_read = rk3568_otp_read,
+};
+
+static const char * const rk3588_otp_clocks[] = {
+	"otpc", "apb", "arb", "phy",
+};
+
+static const struct rockchip_data rk3588_data = {
+	.size = 0x400,
+	.clocks = rk3588_otp_clocks,
+	.num_clks = ARRAY_SIZE(rk3588_otp_clocks),
+	.reg_read = rk3588_otp_read,
+};
+
+static const char * const rv1126_otp_clocks[] = {
+	"otp", "apb_pclk",
+};
+
+static const struct rockchip_data rv1126_data = {
+	.size = 0x200,
+	.clocks = rv1126_otp_clocks,
+	.num_clks = ARRAY_SIZE(rv1126_otp_clocks),
+	.init = rv1126_otp_init,
+	.reg_read = rv1126_otp_read,
+	.reg_write = rv1126_otp_oem_write,
 };
 
 static const struct of_device_id rockchip_otp_match[] = {
@@ -202,6 +634,18 @@ static const struct of_device_id rockchip_otp_match[] = {
 		.compatible = "rockchip,rk3308-otp",
 		.data = (void *)&px30_data,
 	},
+	{
+		.compatible = "rockchip,rk3568-otp",
+		.data = (void *)&rk3568_data,
+	},
+	{
+		.compatible = "rockchip,rk3588-otp",
+		.data = (void *)&rk3588_data,
+	},
+	{
+		.compatible = "rockchip,rv1126-otp",
+		.data = (void *)&rv1126_data,
+	},
 	{ /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, rockchip_otp_match);
@@ -225,32 +669,42 @@ static int rockchip_otp_probe(struct platform_device *pdev)
 	if (!otp)
 		return -ENOMEM;
 
+	mutex_init(&otp->mutex);
+	otp->data = data;
 	otp->dev = dev;
 	otp->base = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(otp->base))
 		return PTR_ERR(otp->base);
 
-	otp->num_clks = ARRAY_SIZE(rockchip_otp_clocks);
+	otp->num_clks = data->num_clks;
 	otp->clks = devm_kcalloc(dev, otp->num_clks,
 				     sizeof(*otp->clks), GFP_KERNEL);
 	if (!otp->clks)
 		return -ENOMEM;
 
 	for (i = 0; i < otp->num_clks; ++i)
-		otp->clks[i].id = rockchip_otp_clocks[i];
+		otp->clks[i].id = data->clocks[i];
 
 	ret = devm_clk_bulk_get(dev, otp->num_clks, otp->clks);
 	if (ret)
 		return ret;
 
-	otp->rst = devm_reset_control_get(dev, "phy");
+	otp->rst = devm_reset_control_array_get_optional_exclusive(dev);
 	if (IS_ERR(otp->rst))
 		return PTR_ERR(otp->rst);
 
-	otp_config.size = data->size;
-	otp_config.priv = otp;
-	otp_config.dev = dev;
-	nvmem = devm_nvmem_register(dev, &otp_config);
+	otp->config = &otp_config;
+	otp->config->size = data->size;
+	otp->config->priv = otp;
+	otp->config->dev = dev;
+
+	if (data->init) {
+		ret = data->init(otp);
+		if (ret)
+			return ret;
+	}
+
+	nvmem = devm_nvmem_register(dev, otp->config);
 
 	return PTR_ERR_OR_ZERO(nvmem);
 }
@@ -263,6 +717,26 @@ static struct platform_driver rockchip_otp_driver = {
 	},
 };
 
-module_platform_driver(rockchip_otp_driver);
+static int __init rockchip_otp_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&rockchip_otp_driver);
+	if (ret) {
+		pr_err("failed to register otp driver\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void __exit rockchip_otp_exit(void)
+{
+	return platform_driver_unregister(&rockchip_otp_driver);
+}
+
+subsys_initcall(rockchip_otp_init);
+module_exit(rockchip_otp_exit);
+
 MODULE_DESCRIPTION("Rockchip OTP driver");
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/opp/debugfs.c b/drivers/opp/debugfs.c
index 596c185b5..bdc776b7b 100644
--- a/drivers/opp/debugfs.c
+++ b/drivers/opp/debugfs.c
@@ -239,11 +239,60 @@ void opp_debug_unregister(struct opp_device *opp_dev,
 	opp_dev->dentry = NULL;
 }
 
+static int opp_summary_show(struct seq_file *s, void *data)
+{
+	struct list_head *lists = (struct list_head *)s->private;
+	struct opp_table *opp_table;
+	struct dev_pm_opp *opp;
+
+	mutex_lock(&opp_table_lock);
+
+	seq_puts(s, " device                rate(Hz)    target(uV)    min(uV)    max(uV)\n");
+	seq_puts(s, "-------------------------------------------------------------------\n");
+
+	list_for_each_entry(opp_table, lists, node) {
+		seq_printf(s, " %s\n", opp_table->dentry_name);
+		mutex_lock(&opp_table->lock);
+		list_for_each_entry(opp, &opp_table->opp_list, node) {
+			seq_printf(s, "%31lu %12lu %11lu %11lu\n",
+				   opp->rate,
+				   opp->supplies[0].u_volt,
+				   opp->supplies[0].u_volt_min,
+				   opp->supplies[0].u_volt_max);
+			if (opp_table->regulator_count > 1)
+				seq_printf(s, "%44lu %11lu %11lu\n",
+					   opp->supplies[1].u_volt,
+					   opp->supplies[1].u_volt_min,
+					   opp->supplies[1].u_volt_max);
+		}
+		mutex_unlock(&opp_table->lock);
+	}
+
+	mutex_unlock(&opp_table_lock);
+
+	return 0;
+}
+
+static int opp_summary_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, opp_summary_show, inode->i_private);
+}
+
+static const struct file_operations opp_summary_fops = {
+	.open		= opp_summary_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
 static int __init opp_debug_init(void)
 {
 	/* Create /sys/kernel/debug/opp directory */
 	rootdir = debugfs_create_dir("opp", NULL);
 
+	debugfs_create_file("opp_summary", 0444, rootdir, &opp_tables,
+			    &opp_summary_fops);
+
 	return 0;
 }
 core_initcall(opp_debug_init);
diff --git a/drivers/opp/of.c b/drivers/opp/of.c
index 3d7adc0de..b163f5d13 100644
--- a/drivers/opp/of.c
+++ b/drivers/opp/of.c
@@ -1328,7 +1328,7 @@ int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus)
 		goto failed;
 	}
 
-	ret = em_dev_register_perf_domain(dev, nr_opp, &em_cb, cpus);
+	ret = em_dev_register_perf_domain(dev, nr_opp, &em_cb, cpus, true);
 	if (ret)
 		goto failed;
 
diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile
index 04c6edc28..d023ff1a1 100644
--- a/drivers/pci/controller/Makefile
+++ b/drivers/pci/controller/Makefile
@@ -23,9 +23,10 @@ obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o
 obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-iproc-bcma.o
 obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o
 obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o
-obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o
-obj-$(CONFIG_PCIE_ROCKCHIP_EP) += pcie-rockchip-ep.o
-obj-$(CONFIG_PCIE_ROCKCHIP_HOST) += pcie-rockchip-host.o
+obj-$(CONFIG_PCIE_ROCKCHIP_EP) += pcie-rockchip-ep.o pcie-rockchip.o
+pcierockchiphost-y := pcie-rockchip-host.o pcie-rockchip.o
+obj-$(CONFIG_PCIE_ROCKCHIP_HOST) += pcierockchiphost.o
+
 obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o
 obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o
 obj-$(CONFIG_VMD) += vmd.o
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 28945351d..2df528706 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -496,6 +496,7 @@ int dw_pcie_link_up(struct dw_pcie *pci)
 	return ((val & PCIE_PORT_DEBUG1_LINK_UP) &&
 		(!(val & PCIE_PORT_DEBUG1_LINK_IN_TRAINING)));
 }
+EXPORT_SYMBOL_GPL(dw_pcie_link_up);
 
 void dw_pcie_upconfig_setup(struct dw_pcie *pci)
 {
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 9d2f511f1..08cd48487 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -42,6 +42,7 @@
 #define PORT_AFR_L1_ENTRANCE_LAT_MASK	GENMASK(29, 27)
 
 #define PCIE_PORT_LINK_CONTROL		0x710
+#define PORT_LINK_LPBK_ENABLE		BIT(2)
 #define PORT_LINK_DLL_LINK_EN		BIT(5)
 #define PORT_LINK_FAST_LINK_MODE	BIT(7)
 #define PORT_LINK_MODE_MASK		GENMASK(21, 16)
@@ -266,7 +267,6 @@ struct dw_pcie {
 	/* Used when iatu_unroll_enabled is true */
 	void __iomem		*atu_base;
 	u32			num_viewport;
-	u8			iatu_unroll_enabled;
 	struct pcie_port	pp;
 	struct dw_pcie_ep	ep;
 	const struct dw_pcie_ops *ops;
@@ -274,6 +274,8 @@ struct dw_pcie {
 	int			num_lanes;
 	int			link_gen;
 	u8			n_fts[2];
+	bool			iatu_unroll_enabled: 1;
+	bool			io_cfg_atu_shared: 1;
 };
 
 #define to_dw_pcie_from_pp(port) container_of((port), struct dw_pcie, pp)
diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c
index 0d6df73bb..2645157f3 100644
--- a/drivers/pci/controller/pcie-rockchip-host.c
+++ b/drivers/pci/controller/pcie-rockchip-host.c
@@ -38,6 +38,37 @@
 
 #include "../pci.h"
 #include "pcie-rockchip.h"
+#include <linux/rockchip-pcie-dma.h>
+
+static void rk_pcie_start_dma_rk3399(struct dma_trx_obj *obj)
+{
+	struct rockchip_pcie *rockchip = dev_get_drvdata(obj->dev);
+	struct dma_table *tbl = obj->cur;
+	int chn = tbl->chn;
+
+	rockchip_pcie_write(rockchip, (u32)(tbl->phys_descs & 0xffffffff),
+			    PCIE_APB_CORE_UDMA_BASE + 0x14 * chn + 0x04);
+	rockchip_pcie_write(rockchip, (u32)(tbl->phys_descs >> 32),
+			    PCIE_APB_CORE_UDMA_BASE + 0x14 * chn + 0x08);
+	rockchip_pcie_write(rockchip, BIT(0) | (tbl->dir << 1),
+			    PCIE_APB_CORE_UDMA_BASE + 0x14 * chn + 0x00);
+}
+
+static void rk_pcie_config_dma_rk3399(struct dma_table *table)
+{
+	u32 *desc = table->descs;
+
+	*(desc + 0) = (u32)(table->local & 0xffffffff);
+	*(desc + 1) = (u32)(table->local >> 32);
+	*(desc + 2) = (u32)(table->bus & 0xffffffff);
+	*(desc + 3) = (u32)(table->bus >> 32);
+	*(desc + 4) = 0;
+	*(desc + 5) = 0;
+	*(desc + 6) = table->buf_size;
+	*(desc + 7) = 0;
+	*(desc + 8) = 0;
+	*(desc + 6) |= 1 << 24;
+}
 
 static void rockchip_pcie_enable_bw_int(struct rockchip_pcie *rockchip)
 {
@@ -159,6 +190,9 @@ static int rockchip_pcie_rd_other_conf(struct rockchip_pcie *rockchip,
 {
 	u32 busdev;
 
+	if (rockchip->in_remove)
+		return PCIBIOS_SUCCESSFUL;
+
 	busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn),
 				PCI_FUNC(devfn), where);
 
@@ -193,6 +227,9 @@ static int rockchip_pcie_wr_other_conf(struct rockchip_pcie *rockchip,
 {
 	u32 busdev;
 
+	if (rockchip->in_remove)
+		return PCIBIOS_SUCCESSFUL;
+
 	busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn),
 				PCI_FUNC(devfn), where);
 	if (!IS_ALIGNED(busdev, size))
@@ -299,6 +336,7 @@ static int rockchip_pcie_host_init_port(struct rockchip_pcie *rockchip)
 	struct device *dev = rockchip->dev;
 	int err, i = MAX_LANE_NUM;
 	u32 status;
+	int timeouts = 500;
 
 	gpiod_set_value_cansleep(rockchip->ep_gpio, 0);
 
@@ -330,15 +368,26 @@ static int rockchip_pcie_host_init_port(struct rockchip_pcie *rockchip)
 
 	gpiod_set_value_cansleep(rockchip->ep_gpio, 1);
 
+	if (rockchip->wait_ep)
+		timeouts = 10000;
+
 	/* 500ms timeout value should be enough for Gen1/2 training */
 	err = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_BASIC_STATUS1,
 				 status, PCIE_LINK_UP(status), 20,
-				 500 * USEC_PER_MSEC);
+				 timeouts * USEC_PER_MSEC);
 	if (err) {
 		dev_err(dev, "PCIe link training gen1 timeout!\n");
 		goto err_power_off_phy;
 	}
 
+	err = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_DEBUG_OUT_0,
+				 status, PCIE_LINK_IS_L0(status), 20,
+				 timeouts * USEC_PER_MSEC);
+	if (err) {
+		dev_err(dev, "LTSSM is not L0!\n");
+		return -ETIMEDOUT;
+	}
+
 	if (rockchip->link_gen == 2) {
 		/*
 		 * Enable retrain for gen2. This should be configured only after
@@ -370,6 +419,11 @@ static int rockchip_pcie_host_init_port(struct rockchip_pcie *rockchip)
 		}
 	}
 
+	/* disable ltssm */
+	if (rockchip->dma_trx_enabled)
+		rockchip_pcie_write(rockchip, PCIE_CLIENT_LINK_TRAIN_DISABLE,
+				    PCIE_CLIENT_CONFIG);
+
 	rockchip_pcie_write(rockchip, ROCKCHIP_VENDOR_ID,
 			    PCIE_CORE_CONFIG_VENDOR);
 	rockchip_pcie_write(rockchip,
@@ -403,6 +457,33 @@ static int rockchip_pcie_host_init_port(struct rockchip_pcie *rockchip)
 	return err;
 }
 
+static inline void
+rockchip_pcie_handle_dma_interrupt(struct rockchip_pcie *rockchip)
+{
+	u32 dma_status;
+	struct dma_trx_obj *obj = rockchip->dma_obj;
+
+	dma_status = rockchip_pcie_read(rockchip,
+			PCIE_APB_CORE_UDMA_BASE + PCIE_UDMA_INT_REG);
+
+	/* Core: clear dma interrupt */
+	rockchip_pcie_write(rockchip, dma_status,
+			PCIE_APB_CORE_UDMA_BASE + PCIE_UDMA_INT_REG);
+
+	WARN_ONCE(!(dma_status & 0x3), "dma_status 0x%x\n", dma_status);
+
+	if (dma_status & (1 << 0)) {
+		obj->irq_num++;
+		obj->dma_free = true;
+	}
+
+	if (list_empty(&obj->tbl_list)) {
+		if (obj->dma_free &&
+			obj->loop_count >= obj->loop_count_threshold)
+			complete(&obj->done);
+	}
+}
+
 static irqreturn_t rockchip_pcie_subsys_irq_handler(int irq, void *arg)
 {
 	struct rockchip_pcie *rockchip = arg;
@@ -411,9 +492,10 @@ static irqreturn_t rockchip_pcie_subsys_irq_handler(int irq, void *arg)
 	u32 sub_reg;
 
 	reg = rockchip_pcie_read(rockchip, PCIE_CLIENT_INT_STATUS);
+	sub_reg = rockchip_pcie_read(rockchip, PCIE_CORE_INT_STATUS);
+	dev_dbg(dev, "reg = 0x%x, sub_reg = 0x%x\n", reg, sub_reg);
 	if (reg & PCIE_CLIENT_INT_LOCAL) {
 		dev_dbg(dev, "local interrupt received\n");
-		sub_reg = rockchip_pcie_read(rockchip, PCIE_CORE_INT_STATUS);
 		if (sub_reg & PCIE_CORE_INT_PRFPE)
 			dev_dbg(dev, "parity error detected while reading from the PNP receive FIFO RAM\n");
 
@@ -463,6 +545,12 @@ static irqreturn_t rockchip_pcie_subsys_irq_handler(int irq, void *arg)
 		rockchip_pcie_clr_bw_int(rockchip);
 	}
 
+	if (reg & PCIE_CLIENT_INT_UDMA) {
+		rockchip_pcie_write(rockchip, sub_reg, PCIE_CLIENT_INT_STATUS);
+		rockchip_pcie_write(rockchip, reg, PCIE_CLIENT_INT_STATUS);
+		rockchip_pcie_handle_dma_interrupt(rockchip);
+	}
+
 	rockchip_pcie_write(rockchip, reg & PCIE_CLIENT_INT_LOCAL,
 			    PCIE_CLIENT_INT_STATUS);
 
@@ -673,6 +761,8 @@ static void rockchip_pcie_enable_interrupts(struct rockchip_pcie *rockchip)
 			    PCIE_CORE_INT_MASK);
 
 	rockchip_pcie_enable_bw_int(rockchip);
+	rockchip_pcie_write(rockchip, PCIE_UDMA_INT_ENABLE_MASK,
+			PCIE_APB_CORE_UDMA_BASE + PCIE_UDMA_INT_ENABLE_REG);
 }
 
 static int rockchip_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
@@ -811,6 +901,12 @@ static int rockchip_pcie_cfg_atu(struct rockchip_pcie *rockchip)
 		}
 	}
 
+	/* Workaround for PCIe DMA transfer */
+	if (rockchip->dma_trx_enabled) {
+		rockchip_pcie_prog_ob_atu(rockchip, 1, AXI_WRAPPER_MEM_WRITE,
+				32 - 1, rockchip->mem_reserve_start, 0x0);
+	}
+
 	err = rockchip_pcie_prog_ib_atu(rockchip, 2, 32 - 1, 0x0, 0);
 	if (err) {
 		dev_err(dev, "program RC mem inbound ATU failed\n");
@@ -846,6 +942,9 @@ static int rockchip_pcie_cfg_atu(struct rockchip_pcie *rockchip)
 				  20 - 1, 0, 0);
 
 	rockchip->msg_bus_addr += ((reg_no + offset) << 20);
+	rockchip->msg_region = devm_ioremap(dev, rockchip->msg_bus_addr, SZ_1M);
+	if (!rockchip->msg_region)
+		err = -ENOMEM;
 	return err;
 }
 
@@ -854,6 +953,10 @@ static int rockchip_pcie_wait_l2(struct rockchip_pcie *rockchip)
 	u32 value;
 	int err;
 
+	/* Don't enter L2 state when no ep connected */
+	if (rockchip->dma_trx_enabled == 1)
+		return 0;
+
 	/* send PME_TURN_OFF message */
 	writel(0x0, rockchip->msg_region + PCIE_RC_SEND_PME_OFF);
 
@@ -869,7 +972,7 @@ static int rockchip_pcie_wait_l2(struct rockchip_pcie *rockchip)
 	return 0;
 }
 
-static int __maybe_unused rockchip_pcie_suspend_noirq(struct device *dev)
+static int rockchip_pcie_suspend_for_user(struct device *dev)
 {
 	struct rockchip_pcie *rockchip = dev_get_drvdata(dev);
 	int ret;
@@ -885,8 +988,43 @@ static int __maybe_unused rockchip_pcie_suspend_noirq(struct device *dev)
 		return ret;
 	}
 
+	/* disable ltssm */
+	rockchip_pcie_write(rockchip, PCIE_CLIENT_LINK_TRAIN_DISABLE,
+			    PCIE_CLIENT_CONFIG);
+
 	rockchip_pcie_deinit_phys(rockchip);
 
+	return ret;
+}
+
+static int rockchip_pcie_resume_for_user(struct device *dev)
+{
+	struct rockchip_pcie *rockchip = dev_get_drvdata(dev);
+	int err;
+
+	err = rockchip_pcie_host_init_port(rockchip);
+	if (err)
+		return err;
+
+	err = rockchip_pcie_cfg_atu(rockchip);
+	if (err)
+		return err;
+
+	/* Need this to enter L1 again */
+	rockchip_pcie_update_txcredit_mui(rockchip);
+	rockchip_pcie_enable_interrupts(rockchip);
+
+	return 0;
+}
+
+static int __maybe_unused rockchip_pcie_suspend_noirq(struct device *dev)
+{
+	struct rockchip_pcie *rockchip = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (!rockchip->dma_trx_enabled)
+		ret = rockchip_pcie_suspend_for_user(dev);
+
 	rockchip_pcie_disable_clocks(rockchip);
 
 	regulator_disable(rockchip->vpcie0v9);
@@ -909,29 +1047,105 @@ static int __maybe_unused rockchip_pcie_resume_noirq(struct device *dev)
 	if (err)
 		goto err_disable_0v9;
 
-	err = rockchip_pcie_host_init_port(rockchip);
+	if (!rockchip->dma_trx_enabled)
+		err = rockchip_pcie_resume_for_user(dev);
 	if (err)
-		goto err_pcie_resume;
-
-	err = rockchip_pcie_cfg_atu(rockchip);
-	if (err)
-		goto err_err_deinit_port;
-
-	/* Need this to enter L1 again */
-	rockchip_pcie_update_txcredit_mui(rockchip);
-	rockchip_pcie_enable_interrupts(rockchip);
+		goto err_disable_clocks;
 
 	return 0;
 
-err_err_deinit_port:
-	rockchip_pcie_deinit_phys(rockchip);
-err_pcie_resume:
+err_disable_clocks:
 	rockchip_pcie_disable_clocks(rockchip);
 err_disable_0v9:
 	regulator_disable(rockchip->vpcie0v9);
+
 	return err;
 }
 
+static int rockchip_pcie_really_probe(struct rockchip_pcie *rockchip)
+{
+	int err;
+
+	err = rockchip_pcie_host_init_port(rockchip);
+	if (err)
+		return err;
+
+	err = rockchip_pcie_setup_irq(rockchip);
+	if (err)
+		return err;
+
+	rockchip_pcie_enable_interrupts(rockchip);
+
+	err = rockchip_pcie_cfg_atu(rockchip);
+	if (err)
+		return err;
+
+	rockchip->bridge->sysdata = rockchip;
+	rockchip->bridge->ops = &rockchip_pcie_ops;
+
+	return pci_host_probe(rockchip->bridge);
+}
+
+static ssize_t pcie_deferred_store(struct device *dev,
+			   struct device_attribute *attr,
+			   const char *buf, size_t size)
+{
+	u32 val = 0;
+	int err;
+	struct rockchip_pcie *rockchip = dev_get_drvdata(dev);
+
+	err = kstrtou32(buf, 10, &val);
+	if (err)
+		return err;
+
+	if (val) {
+		rockchip->wait_ep = 1;
+		err = rockchip_pcie_really_probe(rockchip);
+		if (err)
+			return -EINVAL;
+	}
+
+	return size;
+}
+
+static ssize_t pcie_reset_ep_store(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t size)
+{
+	u32 val = 0;
+	int err;
+	struct rockchip_pcie *rockchip = dev_get_drvdata(dev);
+	struct dma_trx_obj *obj = rockchip->dma_obj;
+
+	dev_info(dev, "loop_cout = %d\n", obj->loop_count);
+
+	err = kstrtou32(buf, 10, &val);
+	if (err)
+		return err;
+
+	if (val == PCIE_USER_UNLINK)
+		rockchip_pcie_suspend_for_user(rockchip->dev);
+	else if (val == PCIE_USER_RELINK)
+		rockchip_pcie_resume_for_user(rockchip->dev);
+	else
+		return -EINVAL;
+
+	return size;
+}
+
+static DEVICE_ATTR_WO(pcie_deferred);
+static DEVICE_ATTR_WO(pcie_reset_ep);
+
+static struct attribute *pcie_attrs[] = {
+	&dev_attr_pcie_deferred.attr,
+	&dev_attr_pcie_reset_ep.attr,
+	NULL
+};
+
+static const struct attribute_group pcie_attr_group = {
+	.attrs = pcie_attrs,
+};
+
 static int rockchip_pcie_probe(struct platform_device *pdev)
 {
 	struct rockchip_pcie *rockchip;
@@ -948,6 +1162,8 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
 
 	rockchip = pci_host_bridge_priv(bridge);
 
+	rockchip->bridge = bridge;
+
 	platform_set_drvdata(pdev, rockchip);
 	rockchip->dev = dev;
 	rockchip->is_rc = true;
@@ -966,43 +1182,47 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
 		goto err_set_vpcie;
 	}
 
-	err = rockchip_pcie_host_init_port(rockchip);
-	if (err)
-		goto err_vpcie;
-
 	err = rockchip_pcie_init_irq_domain(rockchip);
 	if (err < 0)
-		goto err_deinit_port;
-
-	err = rockchip_pcie_cfg_atu(rockchip);
-	if (err)
-		goto err_remove_irq_domain;
+		goto err_vpcie;
 
-	rockchip->msg_region = devm_ioremap(dev, rockchip->msg_bus_addr, SZ_1M);
-	if (!rockchip->msg_region) {
-		err = -ENOMEM;
-		goto err_remove_irq_domain;
+	if (rockchip->deferred) {
+		err = sysfs_create_group(&pdev->dev.kobj, &pcie_attr_group);
+		if (err) {
+			dev_err(&pdev->dev, "SysFS group creation failed\n");
+			goto err_remove_irq_domain;
+		}
+	} else {
+		err = rockchip_pcie_really_probe(rockchip);
+		if (err) {
+			dev_err(&pdev->dev, "deferred probe failed\n");
+			goto err_deinit_port;
+		}
 	}
 
-	bridge->sysdata = rockchip;
-	bridge->ops = &rockchip_pcie_ops;
+	if (rockchip->dma_trx_enabled == 0)
+		return 0;
 
-	err = rockchip_pcie_setup_irq(rockchip);
-	if (err)
-		goto err_remove_irq_domain;
-
-	rockchip_pcie_enable_interrupts(rockchip);
+	rockchip->dma_obj = rk_pcie_dma_obj_probe(dev);
+	if (IS_ERR(rockchip->dma_obj)) {
+		dev_err(dev, "failed to prepare dma object\n");
+		err = -EINVAL;
+		goto err_deinit_port;
+	}
 
-	err = pci_host_probe(bridge);
-	if (err < 0)
-		goto err_remove_irq_domain;
+	if (rockchip->dma_obj) {
+		rockchip->dma_obj->start_dma_func = rk_pcie_start_dma_rk3399;
+		rockchip->dma_obj->config_dma_func = rk_pcie_config_dma_rk3399;
+	}
 
 	return 0;
 
-err_remove_irq_domain:
-	irq_domain_remove(rockchip->irq_domain);
 err_deinit_port:
 	rockchip_pcie_deinit_phys(rockchip);
+	if (rockchip->deferred)
+		sysfs_remove_group(&pdev->dev.kobj, &pcie_attr_group);
+err_remove_irq_domain:
+	irq_domain_remove(rockchip->irq_domain);
 err_vpcie:
 	if (!IS_ERR(rockchip->vpcie12v))
 		regulator_disable(rockchip->vpcie12v);
@@ -1019,16 +1239,41 @@ static int rockchip_pcie_remove(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct rockchip_pcie *rockchip = dev_get_drvdata(dev);
+	u32 status1, status2;
+	u32 status;
 	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(rockchip);
 
+	status1 = rockchip_pcie_read(rockchip, PCIE_CLIENT_BASIC_STATUS1);
+	status2 = rockchip_pcie_read(rockchip, PCIE_CLIENT_DEBUG_OUT_0);
+
+	if (!PCIE_LINK_UP(status1) || !PCIE_LINK_IS_L0(status2))
+		rockchip->in_remove = 1;
+
 	pci_stop_root_bus(bridge->bus);
 	pci_remove_root_bus(bridge->bus);
 	irq_domain_remove(rockchip->irq_domain);
 
+	/* disable link state */
+	status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS);
+	status |= BIT(4);
+	rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS);
+
+	mdelay(1);
+
+	status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_LCS);
+	status &= ~BIT(4);
+	rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_LCS);
+
 	rockchip_pcie_deinit_phys(rockchip);
 
 	rockchip_pcie_disable_clocks(rockchip);
 
+	if (rockchip->dma_trx_enabled)
+		rk_pcie_dma_obj_remove(rockchip->dma_obj);
+
+	if (rockchip->deferred)
+		sysfs_remove_group(&pdev->dev.kobj, &pcie_attr_group);
+
 	if (!IS_ERR(rockchip->vpcie12v))
 		regulator_disable(rockchip->vpcie12v);
 	if (!IS_ERR(rockchip->vpcie3v3))
diff --git a/drivers/pci/controller/pcie-rockchip.c b/drivers/pci/controller/pcie-rockchip.c
index 990a00e08..92cced721 100644
--- a/drivers/pci/controller/pcie-rockchip.c
+++ b/drivers/pci/controller/pcie-rockchip.c
@@ -14,6 +14,7 @@
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/gpio/consumer.h>
+#include <linux/module.h>
 #include <linux/of_pci.h>
 #include <linux/phy/phy.h>
 #include <linux/platform_device.h>
@@ -421,3 +422,7 @@ void rockchip_pcie_cfg_configuration_accesses(
 	rockchip_pcie_write(rockchip, 0x0, PCIE_CORE_OB_REGION_DESC1);
 }
 EXPORT_SYMBOL_GPL(rockchip_pcie_cfg_configuration_accesses);
+
+MODULE_AUTHOR("Rockchip Inc");
+MODULE_DESCRIPTION("Rockchip AXI PCIe driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/controller/pcie-rockchip.h b/drivers/pci/controller/pcie-rockchip.h
index c7d0178fc..e34d33264 100644
--- a/drivers/pci/controller/pcie-rockchip.h
+++ b/drivers/pci/controller/pcie-rockchip.h
@@ -31,6 +31,7 @@
 #define   PCIE_CLIENT_CONF_ENABLE	  HIWORD_UPDATE_BIT(0x0001)
 #define   PCIE_CLIENT_CONF_DISABLE       HIWORD_UPDATE(0x0001, 0)
 #define   PCIE_CLIENT_LINK_TRAIN_ENABLE	  HIWORD_UPDATE_BIT(0x0002)
+#define   PCIE_CLIENT_LINK_TRAIN_DISABLE  HIWORD_UPDATE(0x0002, 0x0000)
 #define   PCIE_CLIENT_ARI_ENABLE	  HIWORD_UPDATE_BIT(0x0008)
 #define   PCIE_CLIENT_CONF_LANE_NUM(x)	  HIWORD_UPDATE(0x0030, ENCODE_LANES(x))
 #define   PCIE_CLIENT_MODE_RC		  HIWORD_UPDATE_BIT(0x0040)
@@ -39,6 +40,7 @@
 #define   PCIE_CLIENT_GEN_SEL_2		  HIWORD_UPDATE_BIT(0x0080)
 #define PCIE_CLIENT_DEBUG_OUT_0		(PCIE_CLIENT_BASE + 0x3c)
 #define   PCIE_CLIENT_DEBUG_LTSSM_MASK		GENMASK(5, 0)
+#define   PCIE_CLIENT_DEBUG_LTSSM_L0		0x10
 #define   PCIE_CLIENT_DEBUG_LTSSM_L1		0x18
 #define   PCIE_CLIENT_DEBUG_LTSSM_L2		0x19
 #define PCIE_CLIENT_BASIC_STATUS1	(PCIE_CLIENT_BASE + 0x48)
@@ -74,7 +76,20 @@
 	PCIE_CLIENT_INT_FATAL_ERR | PCIE_CLIENT_INT_DPA | \
 	PCIE_CLIENT_INT_HOT_RST | PCIE_CLIENT_INT_MSG | \
 	PCIE_CLIENT_INT_LEGACY_DONE | PCIE_CLIENT_INT_LEGACY | \
-	PCIE_CLIENT_INT_PHY)
+	PCIE_CLIENT_INT_PHY | PCIE_CLIENT_INT_UDMA)
+
+#define PCIE_APB_CORE_UDMA_BASE	(BIT(23) | BIT(22) | BIT(21))
+#define PCIE_CH0_DONE_ENABLE	BIT(0)
+#define PCIE_CH1_DONE_ENABLE	BIT(1)
+#define PCIE_CH0_ERR_ENABLE	BIT(8)
+#define PCIE_CH1_ERR_ENABLE	BIT(9)
+
+#define PCIE_UDMA_INT_REG			0xa0
+#define PCIE_UDMA_INT_ENABLE_REG	0xa4
+
+#define PCIE_UDMA_INT_ENABLE_MASK \
+	(PCIE_CH0_DONE_ENABLE | PCIE_CH1_DONE_ENABLE | \
+	PCIE_CH0_ERR_ENABLE | PCIE_CH1_ERR_ENABLE)
 
 #define PCIE_CORE_CTRL_MGMT_BASE	0x900000
 #define PCIE_CORE_CTRL			(PCIE_CORE_CTRL_MGMT_BASE + 0x000)
@@ -185,6 +200,8 @@
 #define PCIE_ECAM_ADDR(bus, dev, func, reg) \
 	  (PCIE_ECAM_BUS(bus) | PCIE_ECAM_DEV(dev) | \
 	   PCIE_ECAM_FUNC(func) | PCIE_ECAM_REG(reg))
+#define PCIE_LINK_IS_L0(x) \
+	(((x) & PCIE_CLIENT_DEBUG_LTSSM_MASK) == PCIE_CLIENT_DEBUG_LTSSM_L0)
 #define PCIE_LINK_IS_L2(x) \
 	(((x) & PCIE_CLIENT_DEBUG_LTSSM_MASK) == PCIE_CLIENT_DEBUG_LTSSM_L2)
 #define PCIE_LINK_UP(x) \
@@ -275,6 +292,9 @@
 		(((c) << ((b) * 8 + 5)) & \
 		 ROCKCHIP_PCIE_CORE_EP_FUNC_BAR_CFG_BAR_CTRL_MASK(b))
 
+#define PCIE_USER_RELINK 0x1
+#define PCIE_USER_UNLINK 0x2
+
 struct rockchip_pcie {
 	void	__iomem *reg_base;		/* DT axi-base */
 	void	__iomem *apb_base;		/* DT apb-base */
@@ -306,6 +326,15 @@ struct rockchip_pcie {
 	phys_addr_t msg_bus_addr;
 	bool is_rc;
 	struct resource *mem_res;
+	phys_addr_t mem_reserve_start;
+	size_t mem_reserve_size;
+	int dma_trx_enabled;
+	int deferred;
+	int wait_ep;
+	struct dma_trx_obj *dma_obj;
+	struct list_head resources;
+	struct pci_host_bridge *bridge;
+	int in_remove;
 };
 
 static u32 rockchip_pcie_read(struct rockchip_pcie *rockchip, u32 reg)
diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile
index c3cfc7f0a..d0dfcf9a8 100644
--- a/drivers/phy/rockchip/Makefile
+++ b/drivers/phy/rockchip/Makefile
@@ -3,7 +3,7 @@ obj-$(CONFIG_PHY_ROCKCHIP_DP)		+= phy-rockchip-dp.o
 obj-$(CONFIG_PHY_ROCKCHIP_DPHY_RX0)     += phy-rockchip-dphy-rx0.o
 obj-$(CONFIG_PHY_ROCKCHIP_EMMC)		+= phy-rockchip-emmc.o
 obj-$(CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY)	+= phy-rockchip-inno-dsidphy.o
-obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI)	+= phy-rockchip-inno-hdmi.o
+
 obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2)	+= phy-rockchip-inno-usb2.o
 obj-$(CONFIG_PHY_ROCKCHIP_PCIE)		+= phy-rockchip-pcie.o
 obj-$(CONFIG_PHY_ROCKCHIP_TYPEC)	+= phy-rockchip-typec.o
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c b/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c
index 8af8c6c5c..92462fee9 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c
@@ -83,9 +83,31 @@
 #define DATA_LANE_0_SKEW_PHASE_MASK		GENMASK(2, 0)
 #define DATA_LANE_0_SKEW_PHASE(x)		UPDATE(x, 2, 0)
 /* Analog Register Part: reg08 */
+#define PRE_EMPHASIS_ENABLE_MASK		BIT(7)
+#define PRE_EMPHASIS_ENABLE			BIT(7)
+#define PRE_EMPHASIS_DISABLE			0
+#define PLL_POST_DIV_ENABLE_MASK		BIT(5)
+#define PLL_POST_DIV_ENABLE			BIT(5)
+#define PLL_POST_DIV_DISABLE			0
+#define DATA_LANE_VOD_RANGE_SET_MASK		GENMASK(3, 0)
+#define DATA_LANE_VOD_RANGE_SET(x)		UPDATE(x, 3, 0)
 #define SAMPLE_CLOCK_DIRECTION_MASK		BIT(4)
 #define SAMPLE_CLOCK_DIRECTION_REVERSE		BIT(4)
 #define SAMPLE_CLOCK_DIRECTION_FORWARD		0
+#define LOWFRE_EN_MASK                          BIT(5)
+#define PLL_OUTPUT_FREQUENCY_DIV_BY_1           0
+#define PLL_OUTPUT_FREQUENCY_DIV_BY_2           1
+/* Analog Register Part: reg1e */
+#define PLL_MODE_SEL_MASK			GENMASK(6, 5)
+#define PLL_MODE_SEL_LVDS_MODE			0
+#define PLL_MODE_SEL_MIPI_MODE			BIT(5)
+/* Analog Register Part: reg0b */
+#define CLOCK_LANE_VOD_RANGE_SET_MASK	GENMASK(3, 0)
+#define CLOCK_LANE_VOD_RANGE_SET(x)	UPDATE(x, 3, 0)
+#define VOD_MIN_RANGE			0x1
+#define VOD_MID_RANGE			0x3
+#define VOD_BIG_RANGE			0x7
+#define VOD_MAX_RANGE			0xf
 /* Digital Register Part: reg00 */
 #define REG_DIG_RSTN_MASK			BIT(0)
 #define REG_DIG_RSTN_NORMAL			BIT(0)
@@ -101,20 +123,22 @@
 #define T_LPX_CNT_MASK				GENMASK(5, 0)
 #define T_LPX_CNT(x)				UPDATE(x, 5, 0)
 /* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg06 */
+#define T_HS_ZERO_CNT_HI_MASK			BIT(7)
+#define T_HS_ZERO_CNT_HI(x)			UPDATE(x, 7, 7)
 #define T_HS_PREPARE_CNT_MASK			GENMASK(6, 0)
 #define T_HS_PREPARE_CNT(x)			UPDATE(x, 6, 0)
 /* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg07 */
-#define T_HS_ZERO_CNT_MASK			GENMASK(5, 0)
-#define T_HS_ZERO_CNT(x)			UPDATE(x, 5, 0)
+#define T_HS_ZERO_CNT_LO_MASK			GENMASK(5, 0)
+#define T_HS_ZERO_CNT_LO(x)			UPDATE(x, 5, 0)
 /* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg08 */
 #define T_HS_TRAIL_CNT_MASK			GENMASK(6, 0)
 #define T_HS_TRAIL_CNT(x)			UPDATE(x, 6, 0)
 /* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg09 */
-#define T_HS_EXIT_CNT_MASK			GENMASK(4, 0)
-#define T_HS_EXIT_CNT(x)			UPDATE(x, 4, 0)
+#define T_HS_EXIT_CNT_LO_MASK			GENMASK(4, 0)
+#define T_HS_EXIT_CNT_LO(x)			UPDATE(x, 4, 0)
 /* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg0a */
-#define T_CLK_POST_CNT_MASK			GENMASK(3, 0)
-#define T_CLK_POST_CNT(x)			UPDATE(x, 3, 0)
+#define T_CLK_POST_CNT_LO_MASK			GENMASK(3, 0)
+#define T_CLK_POST_CNT_LO(x)			UPDATE(x, 3, 0)
 /* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg0c */
 #define LPDT_TX_PPI_SYNC_MASK			BIT(2)
 #define LPDT_TX_PPI_SYNC_ENABLE			BIT(2)
@@ -128,9 +152,13 @@
 #define T_CLK_PRE_CNT_MASK			GENMASK(3, 0)
 #define T_CLK_PRE_CNT(x)			UPDATE(x, 3, 0)
 /* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg10 */
+#define T_CLK_POST_HI_MASK			GENMASK(7, 6)
+#define T_CLK_POST_HI(x)			UPDATE(x, 7, 6)
 #define T_TA_GO_CNT_MASK			GENMASK(5, 0)
 #define T_TA_GO_CNT(x)				UPDATE(x, 5, 0)
 /* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg11 */
+#define T_HS_EXIT_CNT_HI_MASK			BIT(6)
+#define T_HS_EXIT_CNT_HI(x)			UPDATE(x, 6, 6)
 #define T_TA_SURE_CNT_MASK			GENMASK(5, 0)
 #define T_TA_SURE_CNT(x)			UPDATE(x, 5, 0)
 /* Clock/Data0/Data1/Data2/Data3 Lane Register Part: reg12 */
@@ -168,6 +196,20 @@
 #define DSI_PHY_STATUS		0xb0
 #define PHY_LOCK		BIT(0)
 
+enum phy_max_rate {
+	MAX_1GHZ,
+	MAX_2_5GHZ,
+};
+
+struct inno_mipi_dphy_timing {
+	unsigned int max_lane_mbps;
+	u8 lpx;
+	u8 hs_prepare;
+	u8 clk_lane_hs_zero;
+	u8 data_lane_hs_zero;
+	u8 hs_trail;
+};
+
 struct inno_dsidphy {
 	struct device *dev;
 	struct clk *ref_clk;
@@ -176,8 +218,9 @@ struct inno_dsidphy {
 	void __iomem *phy_base;
 	void __iomem *host_base;
 	struct reset_control *rst;
-	enum phy_mode mode;
 	struct phy_configure_opts_mipi_dphy dphy_cfg;
+	unsigned int lanes;
+	const struct inno_dsidphy_plat_data *pdata;
 
 	struct clk *pll_clk;
 	struct {
@@ -188,6 +231,12 @@ struct inno_dsidphy {
 	} pll;
 };
 
+struct inno_dsidphy_plat_data {
+	const struct inno_mipi_dphy_timing *inno_mipi_dphy_timing_table;
+	const unsigned int num_timings;
+	enum phy_max_rate max_rate;
+};
+
 enum {
 	REGISTER_PART_ANALOG,
 	REGISTER_PART_DIGITAL,
@@ -199,6 +248,44 @@ enum {
 	REGISTER_PART_LVDS,
 };
 
+static const
+struct inno_mipi_dphy_timing inno_mipi_dphy_timing_table_max_1GHz[] = {
+	{ 110, 0x0, 0x20, 0x16, 0x02, 0x22},
+	{ 150, 0x0, 0x06, 0x16, 0x03, 0x45},
+	{ 200, 0x0, 0x18, 0x17, 0x04, 0x0b},
+	{ 250, 0x0, 0x05, 0x17, 0x05, 0x16},
+	{ 300, 0x0, 0x51, 0x18, 0x06, 0x2c},
+	{ 400, 0x0, 0x64, 0x19, 0x07, 0x33},
+	{ 500, 0x0, 0x20, 0x1b, 0x07, 0x4e},
+	{ 600, 0x0, 0x6a, 0x1d, 0x08, 0x3a},
+	{ 700, 0x0, 0x3e, 0x1e, 0x08, 0x6a},
+	{ 800, 0x0, 0x21, 0x1f, 0x09, 0x29},
+	{1000, 0x0, 0x09, 0x20, 0x09, 0x27},
+};
+
+static const
+struct inno_mipi_dphy_timing inno_mipi_dphy_timing_table_max_2_5GHz[] = {
+	{ 110, 0x02, 0x7f, 0x16, 0x02, 0x02},
+	{ 150, 0x02, 0x7f, 0x16, 0x03, 0x02},
+	{ 200, 0x02, 0x7f, 0x17, 0x04, 0x02},
+	{ 250, 0x02, 0x7f, 0x17, 0x05, 0x04},
+	{ 300, 0x02, 0x7f, 0x18, 0x06, 0x04},
+	{ 400, 0x03, 0x7e, 0x19, 0x07, 0x04},
+	{ 500, 0x03, 0x7c, 0x1b, 0x07, 0x08},
+	{ 600, 0x03, 0x70, 0x1d, 0x08, 0x10},
+	{ 700, 0x05, 0x40, 0x1e, 0x08, 0x30},
+	{ 800, 0x05, 0x02, 0x1f, 0x09, 0x30},
+	{1000, 0x05, 0x08, 0x20, 0x09, 0x30},
+	{1200, 0x06, 0x03, 0x32, 0x14, 0x0f},
+	{1400, 0x09, 0x03, 0x32, 0x14, 0x0f},
+	{1600, 0x0d, 0x42, 0x36, 0x0e, 0x0f},
+	{1800, 0x0e, 0x47, 0x7a, 0x0e, 0x0f},
+	{2000, 0x11, 0x64, 0x7a, 0x0e, 0x0b},
+	{2200, 0x13, 0x64, 0x7e, 0x15, 0x0b},
+	{2400, 0x13, 0x33, 0x7f, 0x15, 0x6a},
+	{2500, 0x15, 0x54, 0x7f, 0x15, 0x6a},
+};
+
 static inline struct inno_dsidphy *hw_to_inno(struct clk_hw *hw)
 {
 	return container_of(hw, struct inno_dsidphy, pll.hw);
@@ -216,6 +303,17 @@ static void phy_update_bits(struct inno_dsidphy *inno,
 	writel(tmp, inno->phy_base + reg);
 }
 
+static void host_update_bits(struct inno_dsidphy *inno,
+			     u32 reg, u32 mask, u32 val)
+{
+	unsigned int tmp, orig;
+
+	orig = readl(inno->host_base + reg);
+	tmp = orig & ~mask;
+	tmp |= val & mask;
+	writel(tmp, inno->host_base + reg);
+}
+
 static unsigned long inno_dsidphy_pll_calc_rate(struct inno_dsidphy *inno,
 						unsigned long rate)
 {
@@ -286,39 +384,48 @@ static unsigned long inno_dsidphy_pll_calc_rate(struct inno_dsidphy *inno,
 	return best_freq;
 }
 
-static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
+static const struct inno_mipi_dphy_timing *
+inno_mipi_dphy_get_timing(struct inno_dsidphy *inno)
 {
-	struct phy_configure_opts_mipi_dphy *cfg = &inno->dphy_cfg;
-	const struct {
-		unsigned long rate;
-		u8 hs_prepare;
-		u8 clk_lane_hs_zero;
-		u8 data_lane_hs_zero;
-		u8 hs_trail;
-	} timings[] = {
-		{ 110000000, 0x20, 0x16, 0x02, 0x22},
-		{ 150000000, 0x06, 0x16, 0x03, 0x45},
-		{ 200000000, 0x18, 0x17, 0x04, 0x0b},
-		{ 250000000, 0x05, 0x17, 0x05, 0x16},
-		{ 300000000, 0x51, 0x18, 0x06, 0x2c},
-		{ 400000000, 0x64, 0x19, 0x07, 0x33},
-		{ 500000000, 0x20, 0x1b, 0x07, 0x4e},
-		{ 600000000, 0x6a, 0x1d, 0x08, 0x3a},
-		{ 700000000, 0x3e, 0x1e, 0x08, 0x6a},
-		{ 800000000, 0x21, 0x1f, 0x09, 0x29},
-		{1000000000, 0x09, 0x20, 0x09, 0x27},
-	};
-	u32 t_txbyteclkhs, t_txclkesc;
-	u32 txbyteclkhs, txclkesc, esc_clk_div;
-	u32 hs_exit, clk_post, clk_pre, wakeup, lpx, ta_go, ta_sure, ta_wait;
-	u32 hs_prepare, hs_trail, hs_zero, clk_lane_hs_zero, data_lane_hs_zero;
+	const struct inno_mipi_dphy_timing *timings;
+	unsigned int num_timings;
+	unsigned int lane_mbps = inno->pll.rate / USEC_PER_SEC;
 	unsigned int i;
 
-	inno_dsidphy_pll_calc_rate(inno, cfg->hs_clk_rate);
+	timings = inno->pdata->inno_mipi_dphy_timing_table;
+	num_timings = inno->pdata->num_timings;
 
-	/* Select MIPI mode */
-	phy_update_bits(inno, REGISTER_PART_LVDS, 0x03,
-			MODE_ENABLE_MASK, MIPI_MODE_ENABLE);
+	for (i = 0; i < num_timings; i++)
+		if (lane_mbps <= timings[i].max_lane_mbps)
+			break;
+
+	if (i == num_timings)
+		--i;
+
+	return &timings[i];
+}
+
+static void inno_mipi_dphy_max_2_5GHz_pll_enable(struct inno_dsidphy *inno)
+{
+
+	phy_update_bits(inno, REGISTER_PART_ANALOG, 0x03,
+			REG_PREDIV_MASK, REG_PREDIV(inno->pll.prediv));
+	phy_update_bits(inno, REGISTER_PART_ANALOG, 0x03,
+			REG_FBDIV_HI_MASK, REG_FBDIV_HI(inno->pll.fbdiv));
+	phy_update_bits(inno, REGISTER_PART_ANALOG, 0x04,
+			REG_FBDIV_LO_MASK, REG_FBDIV_LO(inno->pll.fbdiv));
+	phy_update_bits(inno, REGISTER_PART_ANALOG, 0x08,
+			PLL_POST_DIV_ENABLE_MASK, PLL_POST_DIV_ENABLE);
+	phy_update_bits(inno, REGISTER_PART_ANALOG, 0x0b,
+			CLOCK_LANE_VOD_RANGE_SET_MASK,
+			CLOCK_LANE_VOD_RANGE_SET(VOD_MAX_RANGE));
+	phy_update_bits(inno, REGISTER_PART_ANALOG, 0x01,
+			 REG_LDOPD_MASK | REG_PLLPD_MASK,
+			 REG_LDOPD_POWER_ON | REG_PLLPD_POWER_ON);
+}
+
+static void inno_mipi_dphy_max_1GHz_pll_enable(struct inno_dsidphy *inno)
+{
 	/* Configure PLL */
 	phy_update_bits(inno, REGISTER_PART_ANALOG, 0x03,
 			REG_PREDIV_MASK, REG_PREDIV(inno->pll.prediv));
@@ -330,6 +437,10 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
 	phy_update_bits(inno, REGISTER_PART_ANALOG, 0x01,
 			REG_LDOPD_MASK | REG_PLLPD_MASK,
 			REG_LDOPD_POWER_ON | REG_PLLPD_POWER_ON);
+}
+
+static void inno_mipi_dphy_reset(struct inno_dsidphy *inno)
+{
 	/* Reset analog */
 	phy_update_bits(inno, REGISTER_PART_ANALOG, 0x01,
 			REG_SYNCRST_MASK, REG_SYNCRST_RESET);
@@ -342,6 +453,17 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
 	udelay(1);
 	phy_update_bits(inno, REGISTER_PART_DIGITAL, 0x00,
 			REG_DIG_RSTN_MASK, REG_DIG_RSTN_NORMAL);
+}
+
+static void inno_mipi_dphy_timing_init(struct inno_dsidphy *inno)
+{
+	struct phy_configure_opts_mipi_dphy *cfg = &inno->dphy_cfg;
+	u32 t_txbyteclkhs, t_txclkesc;
+	u32 txbyteclkhs, txclkesc, esc_clk_div;
+	u32 hs_exit, clk_post, clk_pre, wakeup, lpx, ta_go, ta_sure, ta_wait;
+	u32 hs_prepare, hs_trail, hs_zero, clk_lane_hs_zero, data_lane_hs_zero;
+	const struct inno_mipi_dphy_timing *timing;
+	unsigned int i;
 
 	txbyteclkhs = inno->pll.rate / 8;
 	t_txbyteclkhs = div_u64(PSEC_PER_SEC, txbyteclkhs);
@@ -365,15 +487,6 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
 	 * Tclk-pre = Tpin_txbyteclkhs * value
 	 */
 	clk_pre = DIV_ROUND_UP(cfg->clk_pre, t_txbyteclkhs);
-
-	/*
-	 * The value of counter for HS Tlpx Time
-	 * Tlpx = Tpin_txbyteclkhs * (2 + value)
-	 */
-	lpx = DIV_ROUND_UP(cfg->lpx, t_txbyteclkhs);
-	if (lpx >= 2)
-		lpx -= 2;
-
 	/*
 	 * The value of counter for HS Tta-go
 	 * Tta-go for turnaround
@@ -393,17 +506,22 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
 	 */
 	ta_wait = DIV_ROUND_UP(cfg->ta_get, t_txclkesc);
 
-	for (i = 0; i < ARRAY_SIZE(timings); i++)
-		if (inno->pll.rate <= timings[i].rate)
-			break;
-
-	if (i == ARRAY_SIZE(timings))
-		--i;
-
-	hs_prepare = timings[i].hs_prepare;
-	hs_trail = timings[i].hs_trail;
-	clk_lane_hs_zero = timings[i].clk_lane_hs_zero;
-	data_lane_hs_zero = timings[i].data_lane_hs_zero;
+	timing = inno_mipi_dphy_get_timing(inno);
+	/*
+	 * The value of counter for HS Tlpx Time
+	 * Tlpx = Tpin_txbyteclkhs * (2 + value)
+	 */
+	if (inno->pdata->max_rate == MAX_1GHZ) {
+		lpx = DIV_ROUND_UP(cfg->lpx, t_txbyteclkhs);
+		if (lpx >= 2)
+			lpx -= 2;
+	} else
+		lpx = timing->lpx;
+
+	hs_prepare = timing->hs_prepare;
+	hs_trail = timing->hs_trail;
+	clk_lane_hs_zero = timing->clk_lane_hs_zero;
+	data_lane_hs_zero = timing->data_lane_hs_zero;
 	wakeup = 0x3ff;
 
 	for (i = REGISTER_PART_CLOCK_LANE; i <= REGISTER_PART_DATA3_LANE; i++) {
@@ -416,14 +534,29 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
 				T_LPX_CNT(lpx));
 		phy_update_bits(inno, i, 0x06, T_HS_PREPARE_CNT_MASK,
 				T_HS_PREPARE_CNT(hs_prepare));
-		phy_update_bits(inno, i, 0x07, T_HS_ZERO_CNT_MASK,
-				T_HS_ZERO_CNT(hs_zero));
+
+		if (inno->pdata->max_rate == MAX_2_5GHZ)
+			phy_update_bits(inno, i, 0x06, T_HS_ZERO_CNT_HI_MASK,
+					T_HS_ZERO_CNT_HI(hs_zero >> 6));
+
+		phy_update_bits(inno, i, 0x07, T_HS_ZERO_CNT_LO_MASK,
+				T_HS_ZERO_CNT_LO(hs_zero));
 		phy_update_bits(inno, i, 0x08, T_HS_TRAIL_CNT_MASK,
 				T_HS_TRAIL_CNT(hs_trail));
-		phy_update_bits(inno, i, 0x09, T_HS_EXIT_CNT_MASK,
-				T_HS_EXIT_CNT(hs_exit));
-		phy_update_bits(inno, i, 0x0a, T_CLK_POST_CNT_MASK,
-				T_CLK_POST_CNT(clk_post));
+
+		if (inno->pdata->max_rate == MAX_2_5GHZ)
+			phy_update_bits(inno, i, 0x11, T_HS_EXIT_CNT_HI_MASK,
+					T_HS_EXIT_CNT_HI(hs_exit >> 5));
+
+		phy_update_bits(inno, i, 0x09, T_HS_EXIT_CNT_LO_MASK,
+				T_HS_EXIT_CNT_LO(hs_exit));
+
+		if (inno->pdata->max_rate == MAX_2_5GHZ)
+			phy_update_bits(inno, i, 0x10, T_CLK_POST_HI_MASK,
+					T_CLK_POST_HI(clk_post >> 4));
+
+		phy_update_bits(inno, i, 0x0a, T_CLK_POST_CNT_LO_MASK,
+				T_CLK_POST_CNT_LO(clk_post));
 		phy_update_bits(inno, i, 0x0e, T_CLK_PRE_CNT_MASK,
 				T_CLK_PRE_CNT(clk_pre));
 		phy_update_bits(inno, i, 0x0c, T_WAKEUP_CNT_HI_MASK,
@@ -437,11 +570,46 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
 		phy_update_bits(inno, i, 0x12, T_TA_WAIT_CNT_MASK,
 				T_TA_WAIT_CNT(ta_wait));
 	}
+}
 
-	/* Enable all lanes on analog part */
-	phy_update_bits(inno, REGISTER_PART_ANALOG, 0x00,
-			LANE_EN_MASK, LANE_EN_CK | LANE_EN_3 | LANE_EN_2 |
-			LANE_EN_1 | LANE_EN_0);
+static void inno_mipi_dphy_lane_enable(struct inno_dsidphy *inno)
+{
+	u8 val = LANE_EN_CK;
+
+	switch (inno->lanes) {
+	case 1:
+		val |= LANE_EN_0;
+		break;
+	case 2:
+		val |= LANE_EN_1 | LANE_EN_0;
+		break;
+	case 3:
+		val |= LANE_EN_2 | LANE_EN_1 | LANE_EN_0;
+		break;
+	case 4:
+	default:
+		val |= LANE_EN_3 | LANE_EN_2 | LANE_EN_1 | LANE_EN_0;
+		break;
+	}
+
+	phy_update_bits(inno, REGISTER_PART_ANALOG, 0x00, LANE_EN_MASK, val);
+}
+
+static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
+{
+	/* Select MIPI mode */
+	phy_update_bits(inno, REGISTER_PART_LVDS, 0x03,
+			MODE_ENABLE_MASK, MIPI_MODE_ENABLE);
+
+	if (inno->pdata->max_rate == MAX_2_5GHZ)
+		inno_mipi_dphy_max_2_5GHz_pll_enable(inno);
+	else
+		inno_mipi_dphy_max_1GHz_pll_enable(inno);
+
+	inno_mipi_dphy_reset(inno);
+	inno_mipi_dphy_timing_init(inno);
+	inno_mipi_dphy_lane_enable(inno);
+	inno_mipi_dphy_lane_enable(inno);
 }
 
 static void inno_dsidphy_lvds_mode_enable(struct inno_dsidphy *inno)
@@ -451,8 +619,9 @@ static void inno_dsidphy_lvds_mode_enable(struct inno_dsidphy *inno)
 
 	/* Sample clock reverse direction */
 	phy_update_bits(inno, REGISTER_PART_ANALOG, 0x08,
-			SAMPLE_CLOCK_DIRECTION_MASK,
-			SAMPLE_CLOCK_DIRECTION_REVERSE);
+			SAMPLE_CLOCK_DIRECTION_MASK | LOWFRE_EN_MASK,
+			SAMPLE_CLOCK_DIRECTION_REVERSE |
+			PLL_OUTPUT_FREQUENCY_DIV_BY_1);
 
 	/* Select LVDS mode */
 	phy_update_bits(inno, REGISTER_PART_LVDS, 0x03,
@@ -472,6 +641,10 @@ static void inno_dsidphy_lvds_mode_enable(struct inno_dsidphy *inno)
 
 	msleep(20);
 
+	/* Select PLL mode */
+	phy_update_bits(inno, REGISTER_PART_ANALOG, 0x1e,
+			PLL_MODE_SEL_MASK, PLL_MODE_SEL_LVDS_MODE);
+
 	/* Reset LVDS digital logic */
 	phy_update_bits(inno, REGISTER_PART_LVDS, 0x00,
 			LVDS_DIGITAL_INTERNAL_RESET_MASK,
@@ -491,9 +664,36 @@ static void inno_dsidphy_lvds_mode_enable(struct inno_dsidphy *inno)
 			LVDS_DATA_LANE2_EN | LVDS_DATA_LANE3_EN);
 }
 
+static void inno_dsidphy_phy_ttl_mode_enable(struct inno_dsidphy *inno)
+{
+	/* Select TTL mode */
+	phy_update_bits(inno, REGISTER_PART_LVDS, 0x03,
+			MODE_ENABLE_MASK, TTL_MODE_ENABLE);
+	/* Reset digital logic */
+	phy_update_bits(inno, REGISTER_PART_LVDS, 0x00,
+			LVDS_DIGITAL_INTERNAL_RESET_MASK,
+			LVDS_DIGITAL_INTERNAL_RESET_ENABLE);
+	udelay(1);
+	phy_update_bits(inno, REGISTER_PART_LVDS, 0x00,
+			LVDS_DIGITAL_INTERNAL_RESET_MASK,
+			LVDS_DIGITAL_INTERNAL_RESET_DISABLE);
+	/* Enable digital logic */
+	phy_update_bits(inno, REGISTER_PART_LVDS, 0x01,
+			LVDS_DIGITAL_INTERNAL_ENABLE_MASK,
+			LVDS_DIGITAL_INTERNAL_ENABLE);
+	/* Enable analog driver */
+	phy_update_bits(inno, REGISTER_PART_LVDS, 0x0b,
+			LVDS_LANE_EN_MASK, LVDS_CLK_LANE_EN |
+			LVDS_DATA_LANE0_EN | LVDS_DATA_LANE1_EN |
+			LVDS_DATA_LANE2_EN | LVDS_DATA_LANE3_EN);
+	/* Enable for clk lane in TTL mode */
+	host_update_bits(inno, DSI_PHY_RSTZ, PHY_ENABLECLK, PHY_ENABLECLK);
+}
+
 static int inno_dsidphy_power_on(struct phy *phy)
 {
 	struct inno_dsidphy *inno = phy_get_drvdata(phy);
+	enum phy_mode mode = phy_get_mode(phy);
 
 	clk_prepare_enable(inno->pclk_phy);
 	clk_prepare_enable(inno->ref_clk);
@@ -506,7 +706,7 @@ static int inno_dsidphy_power_on(struct phy *phy)
 	phy_update_bits(inno, REGISTER_PART_ANALOG, 0x00,
 			POWER_WORK_MASK, POWER_WORK_ENABLE);
 
-	switch (inno->mode) {
+	switch (mode) {
 	case PHY_MODE_MIPI_DPHY:
 		inno_dsidphy_mipi_mode_enable(inno);
 		break;
@@ -514,7 +714,7 @@ static int inno_dsidphy_power_on(struct phy *phy)
 		inno_dsidphy_lvds_mode_enable(inno);
 		break;
 	default:
-		return -EINVAL;
+		inno_dsidphy_phy_ttl_mode_enable(inno);
 	}
 
 	return 0;
@@ -551,17 +751,6 @@ static int inno_dsidphy_power_off(struct phy *phy)
 static int inno_dsidphy_set_mode(struct phy *phy, enum phy_mode mode,
 				   int submode)
 {
-	struct inno_dsidphy *inno = phy_get_drvdata(phy);
-
-	switch (mode) {
-	case PHY_MODE_MIPI_DPHY:
-	case PHY_MODE_LVDS:
-		inno->mode = mode;
-		break;
-	default:
-		return -EINVAL;
-	}
-
 	return 0;
 }
 
@@ -569,9 +758,11 @@ static int inno_dsidphy_configure(struct phy *phy,
 				  union phy_configure_opts *opts)
 {
 	struct inno_dsidphy *inno = phy_get_drvdata(phy);
+	struct phy_configure_opts_mipi_dphy *cfg = &inno->dphy_cfg;
+	enum phy_mode mode = phy_get_mode(phy);
 	int ret;
 
-	if (inno->mode != PHY_MODE_MIPI_DPHY)
+	if (mode != PHY_MODE_MIPI_DPHY)
 		return -EINVAL;
 
 	ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);
@@ -580,6 +771,32 @@ static int inno_dsidphy_configure(struct phy *phy,
 
 	memcpy(&inno->dphy_cfg, &opts->mipi_dphy, sizeof(inno->dphy_cfg));
 
+	inno_dsidphy_pll_calc_rate(inno, cfg->hs_clk_rate);
+	cfg->hs_clk_rate = inno->pll.rate;
+	opts->mipi_dphy.hs_clk_rate = inno->pll.rate;
+
+	return 0;
+}
+
+static int inno_dsidphy_init(struct phy *phy)
+{
+	struct inno_dsidphy *inno = phy_get_drvdata(phy);
+
+	clk_prepare_enable(inno->pclk_phy);
+	clk_prepare_enable(inno->ref_clk);
+	pm_runtime_get_sync(inno->dev);
+
+	return 0;
+}
+
+static int inno_dsidphy_exit(struct phy *phy)
+{
+	struct inno_dsidphy *inno = phy_get_drvdata(phy);
+
+	pm_runtime_put(inno->dev);
+	clk_disable_unprepare(inno->ref_clk);
+	clk_disable_unprepare(inno->pclk_phy);
+
 	return 0;
 }
 
@@ -588,6 +805,8 @@ static const struct phy_ops inno_dsidphy_ops = {
 	.set_mode = inno_dsidphy_set_mode,
 	.power_on = inno_dsidphy_power_on,
 	.power_off = inno_dsidphy_power_off,
+	.init = inno_dsidphy_init,
+	.exit = inno_dsidphy_exit,
 	.owner = THIS_MODULE,
 };
 
@@ -597,6 +816,7 @@ static int inno_dsidphy_probe(struct platform_device *pdev)
 	struct inno_dsidphy *inno;
 	struct phy_provider *phy_provider;
 	struct phy *phy;
+	struct resource *res;
 	int ret;
 
 	inno = devm_kzalloc(dev, sizeof(*inno), GFP_KERNEL);
@@ -604,12 +824,23 @@ static int inno_dsidphy_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	inno->dev = dev;
+	inno->pdata = of_device_get_match_data(inno->dev);
 	platform_set_drvdata(pdev, inno);
 
-	inno->phy_base = devm_platform_ioremap_resource(pdev, 0);
+	inno->phy_base = devm_platform_ioremap_resource_byname(pdev, "phy");
 	if (IS_ERR(inno->phy_base))
 		return PTR_ERR(inno->phy_base);
 
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "host");
+	if (!res) {
+		dev_err(dev, "invalid host resource\n");
+		return -EINVAL;
+	}
+
+	inno->host_base = devm_ioremap(dev, res->start, resource_size(res));
+	if (IS_ERR(inno->host_base))
+		return PTR_ERR(inno->host_base);
+
 	inno->ref_clk = devm_clk_get(dev, "ref");
 	if (IS_ERR(inno->ref_clk)) {
 		ret = PTR_ERR(inno->ref_clk);
@@ -624,6 +855,13 @@ static int inno_dsidphy_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	inno->pclk_host = devm_clk_get(dev, "pclk_host");
+	if (IS_ERR(inno->pclk_host)) {
+		ret = PTR_ERR(inno->pclk_host);
+		dev_err(dev, "failed to get host pclk: %d\n", ret);
+		return ret;
+	}
+
 	inno->rst = devm_reset_control_get(dev, "apb");
 	if (IS_ERR(inno->rst)) {
 		ret = PTR_ERR(inno->rst);
@@ -638,6 +876,9 @@ static int inno_dsidphy_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	if (of_property_read_u32(dev->of_node, "inno,lanes", &inno->lanes))
+		inno->lanes = 4;
+
 	phy_set_drvdata(phy, inno);
 
 	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
@@ -652,6 +893,18 @@ static int inno_dsidphy_probe(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct inno_dsidphy_plat_data px30_plat_data = {
+	.inno_mipi_dphy_timing_table = inno_mipi_dphy_timing_table_max_1GHz,
+	.num_timings = ARRAY_SIZE(inno_mipi_dphy_timing_table_max_1GHz),
+	.max_rate = MAX_1GHZ,
+};
+
+static const struct inno_dsidphy_plat_data rk3568_plat_data = {
+	.inno_mipi_dphy_timing_table = inno_mipi_dphy_timing_table_max_2_5GHz,
+	.num_timings = ARRAY_SIZE(inno_mipi_dphy_timing_table_max_2_5GHz),
+	.max_rate = MAX_2_5GHZ,
+};
+
 static int inno_dsidphy_remove(struct platform_device *pdev)
 {
 	struct inno_dsidphy *inno = platform_get_drvdata(pdev);
@@ -662,9 +915,18 @@ static int inno_dsidphy_remove(struct platform_device *pdev)
 }
 
 static const struct of_device_id inno_dsidphy_of_match[] = {
-	{ .compatible = "rockchip,px30-dsi-dphy", },
-	{ .compatible = "rockchip,rk3128-dsi-dphy", },
-	{ .compatible = "rockchip,rk3368-dsi-dphy", },
+	{ .compatible = "rockchip,px30-dsi-dphy",
+	  .data = &px30_plat_data,
+	}, {
+	  .compatible = "rockchip,rk3128-dsi-dphy",
+	  .data = &px30_plat_data,
+	}, {
+	  .compatible = "rockchip,rk3368-dsi-dphy",
+	  .data = &px30_plat_data,
+	}, {
+	  .compatible = "rockchip,rk3568-dsi-dphy",
+	  .data = &rk3568_plat_data,
+	},
 	{}
 };
 MODULE_DEVICE_TABLE(of, inno_dsidphy_of_match);
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
index 46ebdb146..7a1868542 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -22,15 +22,23 @@
 #include <linux/of_platform.h>
 #include <linux/phy/phy.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/power_supply.h>
 #include <linux/regmap.h>
+#include <linux/reset.h>
 #include <linux/mfd/syscon.h>
 #include <linux/usb/of.h>
 #include <linux/usb/otg.h>
+#include <linux/usb/typec_mux.h>
+#include <linux/wakelock.h>
 
 #define BIT_WRITEABLE_SHIFT	16
 #define SCHEDULE_DELAY		(60 * HZ)
-#define OTG_SCHEDULE_DELAY	(2 * HZ)
+#define OTG_SCHEDULE_DELAY	(1 * HZ)
+#define BYPASS_SCHEDULE_DELAY	(2 * HZ)
+#define FILTER_COUNTER		0xF4240
+
+struct rockchip_usb2phy;
 
 enum rockchip_usb2phy_port_id {
 	USB2PHY_PORT_OTG,
@@ -69,6 +77,7 @@ enum usb_chg_state {
 static const unsigned int rockchip_usb2phy_extcon_cable[] = {
 	EXTCON_USB,
 	EXTCON_USB_HOST,
+	EXTCON_USB_VBUS_EN,
 	EXTCON_CHG_USB_SDP,
 	EXTCON_CHG_USB_CDP,
 	EXTCON_CHG_USB_DCP,
@@ -95,7 +104,7 @@ struct usb2phy_reg {
  * @rdm_pdwn_en: open dm pull down resistor.
  * @vdm_src_en: open dm voltage source.
  * @vdp_src_en: open dp voltage source.
- * @opmode: utmi operational mode.
+ * @chg_mode: set phy in charge detection mode.
  */
 struct rockchip_chg_det_reg {
 	struct usb2phy_reg	cp_det;
@@ -107,41 +116,96 @@ struct rockchip_chg_det_reg {
 	struct usb2phy_reg	rdm_pdwn_en;
 	struct usb2phy_reg	vdm_src_en;
 	struct usb2phy_reg	vdp_src_en;
-	struct usb2phy_reg	opmode;
+	struct usb2phy_reg	chg_mode;
 };
 
 /**
  * struct rockchip_usb2phy_port_cfg - usb-phy port configuration.
  * @phy_sus: phy suspend register.
+ * @pipe_phystatus: select pipe phystatus from grf or phy.
  * @bvalid_det_en: vbus valid rise detection enable register.
  * @bvalid_det_st: vbus valid rise detection status register.
  * @bvalid_det_clr: vbus valid rise detection clear register.
+ * @bvalid_grf_con: vbus valid software control.
+ * @bvalid_grf_sel: vbus valid software control select.
+ * @bvalid_phy_con: vbus valid external select and enable.
+ * @bypass_dm_en: usb bypass uart DM enable register.
+ * @bypass_sel: usb bypass uart select register.
+ * @bypass_iomux: usb bypass uart GRF iomux register.
+ * @bypass_bc: bypass battery charging module.
+ * @bypass_otg: bypass otg module.
+ * @bypass_host: bypass host module.
+ * @disfall_en: host disconnect fall edge detection enable.
+ * @disfall_st: host disconnect fall edge detection state.
+ * @disfall_clr: host disconnect fall edge detection clear.
+ * @disrise_en: host disconnect rise edge detection enable.
+ * @disrise_st: host disconnect rise edge detection state.
+ * @disrise_clr: host disconnect rise edge detection clear.
  * @ls_det_en: linestate detection enable register.
  * @ls_det_st: linestate detection state register.
  * @ls_det_clr: linestate detection clear register.
+ * @iddig_output: iddig output from grf.
+ * @iddig_en: utmi iddig select between grf and phy,
+ *	      0: from phy; 1: from grf
+ * @idfall_det_en: id fall detection enable register.
+ * @idfall_det_st: id fall detection state register.
+ * @idfall_det_clr: id fall detection clear register.
+ * @idrise_det_en: id rise detection enable register.
+ * @idrise_det_st: id rise detection state register.
+ * @idrise_det_clr: id rise detection clear register.
  * @utmi_avalid: utmi vbus avalid status register.
  * @utmi_bvalid: utmi vbus bvalid status register.
+ * @utmi_iddig: otg port id pin status register.
  * @utmi_ls: utmi linestate state register.
  * @utmi_hstdet: utmi host disconnect register.
+ * @vbus_det_en: vbus detect function power down register.
  */
 struct rockchip_usb2phy_port_cfg {
 	struct usb2phy_reg	phy_sus;
+	struct usb2phy_reg	pipe_phystatus;
 	struct usb2phy_reg	bvalid_det_en;
 	struct usb2phy_reg	bvalid_det_st;
 	struct usb2phy_reg	bvalid_det_clr;
+	struct usb2phy_reg	bvalid_grf_con;
+	struct usb2phy_reg	bvalid_grf_sel;
+	struct usb2phy_reg	bvalid_phy_con;
+	struct usb2phy_reg	bypass_dm_en;
+	struct usb2phy_reg	bypass_sel;
+	struct usb2phy_reg	bypass_iomux;
+	struct usb2phy_reg	bypass_bc;
+	struct usb2phy_reg	bypass_otg;
+	struct usb2phy_reg	bypass_host;
+	struct usb2phy_reg	disfall_en;
+	struct usb2phy_reg	disfall_st;
+	struct usb2phy_reg	disfall_clr;
+	struct usb2phy_reg	disrise_en;
+	struct usb2phy_reg	disrise_st;
+	struct usb2phy_reg	disrise_clr;
 	struct usb2phy_reg	ls_det_en;
 	struct usb2phy_reg	ls_det_st;
 	struct usb2phy_reg	ls_det_clr;
+	struct usb2phy_reg	iddig_output;
+	struct usb2phy_reg	iddig_en;
+	struct usb2phy_reg	idfall_det_en;
+	struct usb2phy_reg	idfall_det_st;
+	struct usb2phy_reg	idfall_det_clr;
+	struct usb2phy_reg	idrise_det_en;
+	struct usb2phy_reg	idrise_det_st;
+	struct usb2phy_reg	idrise_det_clr;
 	struct usb2phy_reg	utmi_avalid;
 	struct usb2phy_reg	utmi_bvalid;
+	struct usb2phy_reg	utmi_iddig;
 	struct usb2phy_reg	utmi_ls;
 	struct usb2phy_reg	utmi_hstdet;
+	struct usb2phy_reg	vbus_det_en;
 };
 
 /**
  * struct rockchip_usb2phy_cfg - usb-phy configuration.
  * @reg: the address offset of grf for usb-phy config.
  * @num_ports: specify how many ports that the phy has.
+ * @phy_tuning: phy default parameters tuning.
+ * @vbus_detect: vbus voltage level detection function.
  * @clkout_ctl: keep on/turn off output clk of phy.
  * @port_cfgs: usb-phy port configurations.
  * @chg_det: charger detection registers.
@@ -149,6 +213,8 @@ struct rockchip_usb2phy_port_cfg {
 struct rockchip_usb2phy_cfg {
 	unsigned int	reg;
 	unsigned int	num_ports;
+	int (*phy_tuning)(struct rockchip_usb2phy *rphy);
+	int (*vbus_detect)(struct rockchip_usb2phy *rphy, bool en);
 	struct usb2phy_reg	clkout_ctl;
 	const struct rockchip_usb2phy_port_cfg	port_cfgs[USB2PHY_NUM_PORTS];
 	const struct rockchip_chg_det_reg	chg_det;
@@ -158,16 +224,32 @@ struct rockchip_usb2phy_cfg {
  * struct rockchip_usb2phy_port - usb-phy port data.
  * @phy: generic phy.
  * @port_id: flag for otg port or host port.
+ * @low_power_en: enable enter low power when suspend.
+ * @perip_connected: flag for periphyeral connect status.
+ * @prev_iddig: previous otg port id pin status.
+ * @sel_pipe_phystatus: select pipe phystatus from grf.
  * @suspended: phy suspended flag.
+ * @typec_vbus_det: Type-C otg vbus detect.
+ * @utmi_avalid: utmi avalid status usage flag.
+ *	true	- use avalid to get vbus status
+ *	false	- use bvalid to get vbus status
  * @vbus_attached: otg device vbus status.
+ * @vbus_always_on: otg vbus is always powered on.
+ * @vbus_enabled: vbus regulator status.
+ * @bypass_uart_en: usb bypass uart enable, passed from DT.
+ * @host_disconnect: usb host disconnect status.
  * @bvalid_irq: IRQ number assigned for vbus valid rise detection.
  * @ls_irq: IRQ number assigned for linestate detection.
+ * @id_irq: IRQ number assigned for id fall or rise detection.
  * @otg_mux_irq: IRQ number which multiplex otg-id/otg-bvalid/linestate
  *		 irqs to one irq in otg-port.
  * @mutex: for register updating in sm_work.
  * @chg_work: charge detect work.
+ * @bypass_uart_work: usb bypass uart work.
  * @otg_sm_work: OTG state machine work.
  * @sm_work: HOST state machine work.
+ * @vbus: vbus regulator supply on few rockchip boards.
+ * @sw: orientation switch, communicate with TCPM (Type-C Port Manager).
  * @port_cfg: port register configuration, assigned by driver data.
  * @event_nb: hold event notification callback.
  * @state: define OTG enumeration states before device reset.
@@ -176,17 +258,32 @@ struct rockchip_usb2phy_cfg {
 struct rockchip_usb2phy_port {
 	struct phy	*phy;
 	unsigned int	port_id;
+	bool		low_power_en;
+	bool		perip_connected;
+	bool		prev_iddig;
+	bool		sel_pipe_phystatus;
 	bool		suspended;
+	bool		typec_vbus_det;
+	bool		utmi_avalid;
 	bool		vbus_attached;
+	bool		vbus_always_on;
+	bool		vbus_enabled;
+	bool		bypass_uart_en;
+	bool		host_disconnect;
 	int		bvalid_irq;
 	int		ls_irq;
+	int             id_irq;
 	int		otg_mux_irq;
 	struct mutex	mutex;
+	struct		delayed_work bypass_uart_work;
 	struct		delayed_work chg_work;
 	struct		delayed_work otg_sm_work;
 	struct		delayed_work sm_work;
+	struct		regulator *vbus;
+	struct		typec_switch *sw;
 	const struct	rockchip_usb2phy_port_cfg *port_cfg;
 	struct notifier_block	event_nb;
+	struct wake_lock	wakelock;
 	enum usb_otg_state	state;
 	enum usb_dr_mode	mode;
 };
@@ -196,6 +293,9 @@ struct rockchip_usb2phy_port {
  * @dev: pointer to device.
  * @grf: General Register Files regmap.
  * @usbgrf: USB General Register Files regmap.
+ * @usbctrl_grf: USB Controller General Register Files regmap.
+ * *phy_base: the base address of USB PHY.
+ * @phy_reset: phy reset control.
  * @clk: clock struct of phy input clk.
  * @clk480m: clock struct of phy output clk.
  * @clk480m_hw: clock struct of phy output clk management.
@@ -203,6 +303,12 @@ struct rockchip_usb2phy_port {
  * @chg_type: USB charger types.
  * @dcd_retries: The retry count used to track Data contact
  *		 detection process.
+ * @primary_retries: The retry count used for charger
+ *		     detection primary phase.
+ * @phy_sus_cfg: Store the phy current suspend configuration.
+ * @edev_self: represent the source of extcon.
+ * @irq: IRQ number assigned for phy which combined irqs of
+ *	 otg port and host port.
  * @edev: extcon device for notification registration
  * @phy_cfg: phy register configuration, assigned by driver data.
  * @ports: phy port instance.
@@ -211,12 +317,19 @@ struct rockchip_usb2phy {
 	struct device	*dev;
 	struct regmap	*grf;
 	struct regmap	*usbgrf;
+	struct regmap	*usbctrl_grf;
+	void __iomem	*phy_base;
+	struct reset_control	*phy_reset;
 	struct clk	*clk;
 	struct clk	*clk480m;
 	struct clk_hw	clk480m_hw;
 	enum usb_chg_state	chg_state;
 	enum power_supply_type	chg_type;
 	u8			dcd_retries;
+	u8			primary_retries;
+	unsigned int		phy_sus_cfg;
+	bool			edev_self;
+	int			irq;
 	struct extcon_dev	*edev;
 	const struct rockchip_usb2phy_cfg	*phy_cfg;
 	struct rockchip_usb2phy_port	ports[USB2PHY_NUM_PORTS];
@@ -254,6 +367,25 @@ static inline bool property_enabled(struct regmap *base,
 	return tmp == reg->enable;
 }
 
+static int rockchip_usb2phy_reset(struct rockchip_usb2phy *rphy)
+{
+	int ret;
+
+	ret = reset_control_assert(rphy->phy_reset);
+	if (ret)
+		return ret;
+
+	udelay(10);
+
+	ret = reset_control_deassert(rphy->phy_reset);
+	if (ret)
+		return ret;
+
+	usleep_range(100, 200);
+
+	return 0;
+}
+
 static int rockchip_usb2phy_clk480m_prepare(struct clk_hw *hw)
 {
 	struct rockchip_usb2phy *rphy =
@@ -319,7 +451,7 @@ static int
 rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy)
 {
 	struct device_node *node = rphy->dev->of_node;
-	struct clk_init_data init;
+	struct clk_init_data init = {};
 	const char *clk_name;
 	int ret;
 
@@ -393,6 +525,8 @@ static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy)
 			dev_err(rphy->dev, "failed to register extcon device\n");
 			return ret;
 		}
+
+		rphy->edev_self = true;
 	}
 
 	rphy->edev = edev;
@@ -400,6 +534,177 @@ static int rockchip_usb2phy_extcon_register(struct rockchip_usb2phy *rphy)
 	return 0;
 }
 
+/* The caller must hold rport->mutex lock */
+static int rockchip_usb2phy_enable_id_irq(struct rockchip_usb2phy *rphy,
+					  struct rockchip_usb2phy_port *rport,
+					  bool en)
+{
+	int ret;
+
+	ret = property_enable(rphy->grf, &rport->port_cfg->idfall_det_clr, true);
+	if (ret)
+		goto out;
+
+	ret = property_enable(rphy->grf, &rport->port_cfg->idfall_det_en, en);
+	if (ret)
+		goto out;
+
+	ret = property_enable(rphy->grf, &rport->port_cfg->idrise_det_clr, true);
+	if (ret)
+		goto out;
+
+	ret = property_enable(rphy->grf, &rport->port_cfg->idrise_det_en, en);
+out:
+	return ret;
+}
+
+/* The caller must hold rport->mutex lock */
+static int rockchip_usb2phy_enable_vbus_irq(struct rockchip_usb2phy *rphy,
+					    struct rockchip_usb2phy_port *rport,
+					    bool en)
+{
+	int ret;
+
+	ret = property_enable(rphy->grf, &rport->port_cfg->bvalid_det_clr, true);
+	if (ret)
+		goto out;
+
+	ret = property_enable(rphy->grf, &rport->port_cfg->bvalid_det_en, en);
+out:
+	return ret;
+}
+
+static int rockchip_usb2phy_enable_line_irq(struct rockchip_usb2phy *rphy,
+					    struct rockchip_usb2phy_port *rport,
+					    bool en)
+{
+	int ret;
+
+	ret = property_enable(rphy->grf, &rport->port_cfg->ls_det_clr, true);
+	if (ret)
+		goto out;
+
+	ret = property_enable(rphy->grf, &rport->port_cfg->ls_det_en, en);
+out:
+	return ret;
+}
+
+static int rockchip_usb2phy_enable_host_disc_irq(struct rockchip_usb2phy *rphy,
+						 struct rockchip_usb2phy_port *rport,
+						 bool en)
+{
+	int ret;
+
+	ret = property_enable(rphy->grf, &rport->port_cfg->disfall_clr, true);
+	if (ret)
+		goto out;
+
+	ret = property_enable(rphy->grf, &rport->port_cfg->disfall_en, en);
+	if (ret)
+		goto out;
+
+	ret = property_enable(rphy->grf, &rport->port_cfg->disrise_clr, true);
+	if (ret)
+		goto out;
+
+	ret = property_enable(rphy->grf, &rport->port_cfg->disrise_en, en);
+out:
+	return ret;
+}
+
+static int rockchip_usb_bypass_uart(struct rockchip_usb2phy_port *rport,
+				    bool en)
+{
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
+	const struct usb2phy_reg *iomux = &rport->port_cfg->bypass_iomux;
+	struct regmap *base = get_reg_base(rphy);
+	int ret = 0;
+
+	mutex_lock(&rport->mutex);
+
+	if (en == property_enabled(base, &rport->port_cfg->bypass_sel)) {
+		dev_info(&rport->phy->dev,
+			 "bypass uart %s is already set\n", en ? "on" : "off");
+		goto unlock;
+	}
+
+	dev_info(&rport->phy->dev, "bypass uart %s\n", en ? "on" : "off");
+
+	if (en) {
+		/*
+		 * To use UART function:
+		 * 1. Put the USB PHY in suspend mode and opmode is normal;
+		 * 2. Set bypasssel to 1'b1 and bypassdmen to 1'b1;
+		 *
+		 * Note: Although the datasheet requires that put USB PHY
+		 * in non-driving mode to disable resistance when use USB
+		 * bypass UART function, but actually we find that if we
+		 * set phy in non-driving mode, it will cause UART to print
+		 * random codes. So just put USB PHY in normal mode.
+		 */
+		ret |= property_enable(base, &rport->port_cfg->bypass_sel,
+				       true);
+		ret |= property_enable(base, &rport->port_cfg->bypass_dm_en,
+				       true);
+
+		/* Some platforms required to set iomux of bypass uart */
+		if (iomux->offset)
+			ret |= property_enable(rphy->grf, iomux, true);
+	} else {
+		/* just disable bypass, and resume phy in phy power_on later */
+		ret |= property_enable(base, &rport->port_cfg->bypass_sel,
+				       false);
+		ret |= property_enable(base, &rport->port_cfg->bypass_dm_en,
+				       false);
+
+		/* Some platforms required to set iomux of bypass uart */
+		if (iomux->offset)
+			ret |= property_enable(rphy->grf, iomux, false);
+	}
+
+unlock:
+	mutex_unlock(&rport->mutex);
+
+	return ret;
+}
+
+static void rockchip_usb_bypass_uart_work(struct work_struct *work)
+{
+	struct rockchip_usb2phy_port *rport =
+		container_of(work, struct rockchip_usb2phy_port,
+			     bypass_uart_work.work);
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
+	bool vbus, iddig;
+	int ret;
+
+	mutex_lock(&rport->mutex);
+
+	iddig = property_enabled(rphy->grf, &rport->port_cfg->utmi_iddig);
+
+	if (rport->utmi_avalid)
+		vbus = property_enabled(rphy->grf, &rport->port_cfg->utmi_avalid);
+	else
+		vbus = property_enabled(rphy->grf, &rport->port_cfg->utmi_bvalid);
+
+	mutex_unlock(&rport->mutex);
+
+	/*
+	 * If the vbus is low and iddig is high, it indicates that usb
+	 * otg is not working, then we can enable usb to bypass uart,
+	 * otherwise schedule the work until the conditions (vbus is low
+	 * and iddig is high) are matched.
+	 */
+	if (!vbus && iddig) {
+		ret = rockchip_usb_bypass_uart(rport, true);
+		if (ret)
+			dev_warn(&rport->phy->dev,
+				 "failed to enable bypass uart\n");
+	} else {
+		schedule_delayed_work(&rport->bypass_uart_work,
+				      BYPASS_SCHEDULE_DELAY);
+	}
+}
+
 static int rockchip_usb2phy_init(struct phy *phy)
 {
 	struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
@@ -408,39 +713,50 @@ static int rockchip_usb2phy_init(struct phy *phy)
 
 	mutex_lock(&rport->mutex);
 
-	if (rport->port_id == USB2PHY_PORT_OTG) {
-		if (rport->mode != USB_DR_MODE_HOST &&
-		    rport->mode != USB_DR_MODE_UNKNOWN) {
-			/* clear bvalid status and enable bvalid detect irq */
-			ret = property_enable(rphy->grf,
-					      &rport->port_cfg->bvalid_det_clr,
-					      true);
-			if (ret)
+	if (rport->port_id == USB2PHY_PORT_OTG &&
+	    (rport->mode == USB_DR_MODE_PERIPHERAL ||
+	     rport->mode == USB_DR_MODE_OTG)) {
+		/* clear id status and enable id detect irq */
+		if (rport->id_irq > 0 || rport->otg_mux_irq > 0 ||
+		    rphy->irq > 0) {
+			ret = rockchip_usb2phy_enable_id_irq(rphy, rport,
+							     true);
+			if (ret) {
+				dev_err(rphy->dev,
+					"failed to enable id irq\n");
 				goto out;
+			}
+		}
 
-			ret = property_enable(rphy->grf,
-					      &rport->port_cfg->bvalid_det_en,
-					      true);
-			if (ret)
+		/* clear bvalid status and enable bvalid detect irq */
+		if ((rport->bvalid_irq > 0 || rport->otg_mux_irq > 0 ||
+		    rphy->irq > 0) && !rport->vbus_always_on) {
+			ret = rockchip_usb2phy_enable_vbus_irq(rphy, rport,
+							       true);
+			if (ret) {
+				dev_err(rphy->dev,
+					"failed to enable bvalid irq\n");
 				goto out;
+			}
 
-			schedule_delayed_work(&rport->otg_sm_work,
-					      OTG_SCHEDULE_DELAY * 3);
-		} else {
-			/* If OTG works in host only mode, do nothing. */
-			dev_dbg(&rport->phy->dev, "mode %d\n", rport->mode);
+			schedule_delayed_work(&rport->otg_sm_work, 0);
 		}
 	} else if (rport->port_id == USB2PHY_PORT_HOST) {
-		/* clear linestate and enable linestate detect irq */
-		ret = property_enable(rphy->grf,
-				      &rport->port_cfg->ls_det_clr, true);
-		if (ret)
-			goto out;
+		if (rport->port_cfg->disfall_en.offset) {
+			rport->host_disconnect = true;
+			ret = rockchip_usb2phy_enable_host_disc_irq(rphy, rport, true);
+			if (ret) {
+				dev_err(rphy->dev, "failed to enable disconnect irq\n");
+				goto out;
+			}
+		}
 
-		ret = property_enable(rphy->grf,
-				      &rport->port_cfg->ls_det_en, true);
-		if (ret)
+		/* clear linestate and enable linestate detect irq */
+		ret = rockchip_usb2phy_enable_line_irq(rphy, rport, true);
+		if (ret) {
+			dev_err(rphy->dev, "failed to enable linestate irq\n");
 			goto out;
+		}
 
 		schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY);
 	}
@@ -459,22 +775,60 @@ static int rockchip_usb2phy_power_on(struct phy *phy)
 
 	dev_dbg(&rport->phy->dev, "port power on\n");
 
-	if (!rport->suspended)
-		return 0;
+	if (rport->bypass_uart_en) {
+		ret = rockchip_usb_bypass_uart(rport, false);
+		if (ret) {
+			dev_warn(&rport->phy->dev,
+				 "failed to disable bypass uart\n");
+			goto exit;
+		}
+	}
+
+	mutex_lock(&rport->mutex);
+
+	if (!rport->suspended) {
+		ret = 0;
+		goto unlock;
+	}
 
 	ret = clk_prepare_enable(rphy->clk480m);
 	if (ret)
-		return ret;
+		goto unlock;
+
+	if (rport->sel_pipe_phystatus)
+		property_enable(rphy->usbctrl_grf,
+				&rport->port_cfg->pipe_phystatus, true);
 
 	ret = property_enable(base, &rport->port_cfg->phy_sus, false);
 	if (ret)
-		return ret;
+		goto unlock;
+
+	/*
+	 * For rk3588, it needs to reset phy when exit from
+	 * suspend mode with common_on_n 1'b1(aka REFCLK_LOGIC,
+	 * Bias, and PLL blocks are powered down) for lower
+	 * power consumption. If you don't want to reset phy,
+	 * please keep the common_on_n 1'b0 to set these blocks
+	 * remain powered.
+	 */
+	ret = rockchip_usb2phy_reset(rphy);
+	if (ret)
+		goto unlock;
 
 	/* waiting for the utmi_clk to become stable */
 	usleep_range(1500, 2000);
 
 	rport->suspended = false;
-	return 0;
+
+unlock:
+	mutex_unlock(&rport->mutex);
+
+	/* Enable bypass uart in the bypass_uart_work. */
+	if (rport->bypass_uart_en)
+		schedule_delayed_work(&rport->bypass_uart_work, 0);
+
+exit:
+	return ret;
 }
 
 static int rockchip_usb2phy_power_off(struct phy *phy)
@@ -486,42 +840,258 @@ static int rockchip_usb2phy_power_off(struct phy *phy)
 
 	dev_dbg(&rport->phy->dev, "port power off\n");
 
-	if (rport->suspended)
-		return 0;
+	mutex_lock(&rport->mutex);
+
+	if (rport->suspended) {
+		ret = 0;
+		goto unlock;
+	}
 
 	ret = property_enable(base, &rport->port_cfg->phy_sus, true);
 	if (ret)
-		return ret;
+		goto unlock;
 
 	rport->suspended = true;
 	clk_disable_unprepare(rphy->clk480m);
 
-	return 0;
+unlock:
+	mutex_unlock(&rport->mutex);
+
+	/* Enable bypass uart in the bypass_uart_work. */
+	if (rport->bypass_uart_en)
+		schedule_delayed_work(&rport->bypass_uart_work, 0);
+
+	return ret;
 }
 
 static int rockchip_usb2phy_exit(struct phy *phy)
 {
 	struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
 
-	if (rport->port_id == USB2PHY_PORT_OTG &&
-	    rport->mode != USB_DR_MODE_HOST &&
-	    rport->mode != USB_DR_MODE_UNKNOWN) {
-		cancel_delayed_work_sync(&rport->otg_sm_work);
-		cancel_delayed_work_sync(&rport->chg_work);
-	} else if (rport->port_id == USB2PHY_PORT_HOST)
+	if (rport->port_id == USB2PHY_PORT_HOST)
 		cancel_delayed_work_sync(&rport->sm_work);
+	else if (rport->port_id == USB2PHY_PORT_OTG &&
+		 rport->bvalid_irq > 0)
+		flush_delayed_work(&rport->otg_sm_work);
 
 	return 0;
 }
 
+static int rockchip_set_vbus_power(struct rockchip_usb2phy_port *rport,
+				   bool en)
+{
+	int ret = 0;
+
+	if (!rport->vbus)
+		return 0;
+
+	if (en && !rport->vbus_enabled) {
+		ret = regulator_enable(rport->vbus);
+		if (ret)
+			dev_err(&rport->phy->dev,
+				"Failed to enable VBUS supply\n");
+	} else if (!en && rport->vbus_enabled) {
+		ret = regulator_disable(rport->vbus);
+	}
+
+	if (ret == 0)
+		rport->vbus_enabled = en;
+
+	return ret;
+}
+
+static int rockchip_usb2phy_set_mode(struct phy *phy,
+				     enum phy_mode mode, int submode)
+{
+	struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
+	bool vbus_det_en;
+	int ret = 0;
+
+	if (rport->port_id != USB2PHY_PORT_OTG)
+		return ret;
+
+	switch (mode) {
+	case PHY_MODE_USB_OTG:
+		/*
+		 * In case of using vbus to detect connect state by u2phy,
+		 * enable vbus detect on otg mode.
+		 */
+		fallthrough;
+	case PHY_MODE_USB_DEVICE:
+		/* Disable VBUS supply */
+		rockchip_set_vbus_power(rport, false);
+		extcon_set_state_sync(rphy->edev, EXTCON_USB_VBUS_EN, false);
+		/* For vbus always on, set EXTCON_USB to true. */
+		extcon_set_state(rphy->edev, EXTCON_USB, true);
+		rport->perip_connected = true;
+		vbus_det_en = true;
+		break;
+	case PHY_MODE_USB_HOST:
+		/* Enable VBUS supply */
+		ret = rockchip_set_vbus_power(rport, true);
+		if (ret) {
+			dev_err(&rport->phy->dev,
+				"Failed to set host mode\n");
+			return ret;
+		}
+
+		extcon_set_state_sync(rphy->edev, EXTCON_USB_VBUS_EN, true);
+		/* For vbus always on, deinit EXTCON_USB to false. */
+		extcon_set_state(rphy->edev, EXTCON_USB, false);
+		rport->perip_connected = false;
+		fallthrough;
+	case PHY_MODE_INVALID:
+		vbus_det_en = false;
+		break;
+	default:
+		dev_info(&rport->phy->dev, "illegal mode\n");
+		return ret;
+	}
+
+	if (rphy->phy_cfg->vbus_detect)
+		rphy->phy_cfg->vbus_detect(rphy, vbus_det_en);
+	else
+		ret = property_enable(rphy->grf, &rport->port_cfg->vbus_det_en,
+				      vbus_det_en);
+
+	return ret;
+}
+
 static const struct phy_ops rockchip_usb2phy_ops = {
 	.init		= rockchip_usb2phy_init,
 	.exit		= rockchip_usb2phy_exit,
 	.power_on	= rockchip_usb2phy_power_on,
 	.power_off	= rockchip_usb2phy_power_off,
+	.set_mode	= rockchip_usb2phy_set_mode,
 	.owner		= THIS_MODULE,
 };
 
+/* Show & store the current value of otg mode for otg port */
+static ssize_t otg_mode_show(struct device *device,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(device);
+	struct rockchip_usb2phy_port *rport = NULL;
+	unsigned int index;
+
+	for (index = 0; index < rphy->phy_cfg->num_ports; index++) {
+		rport = &rphy->ports[index];
+		if (rport->port_id == USB2PHY_PORT_OTG)
+			break;
+	}
+
+	if (!rport) {
+		dev_err(rphy->dev, "Fail to get otg port\n");
+		return -EINVAL;
+	} else if (rport->port_id != USB2PHY_PORT_OTG) {
+		dev_err(rphy->dev, "No support otg\n");
+		return -EINVAL;
+	}
+
+	switch (rport->mode) {
+	case USB_DR_MODE_HOST:
+		return sprintf(buf, "host\n");
+	case USB_DR_MODE_PERIPHERAL:
+		return sprintf(buf, "peripheral\n");
+	case USB_DR_MODE_OTG:
+		return sprintf(buf, "otg\n");
+	case USB_DR_MODE_UNKNOWN:
+		return sprintf(buf, "UNKNOWN\n");
+	}
+
+	return -EINVAL;
+}
+
+static ssize_t otg_mode_store(struct device *device,
+			      struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(device);
+	struct rockchip_usb2phy_port *rport = NULL;
+	struct regmap *base = get_reg_base(rphy);
+	enum usb_dr_mode new_dr_mode;
+	unsigned int index;
+	int rc = count;
+
+	for (index = 0; index < rphy->phy_cfg->num_ports; index++) {
+		rport = &rphy->ports[index];
+		if (rport->port_id == USB2PHY_PORT_OTG)
+			break;
+	}
+
+	if (!rport) {
+		dev_err(rphy->dev, "Fail to get otg port\n");
+		rc = -EINVAL;
+		goto err0;
+	} else if (rport->port_id != USB2PHY_PORT_OTG ||
+		   rport->mode == USB_DR_MODE_UNKNOWN) {
+		dev_err(rphy->dev, "No support otg\n");
+		rc = -EINVAL;
+		goto err0;
+	}
+
+	mutex_lock(&rport->mutex);
+
+	if (!strncmp(buf, "0", 1) || !strncmp(buf, "otg", 3)) {
+		new_dr_mode = USB_DR_MODE_OTG;
+	} else if (!strncmp(buf, "1", 1) || !strncmp(buf, "host", 4)) {
+		new_dr_mode = USB_DR_MODE_HOST;
+	} else if (!strncmp(buf, "2", 1) || !strncmp(buf, "peripheral", 10)) {
+		new_dr_mode = USB_DR_MODE_PERIPHERAL;
+	} else {
+		dev_err(rphy->dev, "Error mode! Input 'otg' or 'host' or 'peripheral'\n");
+		rc = -EINVAL;
+		goto err1;
+	}
+
+	if (rport->mode == new_dr_mode) {
+		dev_warn(rphy->dev, "Same as current mode\n");
+		goto err1;
+	}
+
+	rport->mode = new_dr_mode;
+
+	switch (rport->mode) {
+	case USB_DR_MODE_HOST:
+		rockchip_usb2phy_set_mode(rport->phy, PHY_MODE_USB_HOST, 0);
+		property_enable(base, &rport->port_cfg->iddig_output, false);
+		property_enable(base, &rport->port_cfg->iddig_en, true);
+		break;
+	case USB_DR_MODE_PERIPHERAL:
+		rockchip_usb2phy_set_mode(rport->phy, PHY_MODE_USB_DEVICE, 0);
+		property_enable(base, &rport->port_cfg->iddig_output, true);
+		property_enable(base, &rport->port_cfg->iddig_en, true);
+		break;
+	case USB_DR_MODE_OTG:
+		rockchip_usb2phy_set_mode(rport->phy, PHY_MODE_USB_OTG, 0);
+		property_enable(base, &rport->port_cfg->iddig_output, false);
+		property_enable(base, &rport->port_cfg->iddig_en, false);
+		break;
+	default:
+		break;
+	}
+
+err1:
+	mutex_unlock(&rport->mutex);
+
+err0:
+	return rc;
+}
+static DEVICE_ATTR_RW(otg_mode);
+
+/* Group all the usb2 phy attributes */
+static struct attribute *usb2_phy_attrs[] = {
+	&dev_attr_otg_mode.attr,
+	NULL,
+};
+
+static struct attribute_group usb2_phy_attr_group = {
+	.name = NULL,	/* we want them in the same directory */
+	.attrs = usb2_phy_attrs,
+};
+
 static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
 {
 	struct rockchip_usb2phy_port *rport =
@@ -530,59 +1100,80 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
 	struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
 	static unsigned int cable;
 	unsigned long delay;
-	bool vbus_attach, sch_work, notify_charger;
+	bool sch_work;
 
-	vbus_attach = property_enabled(rphy->grf,
-				       &rport->port_cfg->utmi_bvalid);
+	mutex_lock(&rport->mutex);
+
+	if (rport->port_cfg->bvalid_grf_con.enable && rport->typec_vbus_det)
+		rport->vbus_attached =
+			property_enabled(rphy->grf, &rport->port_cfg->bvalid_grf_con);
+	else if (rport->utmi_avalid)
+		rport->vbus_attached =
+			property_enabled(rphy->grf, &rport->port_cfg->utmi_avalid);
+	else
+		rport->vbus_attached =
+			property_enabled(rphy->grf, &rport->port_cfg->utmi_bvalid);
 
 	sch_work = false;
-	notify_charger = false;
 	delay = OTG_SCHEDULE_DELAY;
+
 	dev_dbg(&rport->phy->dev, "%s otg sm work\n",
 		usb_otg_state_string(rport->state));
 
 	switch (rport->state) {
 	case OTG_STATE_UNDEFINED:
 		rport->state = OTG_STATE_B_IDLE;
-		if (!vbus_attach)
+		if (!rport->vbus_attached) {
+			mutex_unlock(&rport->mutex);
 			rockchip_usb2phy_power_off(rport->phy);
+			mutex_lock(&rport->mutex);
+		}
 		fallthrough;
 	case OTG_STATE_B_IDLE:
-		if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) > 0) {
+		if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) > 0 ||
+		    extcon_get_state(rphy->edev, EXTCON_USB_VBUS_EN) > 0) {
 			dev_dbg(&rport->phy->dev, "usb otg host connect\n");
 			rport->state = OTG_STATE_A_HOST;
+			rphy->chg_state = USB_CHG_STATE_UNDEFINED;
+			rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
+			mutex_unlock(&rport->mutex);
 			rockchip_usb2phy_power_on(rport->phy);
 			return;
-		} else if (vbus_attach) {
+		} else if (rport->vbus_attached) {
 			dev_dbg(&rport->phy->dev, "vbus_attach\n");
 			switch (rphy->chg_state) {
 			case USB_CHG_STATE_UNDEFINED:
+				mutex_unlock(&rport->mutex);
 				schedule_delayed_work(&rport->chg_work, 0);
 				return;
 			case USB_CHG_STATE_DETECTED:
 				switch (rphy->chg_type) {
 				case POWER_SUPPLY_TYPE_USB:
 					dev_dbg(&rport->phy->dev, "sdp cable is connected\n");
+					wake_lock(&rport->wakelock);
+					cable = EXTCON_CHG_USB_SDP;
+					mutex_unlock(&rport->mutex);
 					rockchip_usb2phy_power_on(rport->phy);
+					mutex_lock(&rport->mutex);
 					rport->state = OTG_STATE_B_PERIPHERAL;
-					notify_charger = true;
+					rport->perip_connected = true;
 					sch_work = true;
-					cable = EXTCON_CHG_USB_SDP;
 					break;
 				case POWER_SUPPLY_TYPE_USB_DCP:
 					dev_dbg(&rport->phy->dev, "dcp cable is connected\n");
-					rockchip_usb2phy_power_off(rport->phy);
-					notify_charger = true;
-					sch_work = true;
 					cable = EXTCON_CHG_USB_DCP;
+					sch_work = true;
 					break;
 				case POWER_SUPPLY_TYPE_USB_CDP:
 					dev_dbg(&rport->phy->dev, "cdp cable is connected\n");
+					wake_lock(&rport->wakelock);
+					cable = EXTCON_CHG_USB_CDP;
+					mutex_unlock(&rport->mutex);
 					rockchip_usb2phy_power_on(rport->phy);
+					mutex_lock(&rport->mutex);
 					rport->state = OTG_STATE_B_PERIPHERAL;
-					notify_charger = true;
+					rport->perip_connected = true;
 					sch_work = true;
-					cable = EXTCON_CHG_USB_CDP;
 					break;
 				default:
 					break;
@@ -592,32 +1183,34 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
 				break;
 			}
 		} else {
-			notify_charger = true;
 			rphy->chg_state = USB_CHG_STATE_UNDEFINED;
 			rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
-		}
-
-		if (rport->vbus_attached != vbus_attach) {
-			rport->vbus_attached = vbus_attach;
-
-			if (notify_charger && rphy->edev) {
-				extcon_set_state_sync(rphy->edev,
-							cable, vbus_attach);
-				if (cable == EXTCON_CHG_USB_SDP)
-					extcon_set_state_sync(rphy->edev,
-							      EXTCON_USB,
-							      vbus_attach);
-			}
+			mutex_unlock(&rport->mutex);
+			rockchip_usb2phy_power_off(rport->phy);
+			mutex_lock(&rport->mutex);
 		}
 		break;
 	case OTG_STATE_B_PERIPHERAL:
-		if (!vbus_attach) {
-			dev_dbg(&rport->phy->dev, "usb disconnect\n");
+		sch_work = true;
+
+		if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) > 0 ||
+		    extcon_get_state(rphy->edev,
+					    EXTCON_USB_VBUS_EN) > 0) {
+			dev_dbg(&rport->phy->dev, "usb otg host connect\n");
+			rport->state = OTG_STATE_A_HOST;
 			rphy->chg_state = USB_CHG_STATE_UNDEFINED;
 			rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
+			rport->perip_connected = false;
+			sch_work = false;
+			wake_unlock(&rport->wakelock);
+		} else if (!rport->vbus_attached) {
+			dev_dbg(&rport->phy->dev, "usb disconnect\n");
 			rport->state = OTG_STATE_B_IDLE;
-			delay = 0;
-			rockchip_usb2phy_power_off(rport->phy);
+			rport->perip_connected = false;
+			rphy->chg_state = USB_CHG_STATE_UNDEFINED;
+			rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
+			delay = OTG_SCHEDULE_DELAY;
+			wake_unlock(&rport->wakelock);
 		}
 		sch_work = true;
 		break;
@@ -625,15 +1218,47 @@ static void rockchip_usb2phy_otg_sm_work(struct work_struct *work)
 		if (extcon_get_state(rphy->edev, EXTCON_USB_HOST) == 0) {
 			dev_dbg(&rport->phy->dev, "usb otg host disconnect\n");
 			rport->state = OTG_STATE_B_IDLE;
-			rockchip_usb2phy_power_off(rport->phy);
+			sch_work = true;
+		} else {
+			mutex_unlock(&rport->mutex);
+			return;
 		}
 		break;
 	default:
-		break;
+		mutex_unlock(&rport->mutex);
+		return;
+	}
+
+	if (extcon_get_state(rphy->edev, cable) != rport->vbus_attached) {
+		extcon_set_state_sync(rphy->edev,
+					cable, rport->vbus_attached);
+
+		if (!rport->vbus_attached)
+			cable = EXTCON_NONE;
+	} else if (rport->state == OTG_STATE_A_HOST &&
+		 extcon_get_state(rphy->edev, cable)) {
+		/*
+		 * If plug in OTG host cable when the rport state is
+		 * OTG_STATE_B_PERIPHERAL, the vbus voltage will stay
+		 * in high, so the rport->vbus_attached may not be
+		 * changed. We need to set cable state here.
+		 */
+		extcon_set_state_sync(rphy->edev, cable, false);
+		cable = EXTCON_NONE;
 	}
 
+	if (rphy->edev_self &&
+	    (extcon_get_state(rphy->edev, EXTCON_USB) !=
+	     rport->perip_connected)) {
+		extcon_set_state_sync(rphy->edev,
+					EXTCON_USB,
+					rport->perip_connected);
+		extcon_sync(rphy->edev, EXTCON_USB_HOST);
+	}
 	if (sch_work)
 		schedule_delayed_work(&rport->otg_sm_work, delay);
+
+	mutex_unlock(&rport->mutex);
 }
 
 static const char *chg_to_string(enum power_supply_type chg_type)
@@ -687,21 +1312,45 @@ static void rockchip_chg_detect_work(struct work_struct *work)
 		container_of(work, struct rockchip_usb2phy_port, chg_work.work);
 	struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
 	struct regmap *base = get_reg_base(rphy);
+	const struct usb2phy_reg *phy_sus_reg;
 	bool is_dcd, tmout, vout;
 	unsigned long delay;
+	unsigned int mask;
+	int ret;
 
 	dev_dbg(&rport->phy->dev, "chg detection work state = %d\n",
 		rphy->chg_state);
+
+	/*
+	 * The conditions for charger detection:
+	 * 1. Set the PHY in normal mode to keep the UTMI_CLK on.
+	 * 2. Set the utmi_opmode in non-driving mode.
+	 * 3. Set the utmi_xcvrselect to FS speed.
+	 * 4. Set the utmi_termselect to FS speed.
+	 * 5. Enable the DP/DM pulldown resistor.
+	 */
 	switch (rphy->chg_state) {
 	case USB_CHG_STATE_UNDEFINED:
-		if (!rport->suspended)
-			rockchip_usb2phy_power_off(rport->phy);
-		/* put the controller in non-driving mode */
-		property_enable(base, &rphy->phy_cfg->chg_det.opmode, false);
+		mutex_lock(&rport->mutex);
+		/* Store the PHY current suspend configuration */
+		phy_sus_reg = &rport->port_cfg->phy_sus;
+		ret = regmap_read(base, phy_sus_reg->offset,
+				  &rphy->phy_sus_cfg);
+		if (ret) {
+			dev_err(&rport->phy->dev,
+				"Fail to read phy_sus reg offset 0x%x, ret %d\n",
+				phy_sus_reg->offset, ret);
+			mutex_unlock(&rport->mutex);
+			return;
+		}
+
+		/* Set the PHY in charger detection mode */
+		property_enable(base, &rphy->phy_cfg->chg_det.chg_mode, true);
 		/* Start DCD processing stage 1 */
 		rockchip_chg_enable_dcd(rphy, true);
 		rphy->chg_state = USB_CHG_STATE_WAIT_FOR_DCD;
 		rphy->dcd_retries = 0;
+		rphy->primary_retries = 0;
 		delay = CHG_DCD_POLL_TIME;
 		break;
 	case USB_CHG_STATE_WAIT_FOR_DCD:
@@ -739,6 +1388,19 @@ static void rockchip_chg_detect_work(struct work_struct *work)
 				rphy->chg_state = USB_CHG_STATE_DETECTED;
 				delay = 0;
 			} else {
+				if (rphy->primary_retries < 2) {
+					/* Turn off DCD circuitry */
+					rockchip_chg_enable_dcd(rphy, false);
+					/* Voltage Source on DP, Probe on DM */
+					rockchip_chg_enable_primary_det(rphy,
+									true);
+					delay = CHG_PRIMARY_DET_TIME;
+					rphy->chg_state =
+						USB_CHG_STATE_DCD_DONE;
+					rphy->primary_retries++;
+					/* break USB_CHG_STATE_DCD_DONE */
+					break;
+				}
 				rphy->chg_type = POWER_SUPPLY_TYPE_USB;
 				rphy->chg_state = USB_CHG_STATE_DETECTED;
 				delay = 0;
@@ -757,19 +1419,36 @@ static void rockchip_chg_detect_work(struct work_struct *work)
 		fallthrough;
 	case USB_CHG_STATE_SECONDARY_DONE:
 		rphy->chg_state = USB_CHG_STATE_DETECTED;
-		delay = 0;
 		fallthrough;
 	case USB_CHG_STATE_DETECTED:
-		/* put the controller in normal mode */
-		property_enable(base, &rphy->phy_cfg->chg_det.opmode, true);
+		if (rphy->phy_cfg->chg_det.chg_mode.offset !=
+		    rport->port_cfg->phy_sus.offset)
+			property_enable(base, &rphy->phy_cfg->chg_det.chg_mode, false);
+
+		/* Restore the PHY suspend configuration */
+		phy_sus_reg = &rport->port_cfg->phy_sus;
+		mask = GENMASK(phy_sus_reg->bitend, phy_sus_reg->bitstart);
+		ret = regmap_write(base, phy_sus_reg->offset,
+				   (rphy->phy_sus_cfg | (mask << BIT_WRITEABLE_SHIFT)));
+		if (ret)
+			dev_err(&rport->phy->dev,
+				"Fail to set phy_sus reg offset 0x%x, ret %d\n",
+				phy_sus_reg->offset, ret);
+		mutex_unlock(&rport->mutex);
 		rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work);
 		dev_dbg(&rport->phy->dev, "charger = %s\n",
 			 chg_to_string(rphy->chg_type));
 		return;
 	default:
+		mutex_unlock(&rport->mutex);
 		return;
 	}
 
+	/*
+	 * Hold the mutex lock during the whole charger
+	 * detection stage, and release it after detect
+	 * the charger type.
+	 */
 	schedule_delayed_work(&rport->chg_work, delay);
 }
 
@@ -791,30 +1470,43 @@ static void rockchip_usb2phy_sm_work(struct work_struct *work)
 	struct rockchip_usb2phy_port *rport =
 		container_of(work, struct rockchip_usb2phy_port, sm_work.work);
 	struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
-	unsigned int sh = rport->port_cfg->utmi_hstdet.bitend -
-			  rport->port_cfg->utmi_hstdet.bitstart + 1;
-	unsigned int ul, uhd, state;
+	unsigned int sh, ul, uhd, state;
 	unsigned int ul_mask, uhd_mask;
 	int ret;
 
+	if (!rport->port_cfg->utmi_ls.offset ||
+	    (!rport->port_cfg->utmi_hstdet.offset &&
+	     !rport->port_cfg->disfall_en.offset)) {
+		dev_dbg(&rport->phy->dev, "some property may not be specified\n");
+		return;
+	}
+
 	mutex_lock(&rport->mutex);
 
 	ret = regmap_read(rphy->grf, rport->port_cfg->utmi_ls.offset, &ul);
 	if (ret < 0)
 		goto next_schedule;
 
-	ret = regmap_read(rphy->grf, rport->port_cfg->utmi_hstdet.offset, &uhd);
-	if (ret < 0)
-		goto next_schedule;
-
-	uhd_mask = GENMASK(rport->port_cfg->utmi_hstdet.bitend,
-			   rport->port_cfg->utmi_hstdet.bitstart);
 	ul_mask = GENMASK(rport->port_cfg->utmi_ls.bitend,
 			  rport->port_cfg->utmi_ls.bitstart);
 
-	/* stitch on utmi_ls and utmi_hstdet as phy state */
-	state = ((uhd & uhd_mask) >> rport->port_cfg->utmi_hstdet.bitstart) |
-		(((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << sh);
+	if (rport->port_cfg->utmi_hstdet.offset) {
+		ret = regmap_read(rphy->grf, rport->port_cfg->utmi_hstdet.offset, &uhd);
+		if (ret < 0)
+			goto next_schedule;
+
+		uhd_mask = GENMASK(rport->port_cfg->utmi_hstdet.bitend,
+				   rport->port_cfg->utmi_hstdet.bitstart);
+
+		sh = rport->port_cfg->utmi_hstdet.bitend -
+		     rport->port_cfg->utmi_hstdet.bitstart + 1;
+		/* stitch on utmi_ls and utmi_hstdet as phy state */
+		state = ((uhd & uhd_mask) >> rport->port_cfg->utmi_hstdet.bitstart) |
+			(((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << sh);
+	} else {
+		state = ((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << 1 |
+			rport->host_disconnect;
+	}
 
 	switch (state) {
 	case PHY_STATE_HS_ONLINE:
@@ -839,7 +1531,9 @@ static void rockchip_usb2phy_sm_work(struct work_struct *work)
 	case PHY_STATE_CONNECT:
 		if (rport->suspended) {
 			dev_dbg(&rport->phy->dev, "Connected\n");
+			mutex_unlock(&rport->mutex);
 			rockchip_usb2phy_power_on(rport->phy);
+			mutex_lock(&rport->mutex);
 			rport->suspended = false;
 		} else {
 			/* D+ line pull-up, D- line pull-down */
@@ -849,7 +1543,9 @@ static void rockchip_usb2phy_sm_work(struct work_struct *work)
 	case PHY_STATE_DISCONNECT:
 		if (!rport->suspended) {
 			dev_dbg(&rport->phy->dev, "Disconnected\n");
+			mutex_unlock(&rport->mutex);
 			rockchip_usb2phy_power_off(rport->phy);
+			mutex_lock(&rport->mutex);
 			rport->suspended = true;
 		}
 
@@ -857,8 +1553,7 @@ static void rockchip_usb2phy_sm_work(struct work_struct *work)
 		 * activate the linestate detection to get the next device
 		 * plug-in irq.
 		 */
-		property_enable(rphy->grf, &rport->port_cfg->ls_det_clr, true);
-		property_enable(rphy->grf, &rport->port_cfg->ls_det_en, true);
+		rockchip_usb2phy_enable_line_irq(rphy, rport, true);
 
 		/*
 		 * we don't need to rearm the delayed work when the phy port
@@ -867,7 +1562,7 @@ static void rockchip_usb2phy_sm_work(struct work_struct *work)
 		mutex_unlock(&rport->mutex);
 		return;
 	default:
-		dev_dbg(&rport->phy->dev, "unknown phy state\n");
+		dev_dbg(&rport->phy->dev, "unknown phy state %d\n", state);
 		break;
 	}
 
@@ -884,11 +1579,12 @@ static irqreturn_t rockchip_usb2phy_linestate_irq(int irq, void *data)
 	if (!property_enabled(rphy->grf, &rport->port_cfg->ls_det_st))
 		return IRQ_NONE;
 
+	dev_dbg(&rport->phy->dev, "linestate interrupt\n");
+
 	mutex_lock(&rport->mutex);
 
 	/* disable linestate detect irq and clear its status */
-	property_enable(rphy->grf, &rport->port_cfg->ls_det_en, false);
-	property_enable(rphy->grf, &rport->port_cfg->ls_det_clr, true);
+	rockchip_usb2phy_enable_line_irq(rphy, rport, false);
 
 	mutex_unlock(&rport->mutex);
 
@@ -918,99 +1614,157 @@ static irqreturn_t rockchip_usb2phy_bvalid_irq(int irq, void *data)
 
 	mutex_unlock(&rport->mutex);
 
+	if (rport->bypass_uart_en)
+		rockchip_usb_bypass_uart(rport, false);
+
+	cancel_delayed_work_sync(&rport->otg_sm_work);
 	rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work);
 
 	return IRQ_HANDLED;
 }
 
-static irqreturn_t rockchip_usb2phy_otg_mux_irq(int irq, void *data)
+static irqreturn_t rockchip_usb2phy_id_irq(int irq, void *data)
 {
 	struct rockchip_usb2phy_port *rport = data;
 	struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
+	bool cable_vbus_state = false;
 
-	if (property_enabled(rphy->grf, &rport->port_cfg->bvalid_det_st))
-		return rockchip_usb2phy_bvalid_irq(irq, data);
-	else
+	if (!property_enabled(rphy->grf, &rport->port_cfg->idfall_det_st) &&
+	    !property_enabled(rphy->grf, &rport->port_cfg->idrise_det_st))
 		return IRQ_NONE;
+
+	mutex_lock(&rport->mutex);
+
+	/* clear id fall or rise detect irq pending status */
+	if (property_enabled(rphy->grf, &rport->port_cfg->idfall_det_st)) {
+		property_enable(rphy->grf, &rport->port_cfg->idfall_det_clr,
+				true);
+		cable_vbus_state = true;
+	} else if (property_enabled(rphy->grf, &rport->port_cfg->idrise_det_st)) {
+		property_enable(rphy->grf, &rport->port_cfg->idrise_det_clr,
+				true);
+		cable_vbus_state = false;
+	}
+
+	extcon_set_state(rphy->edev, EXTCON_USB_HOST, cable_vbus_state);
+	extcon_set_state(rphy->edev, EXTCON_USB_VBUS_EN, cable_vbus_state);
+
+	extcon_sync(rphy->edev, EXTCON_USB_HOST);
+	extcon_sync(rphy->edev, EXTCON_USB_VBUS_EN);
+
+	rockchip_set_vbus_power(rport, cable_vbus_state);
+
+	mutex_unlock(&rport->mutex);
+
+	return IRQ_HANDLED;
 }
 
-static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy,
-					   struct rockchip_usb2phy_port *rport,
-					   struct device_node *child_np)
+static irqreturn_t rockchip_usb2phy_host_disc_irq(int irq, void *data)
 {
-	int ret;
+	struct rockchip_usb2phy_port *rport = data;
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
 
-	rport->port_id = USB2PHY_PORT_HOST;
-	rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_HOST];
-	rport->suspended = true;
+	if (!property_enabled(rphy->grf, &rport->port_cfg->disfall_st) &&
+	    !property_enabled(rphy->grf, &rport->port_cfg->disrise_st))
+		return IRQ_NONE;
 
-	mutex_init(&rport->mutex);
-	INIT_DELAYED_WORK(&rport->sm_work, rockchip_usb2phy_sm_work);
+	mutex_lock(&rport->mutex);
 
-	rport->ls_irq = of_irq_get_byname(child_np, "linestate");
-	if (rport->ls_irq < 0) {
-		dev_err(rphy->dev, "no linestate irq provided\n");
-		return rport->ls_irq;
+	/* clear disconnect fall or rise detect irq pending status */
+	if (property_enabled(rphy->grf, &rport->port_cfg->disfall_st)) {
+		property_enable(rphy->grf, &rport->port_cfg->disfall_clr,
+				true);
+		rport->host_disconnect = false;
+	} else if (property_enabled(rphy->grf, &rport->port_cfg->disrise_st)) {
+		property_enable(rphy->grf, &rport->port_cfg->disrise_clr,
+				true);
+		rport->host_disconnect = true;
 	}
 
-	ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL,
-					rockchip_usb2phy_linestate_irq,
-					IRQF_ONESHOT,
-					"rockchip_usb2phy", rport);
-	if (ret) {
-		dev_err(rphy->dev, "failed to request linestate irq handle\n");
-		return ret;
-	}
+	mutex_unlock(&rport->mutex);
 
-	return 0;
+	return IRQ_HANDLED;
 }
 
-static int rockchip_otg_event(struct notifier_block *nb,
-			      unsigned long event, void *ptr)
+static irqreturn_t rockchip_usb2phy_otg_mux_irq(int irq, void *data)
 {
-	struct rockchip_usb2phy_port *rport =
-		container_of(nb, struct rockchip_usb2phy_port, event_nb);
+	irqreturn_t ret = IRQ_NONE;
 
-	schedule_delayed_work(&rport->otg_sm_work, OTG_SCHEDULE_DELAY);
+	ret  = rockchip_usb2phy_id_irq(irq, data);
+	ret |= rockchip_usb2phy_bvalid_irq(irq, data);
+	ret |= rockchip_usb2phy_linestate_irq(irq, data);
 
-	return NOTIFY_DONE;
+	return ret;
 }
 
-static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
-					  struct rockchip_usb2phy_port *rport,
-					  struct device_node *child_np)
+static irqreturn_t rockchip_usb2phy_irq(int irq, void *data)
 {
-	int ret;
+	struct rockchip_usb2phy *rphy = data;
+	struct rockchip_usb2phy_port *rport;
+	irqreturn_t ret = IRQ_NONE;
+	unsigned int index;
+	bool force_mode;
 
-	rport->port_id = USB2PHY_PORT_OTG;
-	rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_OTG];
-	rport->state = OTG_STATE_UNDEFINED;
+	for (index = 0; index < rphy->phy_cfg->num_ports; index++) {
+		rport = &rphy->ports[index];
+		if (!rport->phy)
+			continue;
 
-	/*
-	 * set suspended flag to true, but actually don't
-	 * put phy in suspend mode, it aims to enable usb
-	 * phy and clock in power_on() called by usb controller
-	 * driver during probe.
-	 */
-	rport->suspended = true;
-	rport->vbus_attached = false;
+		if (rport->port_id == USB2PHY_PORT_HOST &&
+		    rport->port_cfg->disfall_en.offset)
+			ret = rockchip_usb2phy_host_disc_irq(irq, rport);
 
-	mutex_init(&rport->mutex);
+		/* Handle linestate irq for both otg port and host port */
+		ret = rockchip_usb2phy_linestate_irq(irq, rport);
 
-	rport->mode = of_usb_get_dr_mode_by_phy(child_np, -1);
-	if (rport->mode == USB_DR_MODE_HOST ||
-	    rport->mode == USB_DR_MODE_UNKNOWN) {
-		ret = 0;
-		goto out;
+		/*
+		 * Handle bvalid irq and id irq for otg port which
+		 * is assigned to otg controller.
+		 */
+		if (rport->port_id == USB2PHY_PORT_OTG &&
+		    rport->mode != USB_DR_MODE_UNKNOWN) {
+			if (rport->mode == USB_DR_MODE_HOST) {
+				/*
+				 * If otg port work as usb host mode and
+				 * force_mode is true, it means that the
+				 * otg port is forced to host mode by the
+				 * grf plug iddig indicator via the sys
+				 * interface "otg_mode". We need to handle
+				 * the bvalid irq and id irq in this case.
+				 */
+				force_mode = property_enabled(rphy->grf,
+						&rport->port_cfg->iddig_en);
+				if (!force_mode)
+					continue;
+			}
+
+			if (!rport->vbus_always_on)
+				ret |= rockchip_usb2phy_bvalid_irq(irq, rport);
+
+			ret |= rockchip_usb2phy_id_irq(irq, rport);
+		}
 	}
 
-	INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work);
-	INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work);
+	return ret;
+}
+
+static int rockchip_usb2phy_port_irq_init(struct rockchip_usb2phy *rphy,
+					  struct rockchip_usb2phy_port *rport,
+					  struct device_node *child_np)
+{
+	int ret;
+
+	/*
+	 * If the usb2 phy used combined irq for otg and host port,
+	 * don't need to init otg and host port irq separately.
+	 */
+	if (rphy->irq > 0)
+		return 0;
 
 	/*
-	 * Some SoCs use one interrupt with otg-id/otg-bvalid/linestate
-	 * interrupts muxed together, so probe the otg-mux interrupt first,
-	 * if not found, then look for the regular interrupts one by one.
+	 * Some SoCs (e.g. RV1108) use one combined irq for all of
+	 * the irqs of otg port. So probe the otg-mux interrupt first,
+	 * if not found, then init the regular irqs one by one.
 	 */
 	rport->otg_mux_irq = of_irq_get_byname(child_np, "otg-mux");
 	if (rport->otg_mux_irq > 0) {
@@ -1020,20 +1774,50 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
 						IRQF_ONESHOT,
 						"rockchip_usb2phy_otg",
 						rport);
-		if (ret) {
+		if (ret)
 			dev_err(rphy->dev,
 				"failed to request otg-mux irq handle\n");
-			goto out;
-		}
-	} else {
-		rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid");
-		if (rport->bvalid_irq < 0) {
-			dev_err(rphy->dev, "no vbus valid irq provided\n");
-			ret = rport->bvalid_irq;
-			goto out;
+
+		return ret;
+	}
+
+	/* Init linestate irq for both otg port and host port */
+	rport->ls_irq = of_irq_get_byname(child_np, "linestate");
+	if (rport->ls_irq <= 0) {
+		dev_err(rphy->dev, "no linestate irq provided\n");
+		return -EINVAL;
+	}
+
+	ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL,
+					rockchip_usb2phy_linestate_irq,
+					IRQF_ONESHOT,
+					"rockchip_usb2phy_ls", rport);
+	if (ret) {
+		dev_err(rphy->dev, "failed to request linestate irq handle\n");
+		return ret;
+	}
+
+	/*
+	 * If it's host port or it's otg port but only support
+	 * host mode, return immediately without init the bvalid
+	 * and id irqs/
+	 */
+	if (rport->port_id == USB2PHY_PORT_HOST ||
+	    rport->mode == USB_DR_MODE_HOST ||
+	    rport->mode == USB_DR_MODE_UNKNOWN)
+		return ret;
+
+	/* Init the bvalid irq for otg port */
+	if (!rport->vbus_always_on) {
+		rport->bvalid_irq = of_irq_get_byname(child_np,
+						      "otg-bvalid");
+		if (rport->bvalid_irq <= 0) {
+			dev_err(rphy->dev, "no bvalid irq provided\n");
+			return -EINVAL;
 		}
 
-		ret = devm_request_threaded_irq(rphy->dev, rport->bvalid_irq,
+		ret = devm_request_threaded_irq(rphy->dev,
+						rport->bvalid_irq,
 						NULL,
 						rockchip_usb2phy_bvalid_irq,
 						IRQF_ONESHOT,
@@ -1042,187 +1826,1104 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
 		if (ret) {
 			dev_err(rphy->dev,
 				"failed to request otg-bvalid irq handle\n");
-			goto out;
+			return ret;
 		}
 	}
 
-	if (!IS_ERR(rphy->edev)) {
-		rport->event_nb.notifier_call = rockchip_otg_event;
+	/* Init the id irq for otg port */
+	if (rphy->edev_self) {
+		rport->id_irq = of_irq_get_byname(child_np, "otg-id");
+		if (rport->id_irq <= 0) {
+			dev_err(rphy->dev, "no otg id irq provided\n");
+			return -EINVAL;
+		}
 
-		ret = devm_extcon_register_notifier(rphy->dev, rphy->edev,
-					EXTCON_USB_HOST, &rport->event_nb);
-		if (ret)
-			dev_err(rphy->dev, "register USB HOST notifier failed\n");
+		ret = devm_request_threaded_irq(rphy->dev,
+						rport->id_irq, NULL,
+						rockchip_usb2phy_id_irq,
+						IRQF_ONESHOT,
+						"rockchip_usb2phy_id",
+						rport);
+		if (ret) {
+			dev_err(rphy->dev,
+				"failed to request otg-id irq handle\n");
+			return ret;
+		}
 	}
 
-out:
 	return ret;
 }
 
-static int rockchip_usb2phy_probe(struct platform_device *pdev)
+static void rockchip_usb2phy_usb_bvalid_enable(struct rockchip_usb2phy_port *rport,
+					       u8 enable)
 {
-	struct device *dev = &pdev->dev;
-	struct device_node *np = dev->of_node;
-	struct device_node *child_np;
-	struct phy_provider *provider;
-	struct rockchip_usb2phy *rphy;
-	const struct rockchip_usb2phy_cfg *phy_cfgs;
-	const struct of_device_id *match;
-	unsigned int reg;
-	int index, ret;
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
+	const struct rockchip_usb2phy_port_cfg *cfg = rport->port_cfg;
 
-	rphy = devm_kzalloc(dev, sizeof(*rphy), GFP_KERNEL);
-	if (!rphy)
-		return -ENOMEM;
+	if (cfg->bvalid_phy_con.enable)
+		property_enable(rphy->grf, &cfg->bvalid_phy_con, enable);
 
-	match = of_match_device(dev->driver->of_match_table, dev);
-	if (!match || !match->data) {
-		dev_err(dev, "phy configs are not assigned!\n");
-		return -EINVAL;
-	}
+	if (cfg->bvalid_grf_con.enable)
+		property_enable(rphy->grf, &cfg->bvalid_grf_con, enable);
+}
 
-	if (!dev->parent || !dev->parent->of_node)
-		return -EINVAL;
+static int rockchip_usb2phy_orien_sw_set(struct typec_switch *sw,
+					 enum typec_orientation orien)
+{
+	struct rockchip_usb2phy_port *rport = typec_switch_get_drvdata(sw);
 
-	rphy->grf = syscon_node_to_regmap(dev->parent->of_node);
-	if (IS_ERR(rphy->grf))
-		return PTR_ERR(rphy->grf);
+	dev_dbg(&rport->phy->dev, "type-c orientation: %d\n", orien);
 
-	if (of_device_is_compatible(np, "rockchip,rv1108-usb2phy")) {
-		rphy->usbgrf =
-			syscon_regmap_lookup_by_phandle(dev->of_node,
-							"rockchip,usbgrf");
-		if (IS_ERR(rphy->usbgrf))
-			return PTR_ERR(rphy->usbgrf);
-	} else {
-		rphy->usbgrf = NULL;
-	}
+	mutex_lock(&rport->mutex);
+	rockchip_usb2phy_usb_bvalid_enable(rport, orien != TYPEC_ORIENTATION_NONE);
+	mutex_unlock(&rport->mutex);
 
-	if (of_property_read_u32(np, "reg", &reg)) {
-		dev_err(dev, "the reg property is not assigned in %pOFn node\n",
-			np);
-		return -EINVAL;
+	return 0;
+}
+
+static int
+rockchip_usb2phy_setup_orien_switch(struct rockchip_usb2phy *rphy,
+				    struct rockchip_usb2phy_port *rport)
+{
+	struct typec_switch_desc sw_desc = { };
+	struct device *dev = rphy->dev;
+
+	sw_desc.drvdata = rport;
+	sw_desc.fwnode = dev_fwnode(dev);
+	sw_desc.set = rockchip_usb2phy_orien_sw_set;
+
+	rport->sw = typec_switch_register(dev, &sw_desc);
+	if (IS_ERR(rport->sw)) {
+		dev_err(dev, "Error register typec orientation switch: %ld\n",
+			PTR_ERR(rport->sw));
+		return PTR_ERR(rport->sw);
 	}
 
-	rphy->dev = dev;
-	phy_cfgs = match->data;
-	rphy->chg_state = USB_CHG_STATE_UNDEFINED;
-	rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
-	platform_set_drvdata(pdev, rphy);
+	return 0;
+}
 
-	ret = rockchip_usb2phy_extcon_register(rphy);
-	if (ret)
-		return ret;
+static void rockchip_usb2phy_orien_switch_unregister(void *data)
+{
+	struct rockchip_usb2phy_port *rport = data;
 
-	/* find out a proper config which can be matched with dt. */
-	index = 0;
-	while (phy_cfgs[index].reg) {
-		if (phy_cfgs[index].reg == reg) {
-			rphy->phy_cfg = &phy_cfgs[index];
-			break;
-		}
+	typec_switch_unregister(rport->sw);
+}
 
-		++index;
-	}
+static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy,
+					   struct rockchip_usb2phy_port *rport,
+					   struct device_node *child_np)
+{
+	int ret;
+	struct regmap *base = get_reg_base(rphy);
 
-	if (!rphy->phy_cfg) {
-		dev_err(dev, "no phy-config can be matched with %pOFn node\n",
-			np);
-		return -EINVAL;
-	}
+	rport->port_id = USB2PHY_PORT_HOST;
+	rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_HOST];
 
-	rphy->clk = of_clk_get_by_name(np, "phyclk");
-	if (!IS_ERR(rphy->clk)) {
+	/* enter lower power state when suspend */
+	rport->low_power_en =
+		of_property_read_bool(child_np, "rockchip,low-power-mode");
+
+	mutex_init(&rport->mutex);
+	INIT_DELAYED_WORK(&rport->sm_work, rockchip_usb2phy_sm_work);
+
+	ret = rockchip_usb2phy_port_irq_init(rphy, rport, child_np);
+	if (ret) {
+		dev_err(rphy->dev, "failed to init irq for host port\n");
+		return ret;
+	}
+
+	/*
+	 * Let us put phy-port into suspend mode here for saving power
+	 * consumption, and usb controller will resume it during probe
+	 * time if needed.
+	 */
+	ret = property_enable(base, &rport->port_cfg->phy_sus, true);
+	if (ret)
+		return ret;
+	rport->suspended = true;
+
+	return 0;
+}
+
+static int rockchip_otg_event(struct notifier_block *nb,
+			      unsigned long event, void *ptr)
+{
+	struct rockchip_usb2phy_port *rport =
+		container_of(nb, struct rockchip_usb2phy_port, event_nb);
+
+	schedule_delayed_work(&rport->otg_sm_work, OTG_SCHEDULE_DELAY);
+
+	return NOTIFY_DONE;
+}
+
+static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy,
+					  struct rockchip_usb2phy_port *rport,
+					  struct device_node *child_np)
+{
+	int ret;
+	int iddig;
+	struct regmap *base = get_reg_base(rphy);
+
+	rport->port_id = USB2PHY_PORT_OTG;
+	rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_OTG];
+	rport->state = OTG_STATE_UNDEFINED;
+	rport->vbus_attached = false;
+	rport->vbus_enabled = false;
+	rport->perip_connected = false;
+	rport->prev_iddig = true;
+
+	mutex_init(&rport->mutex);
+
+	/* bypass uart function is only used in debug stage. */
+	rport->bypass_uart_en =
+		of_property_read_bool(child_np, "rockchip,bypass-uart");
+	rport->vbus_always_on =
+		of_property_read_bool(child_np, "rockchip,vbus-always-on");
+	rport->utmi_avalid =
+		of_property_read_bool(child_np, "rockchip,utmi-avalid");
+
+	/* enter lower power state when suspend */
+	rport->low_power_en =
+		of_property_read_bool(child_np, "rockchip,low-power-mode");
+
+	/* For type-c with vbus_det always pull up */
+	rport->typec_vbus_det =
+		of_property_read_bool(child_np, "rockchip,typec-vbus-det");
+
+	rport->sel_pipe_phystatus =
+		of_property_read_bool(child_np, "rockchip,sel-pipe-phystatus");
+
+	if (rport->sel_pipe_phystatus) {
+		rphy->usbctrl_grf =
+			syscon_regmap_lookup_by_phandle(rphy->dev->of_node,
+							"rockchip,usbctrl-grf");
+		if (IS_ERR(rphy->usbctrl_grf)) {
+			dev_err(rphy->dev, "Failed to map usbctrl-grf\n");
+			return PTR_ERR(rphy->usbctrl_grf);
+		}
+	}
+
+	/* Get Vbus regulators */
+	rport->vbus = devm_regulator_get_optional(&rport->phy->dev, "vbus");
+	if (IS_ERR(rport->vbus)) {
+		ret = PTR_ERR(rport->vbus);
+		if (ret == -EPROBE_DEFER)
+			return ret;
+
+		if (rport->mode == USB_DR_MODE_OTG)
+			dev_warn(&rport->phy->dev, "No vbus specified for otg port\n");
+		rport->vbus = NULL;
+	}
+
+	rport->mode = of_usb_get_dr_mode_by_phy(child_np, -1);
+	iddig = property_enabled(rphy->grf, &rport->port_cfg->utmi_iddig);
+	if (rphy->edev_self && (rport->mode == USB_DR_MODE_HOST ||
+	    rport->mode == USB_DR_MODE_UNKNOWN || !iddig)) {
+		/* Enable VBUS supply for otg port */
+		extcon_set_state(rphy->edev, EXTCON_USB, false);
+		extcon_set_state(rphy->edev, EXTCON_USB_HOST, true);
+		extcon_set_state(rphy->edev, EXTCON_USB_VBUS_EN, true);
+		ret = rockchip_set_vbus_power(rport, true);
+		if (ret)
+			return ret;
+	}
+
+	ret = rockchip_usb2phy_port_irq_init(rphy, rport, child_np);
+	if (ret) {
+		dev_err(rphy->dev, "failed to init irq for otg port\n");
+		return ret;
+	}
+
+	if (IS_REACHABLE(CONFIG_TYPEC) &&
+	    device_property_present(rphy->dev, "orientation-switch")) {
+		ret = rockchip_usb2phy_setup_orien_switch(rphy, rport);
+		if (ret)
+			return ret;
+
+		ret = devm_add_action_or_reset(rphy->dev,
+					       rockchip_usb2phy_orien_switch_unregister,
+					       rport);
+		if (ret)
+			return ret;
+	}
+
+	if (rport->vbus_always_on || rport->mode == USB_DR_MODE_HOST ||
+	    rport->mode == USB_DR_MODE_UNKNOWN)
+		goto out;
+
+	/* Select bvalid of usb phy as bvalid of usb controller */
+	if (rport->port_cfg->bvalid_grf_sel.enable != 0)
+		property_enable(base, &rport->port_cfg->bvalid_grf_sel, false);
+
+	wake_lock_init(&rport->wakelock, WAKE_LOCK_SUSPEND, "rockchip_otg");
+	INIT_DELAYED_WORK(&rport->bypass_uart_work,
+			  rockchip_usb_bypass_uart_work);
+	INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work);
+	INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work);
+
+	if (!IS_ERR(rphy->edev)) {
+		rport->event_nb.notifier_call = rockchip_otg_event;
+
+		ret = devm_extcon_register_notifier(rphy->dev, rphy->edev,
+					EXTCON_USB_HOST, &rport->event_nb);
+		if (ret) {
+			dev_err(rphy->dev, "register USB HOST notifier failed\n");
+			goto err;
+		}
+	}
+
+out:
+	/*
+	 * Let us put phy-port into suspend mode here for saving power
+	 * consumption, and usb controller will resume it during probe
+	 * time if needed.
+	 */
+	ret = property_enable(base, &rport->port_cfg->phy_sus, true);
+	if (ret)
+		return ret;
+	rport->suspended = true;
+
+	return 0;
+
+err:
+	wake_lock_destroy(&rport->wakelock);
+	return ret;
+}
+
+static int rockchip_usb2phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct device_node *child_np;
+	struct phy_provider *provider;
+	struct rockchip_usb2phy *rphy;
+	struct resource *res;
+	const struct rockchip_usb2phy_cfg *phy_cfgs;
+	const struct of_device_id *match;
+	unsigned int reg;
+	unsigned int index;
+	int ret;
+
+	rphy = devm_kzalloc(dev, sizeof(*rphy), GFP_KERNEL);
+	if (!rphy)
+		return -ENOMEM;
+
+	match = of_match_device(dev->driver->of_match_table, dev);
+	if (!match || !match->data) {
+		dev_err(dev, "phy configs are not assigned!\n");
+		return -EINVAL;
+	}
+
+	if (!dev->parent || !dev->parent->of_node) {
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+		if (!res) {
+			dev_err(dev, "missing memory resource\n");
+			return -ENODEV;
+		}
+
+		rphy->phy_base = devm_ioremap_resource(dev, res);
+		if (IS_ERR(rphy->phy_base))
+			return PTR_ERR(rphy->phy_base);
+
+		rphy->grf = syscon_regmap_lookup_by_phandle(np,
+							    "rockchip,usbgrf");
+		if (IS_ERR(rphy->grf))
+			return PTR_ERR(rphy->grf);
+
+		reg = res->start;
+	} else {
+		rphy->grf = syscon_node_to_regmap(dev->parent->of_node);
+		if (IS_ERR(rphy->grf))
+			return PTR_ERR(rphy->grf);
+
+		if (of_device_is_compatible(np, "rockchip,rv1108-usb2phy")) {
+			rphy->usbgrf =
+				syscon_regmap_lookup_by_phandle(dev->of_node,
+							"rockchip,usbgrf");
+			if (IS_ERR(rphy->usbgrf))
+				return PTR_ERR(rphy->usbgrf);
+		} else {
+			rphy->usbgrf = NULL;
+		}
+
+		if (of_property_read_u32(np, "reg", &reg)) {
+			dev_err(dev, "missing reg property in %s node\n",
+				np->name);
+			return -EINVAL;
+		}
+	}
+
+	rphy->dev = dev;
+	phy_cfgs = match->data;
+	rphy->chg_state = USB_CHG_STATE_UNDEFINED;
+	rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN;
+	rphy->edev_self = false;
+	rphy->irq = platform_get_irq(pdev, 0);
+	platform_set_drvdata(pdev, rphy);
+
+	ret = rockchip_usb2phy_extcon_register(rphy);
+	if (ret)
+		return ret;
+
+	/* find out a proper config which can be matched with dt. */
+	index = 0;
+	do {
+		if (phy_cfgs[index].reg == reg) {
+			rphy->phy_cfg = &phy_cfgs[index];
+			break;
+		}
+
+		++index;
+	} while (phy_cfgs[index].reg);
+
+	if (!rphy->phy_cfg) {
+		dev_err(dev, "no phy-config can be matched with %pOFn node\n",
+			np);
+		return -EINVAL;
+	}
+
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	pm_runtime_get_sync(dev);
+
+
+	rphy->phy_reset = devm_reset_control_get_optional(dev, "phy");
+	if (IS_ERR(rphy->phy_reset))
+		return PTR_ERR(rphy->phy_reset);
+
+	rphy->clk = of_clk_get_by_name(np, "phyclk");
+	if (!IS_ERR(rphy->clk)) {
 		clk_prepare_enable(rphy->clk);
 	} else {
 		dev_info(&pdev->dev, "no phyclk specified\n");
 		rphy->clk = NULL;
 	}
 
-	ret = rockchip_usb2phy_clk480m_register(rphy);
-	if (ret) {
-		dev_err(dev, "failed to register 480m output clock\n");
-		goto disable_clks;
-	}
+	if (rphy->phy_cfg->phy_tuning) {
+		ret = rphy->phy_cfg->phy_tuning(rphy);
+		if (ret)
+			goto disable_clks;
+	}
+
+	index = 0;
+	for_each_available_child_of_node(np, child_np) {
+		struct rockchip_usb2phy_port *rport = &rphy->ports[index];
+		struct phy *phy;
+
+		/* This driver aims to support both otg-port and host-port */
+		if (!of_node_name_eq(child_np, "host-port") &&
+		    !of_node_name_eq(child_np, "otg-port"))
+			goto next_child;
+
+		phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops);
+		if (IS_ERR(phy)) {
+			dev_err(dev, "failed to create phy\n");
+			ret = PTR_ERR(phy);
+			goto put_child;
+		}
+
+		rport->phy = phy;
+		phy_set_drvdata(rport->phy, rport);
+
+		/* initialize otg/host port separately */
+		if (of_node_name_eq(child_np, "host-port")) {
+			ret = rockchip_usb2phy_host_port_init(rphy, rport,
+							      child_np);
+			if (ret)
+				goto put_child;
+		} else {
+			ret = rockchip_usb2phy_otg_port_init(rphy, rport,
+							     child_np);
+			if (ret)
+				goto put_child;
+		}
+
+next_child:
+		/* to prevent out of boundary */
+		if (++index >= rphy->phy_cfg->num_ports)
+			break;
+	}
+
+	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	if (IS_ERR(provider)) {
+		dev_err(dev, "Failed to register phy provider\n");
+		ret = PTR_ERR(provider);
+		goto put_child;
+	}
+
+	/* Attributes */
+	ret = sysfs_create_group(&dev->kobj, &usb2_phy_attr_group);
+	if (ret) {
+		dev_err(dev, "Cannot create sysfs group: %d\n", ret);
+		goto put_child;
+	}
+
+	ret = rockchip_usb2phy_clk480m_register(rphy);
+	if (ret) {
+		dev_err(dev, "failed to register 480m output clock\n");
+		goto put_child;
+	}
+
+	if (rphy->irq > 0) {
+		ret = devm_request_threaded_irq(rphy->dev, rphy->irq, NULL,
+						rockchip_usb2phy_irq,
+						IRQF_ONESHOT,
+						"rockchip_usb2phy",
+						rphy);
+		if (ret) {
+			dev_err(rphy->dev,
+				"failed to request usb2 phy irq handle\n");
+			goto put_child;
+		}
+	}
+
+	if (of_property_read_bool(np, "wakeup-source"))
+		device_init_wakeup(rphy->dev, true);
+	else
+		device_init_wakeup(rphy->dev, false);
+
+	return 0;
+
+put_child:
+	of_node_put(child_np);
+disable_clks:
+	pm_runtime_put_sync(dev);
+	pm_runtime_disable(dev);
+	if (rphy->clk) {
+		clk_disable_unprepare(rphy->clk);
+		clk_put(rphy->clk);
+	}
+	return ret;
+}
+
+static int __maybe_unused
+rockchip_usb2phy_low_power_enable(struct rockchip_usb2phy *rphy,
+				  struct rockchip_usb2phy_port *rport,
+				  bool value)
+{
+	int ret = 0;
+
+	if (!rport->low_power_en)
+		return ret;
+
+	if (rport->port_id == USB2PHY_PORT_OTG) {
+		dev_info(&rport->phy->dev, "set otg port low power state %d\n",
+			 value);
+		ret = property_enable(rphy->grf, &rport->port_cfg->bypass_bc,
+				      value);
+		if (ret)
+			return ret;
+
+		ret = property_enable(rphy->grf, &rport->port_cfg->bypass_otg,
+				      value);
+		if (ret)
+			return ret;
+
+		ret = property_enable(rphy->grf, &rport->port_cfg->vbus_det_en,
+				      !value);
+	} else if (rport->port_id == USB2PHY_PORT_HOST) {
+		dev_info(&rport->phy->dev, "set host port low power state %d\n",
+			 value);
+
+		ret = property_enable(rphy->grf, &rport->port_cfg->bypass_host,
+				      value);
+	}
+
+	return ret;
+}
+
+static int rk312x_usb2phy_tuning(struct rockchip_usb2phy *rphy)
+{
+	int ret;
+
+	/* Turn off differential receiver in suspend mode */
+	ret = regmap_write(rphy->grf, 0x298, 0x00040000);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int rk3228_usb2phy_tuning(struct rockchip_usb2phy *rphy)
+{
+	int ret = 0;
+
+	/* Open pre-emphasize in non-chirp state for PHY0 otg port */
+	if (rphy->phy_cfg->reg == 0x760)
+		ret = regmap_write(rphy->grf, 0x76c, 0x00070004);
+
+	return ret;
+}
+
+static int rk3308_usb2phy_tuning(struct rockchip_usb2phy *rphy)
+{
+	int ret;
+
+	/* Open pre-emphasize in non-chirp state for otg port */
+	ret = regmap_write(rphy->grf, 0x0, 0x00070004);
+	if (ret)
+		return ret;
+
+	/* Open pre-emphasize in non-chirp state for host port */
+	ret = regmap_write(rphy->grf, 0x30, 0x00070004);
+	if (ret)
+		return ret;
+
+	/* Turn off differential receiver in suspend mode */
+	ret = regmap_write(rphy->grf, 0x18, 0x00040000);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int rk3328_usb2phy_tuning(struct rockchip_usb2phy *rphy)
+{
+	int ret;
+
+	/* Open debug mode for tuning */
+	ret = regmap_write(rphy->grf, 0x2c, 0xffff0400);
+	if (ret)
+		return ret;
+
+	/* Open pre-emphasize in non-chirp state for otg port */
+	ret = regmap_write(rphy->grf, 0x0, 0x00070004);
+	if (ret)
+		return ret;
+
+	/* Open pre-emphasize in non-chirp state for host port */
+	ret = regmap_write(rphy->grf, 0x30, 0x00070004);
+	if (ret)
+		return ret;
+
+	/* Turn off differential receiver in suspend mode */
+	ret = regmap_write(rphy->grf, 0x18, 0x00040000);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int rk3366_usb2phy_tuning(struct rockchip_usb2phy *rphy)
+{
+	unsigned int open_pre_emphasize = 0xffff851f;
+	unsigned int eye_height_tuning = 0xffff68c8;
+	unsigned int compensation_tuning = 0xffff026e;
+	int ret = 0;
+
+	/* open HS pre-emphasize to expand HS slew rate for each port. */
+	ret |= regmap_write(rphy->grf, 0x0780, open_pre_emphasize);
+	ret |= regmap_write(rphy->grf, 0x079c, eye_height_tuning);
+	ret |= regmap_write(rphy->grf, 0x07b0, open_pre_emphasize);
+	ret |= regmap_write(rphy->grf, 0x07cc, eye_height_tuning);
+
+	/* compensate default tuning reference relate to ODT and etc. */
+	ret |= regmap_write(rphy->grf, 0x078c, compensation_tuning);
+
+	return ret;
+}
+
+static int rk3399_usb2phy_tuning(struct rockchip_usb2phy *rphy)
+{
+	struct device_node *node = rphy->dev->of_node;
+	int ret = 0;
+
+	if (rphy->phy_cfg->reg == 0xe450) {
+		/*
+		 * Disable the pre-emphasize in eop state
+		 * and chirp state to avoid mis-trigger the
+		 * disconnect detection and also avoid hs
+		 * handshake fail for PHY0.
+		 */
+		ret |= regmap_write(rphy->grf, 0x4480,
+				    GENMASK(17, 16) | 0x0);
+		ret |= regmap_write(rphy->grf, 0x44b4,
+				    GENMASK(17, 16) | 0x0);
+	} else {
+		/*
+		 * Disable the pre-emphasize in eop state
+		 * and chirp state to avoid mis-trigger the
+		 * disconnect detection and also avoid hs
+		 * handshake fail for PHY1.
+		 */
+		ret |= regmap_write(rphy->grf, 0x4500,
+				    GENMASK(17, 16) | 0x0);
+		ret |= regmap_write(rphy->grf, 0x4534,
+				    GENMASK(17, 16) | 0x0);
+	}
+
+	if (!of_property_read_bool(node, "rockchip,u2phy-tuning"))
+		return ret;
+
+	if (rphy->phy_cfg->reg == 0xe450) {
+		/*
+		 * Set max ODT compensation voltage and
+		 * current tuning reference for PHY0.
+		 */
+		ret |= regmap_write(rphy->grf, 0x448c,
+				    GENMASK(23, 16) | 0xe3);
+
+		/* Set max pre-emphasis level for PHY0 */
+		ret |= regmap_write(rphy->grf, 0x44b0,
+				    GENMASK(18, 16) | 0x07);
+
+		/*
+		 * Set PHY0 A port squelch trigger point to 125mv
+		 */
+		ret |= regmap_write(rphy->grf, 0x4480,
+				    GENMASK(30, 30) | 0x4000);
+	} else {
+		/*
+		 * Set max ODT compensation voltage and
+		 * current tuning reference for PHY1.
+		 */
+		ret |= regmap_write(rphy->grf, 0x450c,
+				    GENMASK(23, 16) | 0xe3);
+
+		/* Set max pre-emphasis level for PHY1 */
+		ret |= regmap_write(rphy->grf, 0x4530,
+				    GENMASK(18, 16) | 0x07);
+
+		/*
+		 * Set PHY1 A port squelch trigger point to 125mv
+		 */
+		ret |= regmap_write(rphy->grf, 0x4500,
+				    GENMASK(30, 30) | 0x4000);
+	}
+
+	return ret;
+}
+
+static int rk3568_usb2phy_tuning(struct rockchip_usb2phy *rphy)
+{
+	u32 reg;
+	int ret = 0;
+
+	reg = readl(rphy->phy_base + 0x30);
+	/* turn off differential reciver in suspend mode */
+	writel(reg & ~BIT(2), rphy->phy_base + 0x30);
+
+	reg = readl(rphy->phy_base);
+	/* Enable otg port pre-emphasis during non-chirp phase */
+	reg &= ~(0x07 << 0);
+	reg |= (0x04 << 0);
+	writel(reg, rphy->phy_base);
+
+	reg = readl(rphy->phy_base + 0x0400);
+	/* Enable host port pre-emphasis during non-chirp phase */
+	reg &= ~(0x07 << 0);
+	reg |= (0x04 << 0);
+	writel(reg, rphy->phy_base + 0x0400);
+
+	if (rphy->phy_cfg->reg == 0xfe8a0000) {
+		/* Set otg port HS eye height to 437.5mv(default is 400mv) */
+		reg = readl(rphy->phy_base + 0x30);
+		reg &= ~(0x07 << 4);
+		reg |= (0x06 << 4);
+		writel(reg, rphy->phy_base + 0x30);
+
+		/*
+		 * Set the bvalid filter time to 10ms
+		 * based on the usb2 phy grf pclk 100MHz.
+		 */
+		ret |= regmap_write(rphy->grf, 0x0048, FILTER_COUNTER);
+
+		/*
+		 * Set the id filter time to 10ms based
+		 * on the usb2 phy grf pclk 100MHz.
+		 */
+		ret |= regmap_write(rphy->grf, 0x004c, FILTER_COUNTER);
+	}
+
+	return ret;
+}
+
+static int rk3568_vbus_detect_control(struct rockchip_usb2phy *rphy, bool en)
+{
+	u32 reg;
+
+	if (en) {
+		reg = readl(rphy->phy_base + 0x3c);
+		/* Enable vbus voltage level detection function */
+		writel(reg & ~BIT(7), rphy->phy_base + 0x3c);
+	} else {
+		reg = readl(rphy->phy_base + 0x3c);
+		/* Disable vbus voltage level detection function */
+		writel(reg | BIT(7), rphy->phy_base + 0x3c);
+	}
+
+	return 0;
+}
+
+static int rk3588_usb2phy_tuning(struct rockchip_usb2phy *rphy)
+{
+	int ret = 0;
+
+	/* Deassert SIDDQ to power on analog block */
+	ret = regmap_write(rphy->grf, 0x0008,
+			   GENMASK(29, 29) | 0x0000);
+	if (ret)
+		return ret;
+
+	/* Do reset after exit IDDQ mode */
+	ret = rockchip_usb2phy_reset(rphy);
+	if (ret)
+		return ret;
+
+	if (rphy->phy_cfg->reg == 0x0000) {
+		/*
+		 * Set USB2 PHY0 suspend configuration for USB3_0
+		 * 1. Set utmi_termselect to 1'b1 (en FS terminations)
+		 * 2. Set utmi_xcvrselect to 2'b01 (FS transceiver)
+		 * 3. Set utmi_opmode to 2'b01 (no-driving)
+		 */
+		ret |= regmap_write(rphy->grf, 0x000c,
+				    GENMASK(20, 16) | 0x0015);
+
+		/* HS DC Voltage Level Adjustment 4'b1001 : +5.89% */
+		ret |= regmap_write(rphy->grf, 0x0004,
+				   GENMASK(27, 24) | 0x0900);
 
-	index = 0;
-	for_each_available_child_of_node(np, child_np) {
-		struct rockchip_usb2phy_port *rport = &rphy->ports[index];
-		struct phy *phy;
+		/* HS Transmitter Pre-Emphasis Current Control 2'b10 : 2x */
+		ret |= regmap_write(rphy->grf, 0x0008,
+				   GENMASK(20, 19) | 0x0010);
 
-		/* This driver aims to support both otg-port and host-port */
-		if (!of_node_name_eq(child_np, "host-port") &&
-		    !of_node_name_eq(child_np, "otg-port"))
-			goto next_child;
+		/* Pullup iddig pin for USB3_0 OTG mode */
+		ret |= regmap_write(rphy->grf, 0x0010,
+				    GENMASK(17, 16) | 0x0003);
+	} else if (rphy->phy_cfg->reg == 0x4000) {
+		/*
+		 * Set USB2 PHY1 suspend configuration for USB3_1
+		 * 1. Set utmi_termselect to 1'b1 (en FS terminations)
+		 * 2. Set utmi_xcvrselect to 2'b01(FS transceiver)
+		 * 3. Set utmi_opmode to 2'b01 (no-driving)
+		 */
+		ret |= regmap_write(rphy->grf, 0x000c,
+				    GENMASK(20, 16) | 0x0015);
 
-		phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops);
-		if (IS_ERR(phy)) {
-			dev_err(dev, "failed to create phy\n");
-			ret = PTR_ERR(phy);
-			goto put_child;
+		/* HS DC Voltage Level Adjustment 4'b1001 : +5.89% */
+		ret |= regmap_write(rphy->grf, 0x0004,
+				   GENMASK(27, 24) | 0x0900);
+
+		/* HS Transmitter Pre-Emphasis Current Control 2'b10 : 2x */
+		ret |= regmap_write(rphy->grf, 0x0008,
+				   GENMASK(20, 19) | 0x0010);
+
+		/* Pullup iddig pin for USB3_1 OTG mode */
+		ret |= regmap_write(rphy->grf, 0x0010,
+				    GENMASK(17, 16) | 0x0003);
+	} else if (rphy->phy_cfg->reg == 0x8000) {
+		/*
+		 * Set USB2 PHY2 suspend configuration for USB2_0
+		 * 1. Set utmi_termselect to 1'b1 (en FS terminations)
+		 * 2. Set utmi_xcvrselect to 2'b01(FS transceiver)
+		 * 3. Set utmi_opmode to 2'b00 (normal)
+		 */
+		ret |= regmap_write(rphy->grf, 0x000c,
+				    GENMASK(20, 16) | 0x0014);
+
+		/* HS DC Voltage Level Adjustment 4'b1001 : +5.89% */
+		ret |= regmap_write(rphy->grf, 0x0004,
+				   GENMASK(27, 24) | 0x0900);
+
+		/* HS Transmitter Pre-Emphasis Current Control 2'b10 : 2x */
+		ret |= regmap_write(rphy->grf, 0x0008,
+				   GENMASK(20, 19) | 0x0010);
+	} else if (rphy->phy_cfg->reg == 0xc000) {
+		/*
+		 * Set USB2 PHY3 suspend configuration for USB2_1
+		 * 1. Set utmi_termselect to 1'b1 (en FS terminations)
+		 * 2. Set utmi_xcvrselect to 2'b01(FS transceiver)
+		 * 3. Set utmi_opmode to 2'b00 (normal)
+		 */
+		ret |= regmap_write(rphy->grf, 0x000c,
+				    GENMASK(20, 16) | 0x0014);
+
+		/* HS DC Voltage Level Adjustment 4'b1001 : +5.89% */
+		ret |= regmap_write(rphy->grf, 0x0004,
+				   GENMASK(27, 24) | 0x0900);
+
+		/* HS Transmitter Pre-Emphasis Current Control 2'b10 : 2x */
+		ret |= regmap_write(rphy->grf, 0x0008,
+				   GENMASK(20, 19) | 0x0010);
+	}
+
+	return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int rockchip_usb2phy_pm_suspend(struct device *dev)
+{
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(dev);
+	struct rockchip_usb2phy_port *rport;
+	unsigned int index;
+	int ret = 0;
+	bool wakeup_enable = false;
+
+	if (device_may_wakeup(rphy->dev))
+		wakeup_enable = true;
+
+	for (index = 0; index < rphy->phy_cfg->num_ports; index++) {
+		rport = &rphy->ports[index];
+		if (!rport->phy)
+			continue;
+
+		if (rport->port_id == USB2PHY_PORT_OTG &&
+		    (rport->id_irq > 0 || rphy->irq > 0)) {
+			mutex_lock(&rport->mutex);
+			rport->prev_iddig = property_enabled(rphy->grf,
+						&rport->port_cfg->utmi_iddig);
+			ret = rockchip_usb2phy_enable_id_irq(rphy, rport,
+							     false);
+			mutex_unlock(&rport->mutex);
+			if (ret) {
+				dev_err(rphy->dev,
+					"failed to disable id irq\n");
+				return ret;
+			}
 		}
 
-		rport->phy = phy;
-		phy_set_drvdata(rport->phy, rport);
+		if (rport->port_id == USB2PHY_PORT_OTG && wakeup_enable &&
+		    rport->bvalid_irq > 0)
+			enable_irq_wake(rport->bvalid_irq);
 
-		/* initialize otg/host port separately */
-		if (of_node_name_eq(child_np, "host-port")) {
-			ret = rockchip_usb2phy_host_port_init(rphy, rport,
-							      child_np);
-			if (ret)
-				goto put_child;
-		} else {
-			ret = rockchip_usb2phy_otg_port_init(rphy, rport,
-							     child_np);
-			if (ret)
-				goto put_child;
+		/* activate the linestate to detect the next interrupt. */
+		mutex_lock(&rport->mutex);
+		ret = rockchip_usb2phy_enable_line_irq(rphy, rport, true);
+		mutex_unlock(&rport->mutex);
+		if (ret) {
+			dev_err(rphy->dev, "failed to enable linestate irq\n");
+			return ret;
 		}
 
-next_child:
-		/* to prevent out of boundary */
-		if (++index >= rphy->phy_cfg->num_ports)
-			break;
+		if (wakeup_enable && rport->ls_irq > 0)
+			enable_irq_wake(rport->ls_irq);
+
+		/* enter low power state */
+		rockchip_usb2phy_low_power_enable(rphy, rport, true);
 	}
 
-	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
-	return PTR_ERR_OR_ZERO(provider);
+	return ret;
+}
 
-put_child:
-	of_node_put(child_np);
-disable_clks:
-	if (rphy->clk) {
-		clk_disable_unprepare(rphy->clk);
-		clk_put(rphy->clk);
+static int rockchip_usb2phy_pm_resume(struct device *dev)
+{
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(dev);
+	struct rockchip_usb2phy_port *rport;
+	unsigned int index;
+	bool iddig;
+	int ret = 0;
+	bool wakeup_enable = false;
+
+	if (device_may_wakeup(rphy->dev))
+		wakeup_enable = true;
+
+	if (rphy->phy_cfg->phy_tuning)
+		ret = rphy->phy_cfg->phy_tuning(rphy);
+
+	for (index = 0; index < rphy->phy_cfg->num_ports; index++) {
+		rport = &rphy->ports[index];
+		if (!rport->phy)
+			continue;
+
+		if (rport->port_id == USB2PHY_PORT_OTG &&
+		    (rport->id_irq > 0 || rphy->irq > 0)) {
+			mutex_lock(&rport->mutex);
+			iddig = property_enabled(rphy->grf,
+						 &rport->port_cfg->utmi_iddig);
+			ret = rockchip_usb2phy_enable_id_irq(rphy, rport,
+							     true);
+			mutex_unlock(&rport->mutex);
+			if (ret) {
+				dev_err(rphy->dev,
+					"failed to enable id irq\n");
+				return ret;
+			}
+
+			if (iddig != rport->prev_iddig) {
+				dev_dbg(&rport->phy->dev,
+					"iddig changed during resume\n");
+				rport->prev_iddig = iddig;
+				extcon_set_state_sync(rphy->edev,
+						      EXTCON_USB_HOST,
+						      !iddig);
+				extcon_set_state_sync(rphy->edev,
+						      EXTCON_USB_VBUS_EN,
+						      !iddig);
+				ret = rockchip_set_vbus_power(rport, !iddig);
+				if (ret)
+					return ret;
+			}
+		}
+
+		if (rport->port_id == USB2PHY_PORT_OTG && wakeup_enable &&
+		    rport->bvalid_irq > 0)
+			disable_irq_wake(rport->bvalid_irq);
+
+		if (wakeup_enable && rport->ls_irq > 0)
+			disable_irq_wake(rport->ls_irq);
+
+		/* exit low power state */
+		rockchip_usb2phy_low_power_enable(rphy, rport, false);
 	}
+
 	return ret;
 }
 
+static const struct dev_pm_ops rockchip_usb2phy_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(rockchip_usb2phy_pm_suspend,
+				rockchip_usb2phy_pm_resume)
+};
+
+#define ROCKCHIP_USB2PHY_DEV_PM	(&rockchip_usb2phy_dev_pm_ops)
+#else
+#define ROCKCHIP_USB2PHY_DEV_PM	NULL
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct rockchip_usb2phy_cfg rk1808_phy_cfgs[] = {
+	{
+		.reg = 0x100,
+		.num_ports	= 2,
+		.clkout_ctl	= { 0x108, 4, 4, 1, 0 },
+		.port_cfgs	= {
+			[USB2PHY_PORT_OTG] = {
+				.phy_sus	= { 0x0100, 8, 0, 0, 0x1d1 },
+				.bvalid_det_en	= { 0x0110, 2, 2, 0, 1 },
+				.bvalid_det_st	= { 0x0114, 2, 2, 0, 1 },
+				.bvalid_det_clr = { 0x0118, 2, 2, 0, 1 },
+				.bypass_dm_en	= { 0x0108, 2, 2, 0, 1},
+				.bypass_sel	= { 0x0108, 3, 3, 0, 1},
+				.iddig_output	= { 0x0100, 10, 10, 0, 1 },
+				.iddig_en	= { 0x0100, 9, 9, 0, 1 },
+				.idfall_det_en	= { 0x0110, 5, 5, 0, 1 },
+				.idfall_det_st	= { 0x0114, 5, 5, 0, 1 },
+				.idfall_det_clr = { 0x0118, 5, 5, 0, 1 },
+				.idrise_det_en	= { 0x0110, 4, 4, 0, 1 },
+				.idrise_det_st	= { 0x0114, 4, 4, 0, 1 },
+				.idrise_det_clr = { 0x0118, 4, 4, 0, 1 },
+				.ls_det_en	= { 0x0110, 0, 0, 0, 1 },
+				.ls_det_st	= { 0x0114, 0, 0, 0, 1 },
+				.ls_det_clr	= { 0x0118, 0, 0, 0, 1 },
+				.utmi_avalid	= { 0x0120, 10, 10, 0, 1 },
+				.utmi_bvalid	= { 0x0120, 9, 9, 0, 1 },
+				.utmi_iddig	= { 0x0120, 6, 6, 0, 1 },
+				.utmi_ls	= { 0x0120, 5, 4, 0, 1 },
+				.vbus_det_en	= { 0x001c, 15, 15, 1, 0 },
+			},
+			[USB2PHY_PORT_HOST] = {
+				.phy_sus	= { 0x104, 8, 0, 0, 0x1d1 },
+				.ls_det_en	= { 0x110, 1, 1, 0, 1 },
+				.ls_det_st	= { 0x114, 1, 1, 0, 1 },
+				.ls_det_clr	= { 0x118, 1, 1, 0, 1 },
+				.utmi_ls	= { 0x120, 17, 16, 0, 1 },
+				.utmi_hstdet	= { 0x120, 19, 19, 0, 1 }
+			}
+		},
+		.chg_det = {
+			.chg_mode	= { 0x0100, 8, 0, 0, 0x1d7 },
+			.cp_det		= { 0x0120, 24, 24, 0, 1 },
+			.dcp_det	= { 0x0120, 23, 23, 0, 1 },
+			.dp_det		= { 0x0120, 25, 25, 0, 1 },
+			.idm_sink_en	= { 0x0108, 8, 8, 0, 1 },
+			.idp_sink_en	= { 0x0108, 7, 7, 0, 1 },
+			.idp_src_en	= { 0x0108, 9, 9, 0, 1 },
+			.rdm_pdwn_en	= { 0x0108, 10, 10, 0, 1 },
+			.vdm_src_en	= { 0x0108, 12, 12, 0, 1 },
+			.vdp_src_en	= { 0x0108, 11, 11, 0, 1 },
+		},
+	},
+	{ /* sentinel */ }
+};
+
+static const struct rockchip_usb2phy_cfg rk312x_phy_cfgs[] = {
+	{
+		.reg = 0x17c,
+		.num_ports	= 2,
+		.phy_tuning	= rk312x_usb2phy_tuning,
+		.clkout_ctl	= { 0x0190, 15, 15, 1, 0 },
+		.port_cfgs	= {
+			[USB2PHY_PORT_OTG] = {
+				.phy_sus	= { 0x017c, 8, 0, 0, 0x1d1 },
+				.bvalid_det_en	= { 0x017c, 14, 14, 0, 1 },
+				.bvalid_det_st	= { 0x017c, 15, 15, 0, 1 },
+				.bvalid_det_clr	= { 0x017c, 15, 15, 0, 1 },
+				.bypass_dm_en	= { 0x0190, 12, 12, 0, 1},
+				.bypass_sel	= { 0x0190, 13, 13, 0, 1},
+				.iddig_output	= { 0x017c, 10, 10, 0, 1 },
+				.iddig_en	= { 0x017c, 9, 9, 0, 1 },
+				.idfall_det_en  = { 0x01a0, 2, 2, 0, 1 },
+				.idfall_det_st  = { 0x01a0, 3, 3, 0, 1 },
+				.idfall_det_clr = { 0x01a0, 3, 3, 0, 1 },
+				.idrise_det_en  = { 0x01a0, 0, 0, 0, 1 },
+				.idrise_det_st  = { 0x01a0, 1, 1, 0, 1 },
+				.idrise_det_clr = { 0x01a0, 1, 1, 0, 1 },
+				.ls_det_en	= { 0x017c, 12, 12, 0, 1 },
+				.ls_det_st	= { 0x017c, 13, 13, 0, 1 },
+				.ls_det_clr	= { 0x017c, 13, 13, 0, 1 },
+				.utmi_bvalid	= { 0x014c, 5, 5, 0, 1 },
+				.utmi_iddig	= { 0x014c, 8, 8, 0, 1 },
+				.utmi_ls	= { 0x014c, 7, 6, 0, 1 },
+			},
+			[USB2PHY_PORT_HOST] = {
+				.phy_sus	= { 0x0194, 8, 0, 0, 0x1d1 },
+				.ls_det_en	= { 0x0194, 14, 14, 0, 1 },
+				.ls_det_st	= { 0x0194, 15, 15, 0, 1 },
+				.ls_det_clr	= { 0x0194, 15, 15, 0, 1 }
+			}
+		},
+		.chg_det = {
+			.chg_mode	= { 0x017c, 8, 0, 0, 0x1d7 },
+			.cp_det		= { 0x02c0, 6, 6, 0, 1 },
+			.dcp_det	= { 0x02c0, 5, 5, 0, 1 },
+			.dp_det		= { 0x02c0, 7, 7, 0, 1 },
+			.idm_sink_en	= { 0x0184, 8, 8, 0, 1 },
+			.idp_sink_en	= { 0x0184, 7, 7, 0, 1 },
+			.idp_src_en	= { 0x0184, 9, 9, 0, 1 },
+			.rdm_pdwn_en	= { 0x0184, 10, 10, 0, 1 },
+			.vdm_src_en	= { 0x0184, 12, 12, 0, 1 },
+			.vdp_src_en	= { 0x0184, 11, 11, 0, 1 },
+		},
+	},
+	{ /* sentinel */ }
+};
+
 static const struct rockchip_usb2phy_cfg rk3228_phy_cfgs[] = {
 	{
 		.reg = 0x760,
 		.num_ports	= 2,
+		.phy_tuning	= rk3228_usb2phy_tuning,
 		.clkout_ctl	= { 0x0768, 4, 4, 1, 0 },
 		.port_cfgs	= {
 			[USB2PHY_PORT_OTG] = {
-				.phy_sus	= { 0x0760, 15, 0, 0, 0x1d1 },
+				.phy_sus	= { 0x0760, 8, 0, 0, 0x1d1 },
 				.bvalid_det_en	= { 0x0680, 3, 3, 0, 1 },
 				.bvalid_det_st	= { 0x0690, 3, 3, 0, 1 },
 				.bvalid_det_clr	= { 0x06a0, 3, 3, 0, 1 },
+				.iddig_output   = { 0x0760, 10, 10, 0, 1 },
+				.iddig_en       = { 0x0760, 9, 9, 0, 1 },
+				.idfall_det_en	= { 0x0680, 6, 6, 0, 1 },
+				.idfall_det_st	= { 0x0690, 6, 6, 0, 1 },
+				.idfall_det_clr	= { 0x06a0, 6, 6, 0, 1 },
+				.idrise_det_en	= { 0x0680, 5, 5, 0, 1 },
+				.idrise_det_st	= { 0x0690, 5, 5, 0, 1 },
+				.idrise_det_clr	= { 0x06a0, 5, 5, 0, 1 },
 				.ls_det_en	= { 0x0680, 2, 2, 0, 1 },
 				.ls_det_st	= { 0x0690, 2, 2, 0, 1 },
 				.ls_det_clr	= { 0x06a0, 2, 2, 0, 1 },
 				.utmi_bvalid	= { 0x0480, 4, 4, 0, 1 },
+				.utmi_iddig	= { 0x0480, 1, 1, 0, 1 },
 				.utmi_ls	= { 0x0480, 3, 2, 0, 1 },
+				.vbus_det_en	= { 0x0788, 15, 15, 1, 0 },
 			},
 			[USB2PHY_PORT_HOST] = {
-				.phy_sus	= { 0x0764, 15, 0, 0, 0x1d1 },
+				.phy_sus	= { 0x0764, 8, 0, 0, 0x1d1 },
 				.ls_det_en	= { 0x0680, 4, 4, 0, 1 },
 				.ls_det_st	= { 0x0690, 4, 4, 0, 1 },
 				.ls_det_clr	= { 0x06a0, 4, 4, 0, 1 }
 			}
 		},
 		.chg_det = {
-			.opmode		= { 0x0760, 3, 0, 5, 1 },
+			.chg_mode	= { 0x0760, 8, 0, 0, 0x1d7 },
 			.cp_det		= { 0x0884, 4, 4, 0, 1 },
 			.dcp_det	= { 0x0884, 3, 3, 0, 1 },
 			.dp_det		= { 0x0884, 5, 5, 0, 1 },
@@ -1240,18 +2941,72 @@ static const struct rockchip_usb2phy_cfg rk3228_phy_cfgs[] = {
 		.clkout_ctl	= { 0x0808, 4, 4, 1, 0 },
 		.port_cfgs	= {
 			[USB2PHY_PORT_OTG] = {
-				.phy_sus	= { 0x800, 15, 0, 0, 0x1d1 },
+				.phy_sus	= { 0x804, 8, 0, 0, 0x1d1 },
+				.ls_det_en	= { 0x0684, 1, 1, 0, 1 },
+				.ls_det_st	= { 0x0694, 1, 1, 0, 1 },
+				.ls_det_clr	= { 0x06a4, 1, 1, 0, 1 }
+			},
+			[USB2PHY_PORT_HOST] = {
+				.phy_sus	= { 0x800, 8, 0, 0, 0x1d1 },
 				.ls_det_en	= { 0x0684, 0, 0, 0, 1 },
 				.ls_det_st	= { 0x0694, 0, 0, 0, 1 },
 				.ls_det_clr	= { 0x06a4, 0, 0, 0, 1 }
+			}
+		},
+	},
+	{ /* sentinel */ }
+};
+
+static const struct rockchip_usb2phy_cfg rk3308_phy_cfgs[] = {
+	{
+		.reg = 0x100,
+		.num_ports	= 2,
+		.phy_tuning	= rk3308_usb2phy_tuning,
+		.clkout_ctl	= { 0x0108, 4, 4, 1, 0 },
+		.port_cfgs	= {
+			[USB2PHY_PORT_OTG] = {
+				.phy_sus	= { 0x0100, 8, 0, 0, 0x1d1 },
+				.bvalid_det_en	= { 0x3020, 2, 2, 0, 1 },
+				.bvalid_det_st	= { 0x3024, 2, 2, 0, 1 },
+				.bvalid_det_clr = { 0x3028, 2, 2, 0, 1 },
+				.iddig_output	= { 0x0100, 10, 10, 0, 1 },
+				.iddig_en	= { 0x0100, 9, 9, 0, 1 },
+				.idfall_det_en	= { 0x3020, 5, 5, 0, 1 },
+				.idfall_det_st	= { 0x3024, 5, 5, 0, 1 },
+				.idfall_det_clr = { 0x3028, 5, 5, 0, 1 },
+				.idrise_det_en	= { 0x3020, 4, 4, 0, 1 },
+				.idrise_det_st	= { 0x3024, 4, 4, 0, 1 },
+				.idrise_det_clr = { 0x3028, 4, 4, 0, 1 },
+				.ls_det_en	= { 0x3020, 0, 0, 0, 1 },
+				.ls_det_st	= { 0x3024, 0, 0, 0, 1 },
+				.ls_det_clr	= { 0x3028, 0, 0, 0, 1 },
+				.utmi_avalid	= { 0x0120, 10, 10, 0, 1 },
+				.utmi_bvalid	= { 0x0120, 9, 9, 0, 1 },
+				.utmi_iddig	= { 0x0120, 6, 6, 0, 1 },
+				.utmi_ls	= { 0x0120, 5, 4, 0, 1 },
+				.vbus_det_en	= { 0x001c, 15, 15, 1, 0 },
 			},
 			[USB2PHY_PORT_HOST] = {
-				.phy_sus	= { 0x804, 15, 0, 0, 0x1d1 },
-				.ls_det_en	= { 0x0684, 1, 1, 0, 1 },
-				.ls_det_st	= { 0x0694, 1, 1, 0, 1 },
-				.ls_det_clr	= { 0x06a4, 1, 1, 0, 1 }
+				.phy_sus	= { 0x0104, 8, 0, 0, 0x1d1 },
+				.ls_det_en	= { 0x3020, 1, 1, 0, 1 },
+				.ls_det_st	= { 0x3024, 1, 1, 0, 1 },
+				.ls_det_clr	= { 0x3028, 1, 1, 0, 1 },
+				.utmi_ls	= { 0x120, 17, 16, 0, 1 },
+				.utmi_hstdet	= { 0x120, 19, 19, 0, 1 }
 			}
 		},
+		.chg_det = {
+			.chg_mode	= { 0x0100, 8, 0, 0, 0x1d7 },
+			.cp_det		= { 0x0120, 24, 24, 0, 1 },
+			.dcp_det	= { 0x0120, 23, 23, 0, 1 },
+			.dp_det		= { 0x0120, 25, 25, 0, 1 },
+			.idm_sink_en	= { 0x0108, 8, 8, 0, 1 },
+			.idp_sink_en	= { 0x0108, 7, 7, 0, 1 },
+			.idp_src_en	= { 0x0108, 9, 9, 0, 1 },
+			.rdm_pdwn_en	= { 0x0108, 10, 10, 0, 1 },
+			.vdm_src_en	= { 0x0108, 12, 12, 0, 1 },
+			.vdp_src_en	= { 0x0108, 11, 11, 0, 1 },
+		},
 	},
 	{ /* sentinel */ }
 };
@@ -1260,22 +3015,36 @@ static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = {
 	{
 		.reg = 0x100,
 		.num_ports	= 2,
+		.phy_tuning = rk3328_usb2phy_tuning,
 		.clkout_ctl	= { 0x108, 4, 4, 1, 0 },
 		.port_cfgs	= {
 			[USB2PHY_PORT_OTG] = {
-				.phy_sus	= { 0x0100, 15, 0, 0, 0x1d1 },
+				.phy_sus	= { 0x0100, 8, 0, 0, 0x1d1 },
 				.bvalid_det_en	= { 0x0110, 2, 2, 0, 1 },
 				.bvalid_det_st	= { 0x0114, 2, 2, 0, 1 },
 				.bvalid_det_clr = { 0x0118, 2, 2, 0, 1 },
+				.bypass_bc      = { 0x0008, 14, 14, 0, 1 },
+				.bypass_otg     = { 0x0018, 15, 15, 1, 0 },
+				.iddig_output   = { 0x0100, 10, 10, 0, 1 },
+				.iddig_en       = { 0x0100, 9, 9, 0, 1 },
+				.idfall_det_en	= { 0x0110, 5, 5, 0, 1 },
+				.idfall_det_st	= { 0x0114, 5, 5, 0, 1 },
+				.idfall_det_clr = { 0x0118, 5, 5, 0, 1 },
+				.idrise_det_en	= { 0x0110, 4, 4, 0, 1 },
+				.idrise_det_st	= { 0x0114, 4, 4, 0, 1 },
+				.idrise_det_clr = { 0x0118, 4, 4, 0, 1 },
 				.ls_det_en	= { 0x0110, 0, 0, 0, 1 },
 				.ls_det_st	= { 0x0114, 0, 0, 0, 1 },
 				.ls_det_clr	= { 0x0118, 0, 0, 0, 1 },
 				.utmi_avalid	= { 0x0120, 10, 10, 0, 1 },
 				.utmi_bvalid	= { 0x0120, 9, 9, 0, 1 },
+				.utmi_iddig	= { 0x0120, 6, 6, 0, 1 },
 				.utmi_ls	= { 0x0120, 5, 4, 0, 1 },
+				.vbus_det_en	= { 0x001c, 15, 15, 1, 0 },
 			},
 			[USB2PHY_PORT_HOST] = {
-				.phy_sus	= { 0x104, 15, 0, 0, 0x1d1 },
+				.phy_sus	= { 0x104, 8, 0, 0, 0x1d1 },
+				.bypass_host	= { 0x048, 15, 15, 1, 0 },
 				.ls_det_en	= { 0x110, 1, 1, 0, 1 },
 				.ls_det_st	= { 0x114, 1, 1, 0, 1 },
 				.ls_det_clr	= { 0x118, 1, 1, 0, 1 },
@@ -1284,7 +3053,7 @@ static const struct rockchip_usb2phy_cfg rk3328_phy_cfgs[] = {
 			}
 		},
 		.chg_det = {
-			.opmode		= { 0x0100, 3, 0, 5, 1 },
+			.chg_mode	= { 0x0100, 8, 0, 0, 0x1d7 },
 			.cp_det		= { 0x0120, 24, 24, 0, 1 },
 			.dcp_det	= { 0x0120, 23, 23, 0, 1 },
 			.dp_det		= { 0x0120, 25, 25, 0, 1 },
@@ -1303,10 +3072,11 @@ static const struct rockchip_usb2phy_cfg rk3366_phy_cfgs[] = {
 	{
 		.reg = 0x700,
 		.num_ports	= 2,
+		.phy_tuning	= rk3366_usb2phy_tuning,
 		.clkout_ctl	= { 0x0724, 15, 15, 1, 0 },
 		.port_cfgs	= {
 			[USB2PHY_PORT_HOST] = {
-				.phy_sus	= { 0x0728, 15, 0, 0, 0x1d1 },
+				.phy_sus	= { 0x0728, 8, 0, 0, 0x1d1 },
 				.ls_det_en	= { 0x0680, 4, 4, 0, 1 },
 				.ls_det_st	= { 0x0690, 4, 4, 0, 1 },
 				.ls_det_clr	= { 0x06a0, 4, 4, 0, 1 },
@@ -1318,19 +3088,86 @@ static const struct rockchip_usb2phy_cfg rk3366_phy_cfgs[] = {
 	{ /* sentinel */ }
 };
 
+static const struct rockchip_usb2phy_cfg rk3368_phy_cfgs[] = {
+	{
+		.reg = 0x700,
+		.num_ports	= 2,
+		.clkout_ctl	= { 0x0724, 15, 15, 1, 0 },
+		.port_cfgs	= {
+			[USB2PHY_PORT_OTG] = {
+				.phy_sus	= { 0x0700, 8, 0, 0, 0x1d1 },
+				.bvalid_det_en	= { 0x0680, 3, 3, 0, 1 },
+				.bvalid_det_st	= { 0x0690, 3, 3, 0, 1 },
+				.bvalid_det_clr = { 0x06a0, 3, 3, 0, 1 },
+				.iddig_output	= { 0x0700, 10, 10, 0, 1 },
+				.iddig_en	= { 0x0700, 9, 9, 0, 1 },
+				.idfall_det_en	= { 0x0680, 6, 6, 0, 1 },
+				.idfall_det_st	= { 0x0690, 6, 6, 0, 1 },
+				.idfall_det_clr	= { 0x06a0, 6, 6, 0, 1 },
+				.idrise_det_en	= { 0x0680, 5, 5, 0, 1 },
+				.idrise_det_st	= { 0x0690, 5, 5, 0, 1 },
+				.idrise_det_clr	= { 0x06a0, 5, 5, 0, 1 },
+				.ls_det_en	= { 0x0680, 2, 2, 0, 1 },
+				.ls_det_st	= { 0x0690, 2, 2, 0, 1 },
+				.ls_det_clr	= { 0x06a0, 2, 2, 0, 1 },
+				.utmi_bvalid	= { 0x04bc, 23, 23, 0, 1 },
+				.utmi_iddig     = { 0x04bc, 26, 26, 0, 1 },
+				.utmi_ls	= { 0x04bc, 25, 24, 0, 1 },
+				.vbus_det_en    = { 0x079c, 15, 15, 1, 0 },
+			},
+			[USB2PHY_PORT_HOST] = {
+				.phy_sus	= { 0x0728, 15, 0, 0, 0x1d1 },
+				.ls_det_en	= { 0x0680, 4, 4, 0, 1 },
+				.ls_det_st	= { 0x0690, 4, 4, 0, 1 },
+				.ls_det_clr	= { 0x06a0, 4, 4, 0, 1 }
+			}
+		},
+		.chg_det = {
+			.chg_mode	= { 0x0700, 8, 0, 0, 0x1d7 },
+			.cp_det		= { 0x04b8, 30, 30, 0, 1 },
+			.dcp_det	= { 0x04b8, 29, 29, 0, 1 },
+			.dp_det		= { 0x04b8, 31, 31, 0, 1 },
+			.idm_sink_en	= { 0x0718, 8, 8, 0, 1 },
+			.idp_sink_en	= { 0x0718, 7, 7, 0, 1 },
+			.idp_src_en	= { 0x0718, 9, 9, 0, 1 },
+			.rdm_pdwn_en	= { 0x0718, 10, 10, 0, 1 },
+			.vdm_src_en	= { 0x0718, 12, 12, 0, 1 },
+			.vdp_src_en	= { 0x0718, 11, 11, 0, 1 },
+		},
+	},
+	{ /* sentinel */ }
+};
+
 static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
 	{
 		.reg		= 0xe450,
 		.num_ports	= 2,
+		.phy_tuning	= rk3399_usb2phy_tuning,
 		.clkout_ctl	= { 0xe450, 4, 4, 1, 0 },
 		.port_cfgs	= {
 			[USB2PHY_PORT_OTG] = {
-				.phy_sus	= { 0xe454, 1, 0, 2, 1 },
+				.phy_sus = { 0xe454, 8, 0, 0x052, 0x1d1 },
 				.bvalid_det_en	= { 0xe3c0, 3, 3, 0, 1 },
 				.bvalid_det_st	= { 0xe3e0, 3, 3, 0, 1 },
 				.bvalid_det_clr	= { 0xe3d0, 3, 3, 0, 1 },
+				.bypass_dm_en   = { 0xe450, 2, 2, 0, 1 },
+				.bypass_sel     = { 0xe450, 3, 3, 0, 1 },
+				.iddig_output	= { 0xe454, 10, 10, 0, 1 },
+				.iddig_en	= { 0xe454, 9, 9, 0, 1 },
+				.idfall_det_en	= { 0xe3c0, 5, 5, 0, 1 },
+				.idfall_det_st	= { 0xe3e0, 5, 5, 0, 1 },
+				.idfall_det_clr	= { 0xe3d0, 5, 5, 0, 1 },
+				.idrise_det_en	= { 0xe3c0, 4, 4, 0, 1 },
+				.idrise_det_st	= { 0xe3e0, 4, 4, 0, 1 },
+				.idrise_det_clr	= { 0xe3d0, 4, 4, 0, 1 },
+				.ls_det_en	= { 0xe3c0, 2, 2, 0, 1 },
+				.ls_det_st	= { 0xe3e0, 2, 2, 0, 1 },
+				.ls_det_clr	= { 0xe3d0, 2, 2, 0, 1 },
 				.utmi_avalid	= { 0xe2ac, 7, 7, 0, 1 },
 				.utmi_bvalid	= { 0xe2ac, 12, 12, 0, 1 },
+				.utmi_iddig     = { 0xe2ac, 8, 8, 0, 1 },
+				.utmi_ls	= { 0xe2ac, 14, 13, 0, 1 },
+				.vbus_det_en    = { 0x449c, 15, 15, 1, 0 },
 			},
 			[USB2PHY_PORT_HOST] = {
 				.phy_sus	= { 0xe458, 1, 0, 0x2, 0x1 },
@@ -1342,7 +3179,7 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
 			}
 		},
 		.chg_det = {
-			.opmode		= { 0xe454, 3, 0, 5, 1 },
+			.chg_mode	= { 0xe454, 8, 0, 0, 0x1d7 },
 			.cp_det		= { 0xe2ac, 2, 2, 0, 1 },
 			.dcp_det	= { 0xe2ac, 1, 1, 0, 1 },
 			.dp_det		= { 0xe2ac, 0, 0, 0, 1 },
@@ -1357,15 +3194,30 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
 	{
 		.reg		= 0xe460,
 		.num_ports	= 2,
+		.phy_tuning	= rk3399_usb2phy_tuning,
 		.clkout_ctl	= { 0xe460, 4, 4, 1, 0 },
 		.port_cfgs	= {
 			[USB2PHY_PORT_OTG] = {
-				.phy_sus        = { 0xe464, 1, 0, 2, 1 },
+				.phy_sus = { 0xe464, 8, 0, 0x052, 0x1d1 },
 				.bvalid_det_en  = { 0xe3c0, 8, 8, 0, 1 },
 				.bvalid_det_st  = { 0xe3e0, 8, 8, 0, 1 },
 				.bvalid_det_clr = { 0xe3d0, 8, 8, 0, 1 },
+				.iddig_output	= { 0xe464, 10, 10, 0, 1 },
+				.iddig_en	= { 0xe464, 9, 9, 0, 1 },
+				.idfall_det_en	= { 0xe3c0, 10, 10, 0, 1 },
+				.idfall_det_st	= { 0xe3e0, 10, 10, 0, 1 },
+				.idfall_det_clr	= { 0xe3d0, 10, 10, 0, 1 },
+				.idrise_det_en	= { 0xe3c0, 9, 9, 0, 1 },
+				.idrise_det_st	= { 0xe3e0, 9, 9, 0, 1 },
+				.idrise_det_clr	= { 0xe3d0, 9, 9, 0, 1 },
+				.ls_det_en	= { 0xe3c0, 7, 7, 0, 1 },
+				.ls_det_st	= { 0xe3e0, 7, 7, 0, 1 },
+				.ls_det_clr	= { 0xe3d0, 7, 7, 0, 1 },
 				.utmi_avalid	= { 0xe2ac, 10, 10, 0, 1 },
 				.utmi_bvalid    = { 0xe2ac, 16, 16, 0, 1 },
+				.utmi_iddig     = { 0xe2ac, 11, 11, 0, 1 },
+				.utmi_ls	= { 0xe2ac, 18, 17, 0, 1 },
+				.vbus_det_en    = { 0x451c, 15, 15, 1, 0 },
 			},
 			[USB2PHY_PORT_HOST] = {
 				.phy_sus	= { 0xe468, 1, 0, 0x2, 0x1 },
@@ -1376,6 +3228,250 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
 				.utmi_hstdet	= { 0xe2ac, 27, 27, 0, 1 }
 			}
 		},
+		.chg_det = {
+			.chg_mode	= { 0xe464, 8, 0, 0, 0x1d7 },
+			.cp_det		= { 0xe2ac, 5, 5, 0, 1 },
+			.dcp_det	= { 0xe2ac, 4, 4, 0, 1 },
+			.dp_det		= { 0xe2ac, 3, 3, 0, 1 },
+			.idm_sink_en	= { 0xe460, 8, 8, 0, 1 },
+			.idp_sink_en	= { 0xe460, 7, 7, 0, 1 },
+			.idp_src_en	= { 0xe460, 9, 9, 0, 1 },
+			.rdm_pdwn_en	= { 0xe460, 10, 10, 0, 1 },
+			.vdm_src_en	= { 0xe460, 12, 12, 0, 1 },
+			.vdp_src_en	= { 0xe460, 11, 11, 0, 1 },
+		},
+	},
+	{ /* sentinel */ }
+};
+
+static const struct rockchip_usb2phy_cfg rk3568_phy_cfgs[] = {
+	{
+		.reg = 0xfe8a0000,
+		.num_ports	= 2,
+		.phy_tuning	= rk3568_usb2phy_tuning,
+		.vbus_detect	= rk3568_vbus_detect_control,
+		.clkout_ctl	= { 0x0008, 4, 4, 1, 0 },
+		.port_cfgs	= {
+			[USB2PHY_PORT_OTG] = {
+				.phy_sus	= { 0x0000, 8, 0, 0, 0x1d1 },
+				.bvalid_det_en	= { 0x0080, 2, 2, 0, 1 },
+				.bvalid_det_st	= { 0x0084, 2, 2, 0, 1 },
+				.bvalid_det_clr = { 0x0088, 2, 2, 0, 1 },
+				.bvalid_grf_sel	= { 0x0008, 15, 14, 0, 3 },
+				.bypass_dm_en	= { 0x0008, 2, 2, 0, 1},
+				.bypass_sel	= { 0x0008, 3, 3, 0, 1},
+				.iddig_output	= { 0x0000, 10, 10, 0, 1 },
+				.iddig_en	= { 0x0000, 9, 9, 0, 1 },
+				.idfall_det_en	= { 0x0080, 5, 5, 0, 1 },
+				.idfall_det_st	= { 0x0084, 5, 5, 0, 1 },
+				.idfall_det_clr = { 0x0088, 5, 5, 0, 1 },
+				.idrise_det_en	= { 0x0080, 4, 4, 0, 1 },
+				.idrise_det_st	= { 0x0084, 4, 4, 0, 1 },
+				.idrise_det_clr = { 0x0088, 4, 4, 0, 1 },
+				.ls_det_en	= { 0x0080, 0, 0, 0, 1 },
+				.ls_det_st	= { 0x0084, 0, 0, 0, 1 },
+				.ls_det_clr	= { 0x0088, 0, 0, 0, 1 },
+				.utmi_avalid	= { 0x00c0, 10, 10, 0, 1 },
+				.utmi_bvalid	= { 0x00c0, 9, 9, 0, 1 },
+				.utmi_iddig	= { 0x00c0, 6, 6, 0, 1 },
+				.utmi_ls	= { 0x00c0, 5, 4, 0, 1 },
+			},
+			[USB2PHY_PORT_HOST] = {
+				/* Select suspend control from controller */
+				.phy_sus	= { 0x0004, 8, 0, 0x1d2, 0x1d2 },
+				.ls_det_en	= { 0x0080, 1, 1, 0, 1 },
+				.ls_det_st	= { 0x0084, 1, 1, 0, 1 },
+				.ls_det_clr	= { 0x0088, 1, 1, 0, 1 },
+				.utmi_ls	= { 0x00c0, 17, 16, 0, 1 },
+				.utmi_hstdet	= { 0x00c0, 19, 19, 0, 1 }
+			}
+		},
+		.chg_det = {
+			.chg_mode	= { 0x0000, 8, 0, 0, 0x1d7 },
+			.cp_det		= { 0x00c0, 24, 24, 0, 1 },
+			.dcp_det	= { 0x00c0, 23, 23, 0, 1 },
+			.dp_det		= { 0x00c0, 25, 25, 0, 1 },
+			.idm_sink_en	= { 0x0008, 8, 8, 0, 1 },
+			.idp_sink_en	= { 0x0008, 7, 7, 0, 1 },
+			.idp_src_en	= { 0x0008, 9, 9, 0, 1 },
+			.rdm_pdwn_en	= { 0x0008, 10, 10, 0, 1 },
+			.vdm_src_en	= { 0x0008, 12, 12, 0, 1 },
+			.vdp_src_en	= { 0x0008, 11, 11, 0, 1 },
+		},
+	},
+	{
+		.reg = 0xfe8b0000,
+		.num_ports	= 2,
+		.phy_tuning	= rk3568_usb2phy_tuning,
+		.clkout_ctl	= { 0x0008, 4, 4, 1, 0 },
+		.port_cfgs	= {
+			[USB2PHY_PORT_OTG] = {
+				.phy_sus	= { 0x0000, 8, 0, 0x1d2, 0x1d1 },
+				.ls_det_en	= { 0x0080, 0, 0, 0, 1 },
+				.ls_det_st	= { 0x0084, 0, 0, 0, 1 },
+				.ls_det_clr	= { 0x0088, 0, 0, 0, 1 },
+				.utmi_ls	= { 0x00c0, 5, 4, 0, 1 },
+				.utmi_hstdet	= { 0x00c0, 7, 7, 0, 1 }
+			},
+			[USB2PHY_PORT_HOST] = {
+				.phy_sus	= { 0x0004, 8, 0, 0x1d2, 0x1d1 },
+				.ls_det_en	= { 0x0080, 1, 1, 0, 1 },
+				.ls_det_st	= { 0x0084, 1, 1, 0, 1 },
+				.ls_det_clr	= { 0x0088, 1, 1, 0, 1 },
+				.utmi_ls	= { 0x00c0, 17, 16, 0, 1 },
+				.utmi_hstdet	= { 0x00c0, 19, 19, 0, 1 }
+			}
+		},
+	},
+	{ /* sentinel */ }
+};
+
+static const struct rockchip_usb2phy_cfg rk3588_phy_cfgs[] = {
+	{
+		.reg = 0x0000,
+		.num_ports	= 1,
+		.phy_tuning	= rk3588_usb2phy_tuning,
+		.clkout_ctl	= { 0x0000, 0, 0, 1, 0 },
+		.port_cfgs	= {
+			[USB2PHY_PORT_OTG] = {
+				.phy_sus	= { 0x000c, 11, 11, 0, 1 },
+				.pipe_phystatus	= { 0x001c, 3, 2, 0, 2 },
+				.bvalid_det_en	= { 0x0080, 1, 1, 0, 1 },
+				.bvalid_det_st	= { 0x0084, 1, 1, 0, 1 },
+				.bvalid_det_clr = { 0x0088, 1, 1, 0, 1 },
+				.bvalid_grf_sel	= { 0x0010, 3, 3, 0, 1 },
+				.bvalid_grf_con	= { 0x0010, 3, 2, 2, 3 },
+				.bvalid_phy_con	= { 0x0008, 1, 0, 2, 3 },
+				.bypass_dm_en	= { 0x000c, 5, 5, 0, 1 },
+				.bypass_sel	= { 0x000c, 6, 6, 0, 1 },
+				.iddig_output	= { 0x0010, 0, 0, 0, 1 },
+				.iddig_en	= { 0x0010, 1, 1, 0, 1 },
+				.idfall_det_en	= { 0x0080, 4, 4, 0, 1 },
+				.idfall_det_st	= { 0x0084, 4, 4, 0, 1 },
+				.idfall_det_clr = { 0x0088, 4, 4, 0, 1 },
+				.idrise_det_en	= { 0x0080, 3, 3, 0, 1 },
+				.idrise_det_st	= { 0x0084, 3, 3, 0, 1 },
+				.idrise_det_clr = { 0x0088, 3, 3, 0, 1 },
+				.ls_det_en	= { 0x0080, 0, 0, 0, 1 },
+				.ls_det_st	= { 0x0084, 0, 0, 0, 1 },
+				.ls_det_clr	= { 0x0088, 0, 0, 0, 1 },
+				.disfall_en	= { 0x0080, 6, 6, 0, 1 },
+				.disfall_st	= { 0x0084, 6, 6, 0, 1 },
+				.disfall_clr	= { 0x0088, 6, 6, 0, 1 },
+				.disrise_en	= { 0x0080, 5, 5, 0, 1 },
+				.disrise_st	= { 0x0084, 5, 5, 0, 1 },
+				.disrise_clr	= { 0x0088, 5, 5, 0, 1 },
+				.utmi_avalid	= { 0x00c0, 7, 7, 0, 1 },
+				.utmi_bvalid	= { 0x00c0, 6, 6, 0, 1 },
+				.utmi_iddig	= { 0x00c0, 5, 5, 0, 1 },
+				.utmi_ls	= { 0x00c0, 10, 9, 0, 1 },
+			}
+		},
+		.chg_det = {
+			.chg_mode	= { 0x0008, 2, 2, 0, 1 },
+			.cp_det		= { 0x00c0, 0, 0, 0, 1 },
+			.dcp_det	= { 0x00c0, 0, 0, 0, 1 },
+			.dp_det		= { 0x00c0, 1, 1, 1, 0 },
+			.idm_sink_en	= { 0x0008, 5, 5, 1, 0 },
+			.idp_sink_en	= { 0x0008, 5, 5, 0, 1 },
+			.idp_src_en	= { 0x0008, 14, 14, 0, 1 },
+			.rdm_pdwn_en	= { 0x0008, 14, 14, 0, 1 },
+			.vdm_src_en	= { 0x0008, 7, 6, 0, 3 },
+			.vdp_src_en	= { 0x0008, 7, 6, 0, 3 },
+		},
+	},
+	{
+		.reg = 0x4000,
+		.num_ports	= 1,
+		.phy_tuning	= rk3588_usb2phy_tuning,
+		.clkout_ctl	= { 0x0000, 0, 0, 1, 0 },
+		.port_cfgs	= {
+			/* Select suspend control from controller */
+			[USB2PHY_PORT_OTG] = {
+				.phy_sus	= { 0x000c, 11, 11, 0, 0 },
+				.pipe_phystatus	= { 0x0034, 3, 2, 0, 2 },
+				.bvalid_det_en	= { 0x0080, 1, 1, 0, 1 },
+				.bvalid_det_st	= { 0x0084, 1, 1, 0, 1 },
+				.bvalid_det_clr = { 0x0088, 1, 1, 0, 1 },
+				.bypass_dm_en	= { 0x000c, 5, 5, 0, 1 },
+				.bypass_sel	= { 0x000c, 6, 6, 0, 1 },
+				.iddig_output	= { 0x0010, 0, 0, 0, 1 },
+				.iddig_en	= { 0x0010, 1, 1, 0, 1 },
+				.idfall_det_en	= { 0x0080, 4, 4, 0, 1 },
+				.idfall_det_st	= { 0x0084, 4, 4, 0, 1 },
+				.idfall_det_clr = { 0x0088, 4, 4, 0, 1 },
+				.idrise_det_en	= { 0x0080, 3, 3, 0, 1 },
+				.idrise_det_st	= { 0x0084, 3, 3, 0, 1 },
+				.idrise_det_clr = { 0x0088, 3, 3, 0, 1 },
+				.ls_det_en	= { 0x0080, 0, 0, 0, 1 },
+				.ls_det_st	= { 0x0084, 0, 0, 0, 1 },
+				.ls_det_clr	= { 0x0088, 0, 0, 0, 1 },
+				.disfall_en	= { 0x0080, 6, 6, 0, 1 },
+				.disfall_st	= { 0x0084, 6, 6, 0, 1 },
+				.disfall_clr	= { 0x0088, 6, 6, 0, 1 },
+				.disrise_en	= { 0x0080, 5, 5, 0, 1 },
+				.disrise_st	= { 0x0084, 5, 5, 0, 1 },
+				.disrise_clr	= { 0x0088, 5, 5, 0, 1 },
+				.utmi_avalid	= { 0x00c0, 7, 7, 0, 1 },
+				.utmi_bvalid	= { 0x00c0, 6, 6, 0, 1 },
+				.utmi_iddig	= { 0x00c0, 5, 5, 0, 1 },
+				.utmi_ls	= { 0x00c0, 10, 9, 0, 1 },
+			}
+		},
+		.chg_det = {
+			.chg_mode	= { 0x0008, 2, 2, 0, 1 },
+			.cp_det		= { 0x00c0, 0, 0, 0, 1 },
+			.dcp_det	= { 0x00c0, 0, 0, 0, 1 },
+			.dp_det		= { 0x00c0, 1, 1, 1, 0 },
+			.idm_sink_en	= { 0x0008, 5, 5, 1, 0 },
+			.idp_sink_en	= { 0x0008, 5, 5, 0, 1 },
+			.idp_src_en	= { 0x0008, 14, 14, 0, 1 },
+			.rdm_pdwn_en	= { 0x0008, 14, 14, 0, 1 },
+			.vdm_src_en	= { 0x0008, 7, 6, 0, 3 },
+			.vdp_src_en	= { 0x0008, 7, 6, 0, 3 },
+		},
+	},
+	{
+		.reg = 0x8000,
+		.num_ports	= 1,
+		.phy_tuning	= rk3588_usb2phy_tuning,
+		.clkout_ctl	= { 0x0000, 0, 0, 1, 0 },
+		.port_cfgs	= {
+			[USB2PHY_PORT_HOST] = {
+				.phy_sus	= { 0x0008, 2, 2, 0, 1 },
+				.ls_det_en	= { 0x0080, 0, 0, 0, 1 },
+				.ls_det_st	= { 0x0084, 0, 0, 0, 1 },
+				.ls_det_clr	= { 0x0088, 0, 0, 0, 1 },
+				.disfall_en	= { 0x0080, 6, 6, 0, 1 },
+				.disfall_st	= { 0x0084, 6, 6, 0, 1 },
+				.disfall_clr	= { 0x0088, 6, 6, 0, 1 },
+				.disrise_en	= { 0x0080, 5, 5, 0, 1 },
+				.disrise_st	= { 0x0084, 5, 5, 0, 1 },
+				.disrise_clr	= { 0x0088, 5, 5, 0, 1 },
+				.utmi_ls	= { 0x00c0, 10, 9, 0, 1 },
+			}
+		},
+	},
+	{
+		.reg = 0xc000,
+		.num_ports	= 1,
+		.phy_tuning	= rk3588_usb2phy_tuning,
+		.clkout_ctl	= { 0x0000, 0, 0, 1, 0 },
+		.port_cfgs	= {
+			[USB2PHY_PORT_HOST] = {
+				.phy_sus	= { 0x0008, 2, 2, 0, 1 },
+				.ls_det_en	= { 0x0080, 0, 0, 0, 1 },
+				.ls_det_st	= { 0x0084, 0, 0, 0, 1 },
+				.ls_det_clr	= { 0x0088, 0, 0, 0, 1 },
+				.disfall_en	= { 0x0080, 6, 6, 0, 1 },
+				.disfall_st	= { 0x0084, 6, 6, 0, 1 },
+				.disfall_clr	= { 0x0088, 6, 6, 0, 1 },
+				.disrise_en	= { 0x0080, 5, 5, 0, 1 },
+				.disrise_st	= { 0x0084, 5, 5, 0, 1 },
+				.disrise_clr	= { 0x0088, 5, 5, 0, 1 },
+				.utmi_ls	= { 0x00c0, 10, 9, 0, 1 },
+			}
+		},
 	},
 	{ /* sentinel */ }
 };
@@ -1407,7 +3503,7 @@ static const struct rockchip_usb2phy_cfg rv1108_phy_cfgs[] = {
 			}
 		},
 		.chg_det = {
-			.opmode		= { 0x0100, 3, 0, 5, 1 },
+			.chg_mode	= { 0x0100, 8, 0, 0, 0x1d7 },
 			.cp_det		= { 0x0804, 1, 1, 0, 1 },
 			.dcp_det	= { 0x0804, 0, 0, 0, 1 },
 			.dp_det		= { 0x0804, 2, 2, 0, 1 },
@@ -1424,10 +3520,16 @@ static const struct rockchip_usb2phy_cfg rv1108_phy_cfgs[] = {
 
 static const struct of_device_id rockchip_usb2phy_dt_match[] = {
 	{ .compatible = "rockchip,px30-usb2phy", .data = &rk3328_phy_cfgs },
+	{ .compatible = "rockchip,rk1808-usb2phy", .data = &rk1808_phy_cfgs },
+	{ .compatible = "rockchip,rk3128-usb2phy", .data = &rk312x_phy_cfgs },
 	{ .compatible = "rockchip,rk3228-usb2phy", .data = &rk3228_phy_cfgs },
+	{ .compatible = "rockchip,rk3308-usb2phy", .data = &rk3308_phy_cfgs },
 	{ .compatible = "rockchip,rk3328-usb2phy", .data = &rk3328_phy_cfgs },
 	{ .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs },
+	{ .compatible = "rockchip,rk3368-usb2phy", .data = &rk3368_phy_cfgs },
 	{ .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs },
+	{ .compatible = "rockchip,rk3568-usb2phy", .data = &rk3568_phy_cfgs },
+	{ .compatible = "rockchip,rk3588-usb2phy", .data = &rk3588_phy_cfgs },
 	{ .compatible = "rockchip,rv1108-usb2phy", .data = &rv1108_phy_cfgs },
 	{}
 };
@@ -1437,6 +3539,7 @@ static struct platform_driver rockchip_usb2phy_driver = {
 	.probe		= rockchip_usb2phy_probe,
 	.driver		= {
 		.name	= "rockchip-usb2phy",
+		.pm	= ROCKCHIP_USB2PHY_DEV_PM,
 		.of_match_table = rockchip_usb2phy_dt_match,
 	},
 };
diff --git a/drivers/phy/rockchip/phy-rockchip-pcie.c b/drivers/phy/rockchip/phy-rockchip-pcie.c
index 75216091d..2f47a3e7f 100644
--- a/drivers/phy/rockchip/phy-rockchip-pcie.c
+++ b/drivers/phy/rockchip/phy-rockchip-pcie.c
@@ -182,6 +182,12 @@ static int rockchip_pcie_phy_power_on(struct phy *phy)
 
 	mutex_lock(&rk_phy->pcie_mutex);
 
+	regmap_write(rk_phy->reg_base,
+		     rk_phy->phy_data->pcie_laneoff,
+		     HIWORD_UPDATE(!PHY_LANE_IDLE_OFF,
+				   PHY_LANE_IDLE_MASK,
+				   PHY_LANE_IDLE_A_SHIFT + inst->index));
+
 	if (rk_phy->pwr_cnt++)
 		goto err_out;
 
@@ -196,12 +202,6 @@ static int rockchip_pcie_phy_power_on(struct phy *phy)
 				   PHY_CFG_ADDR_MASK,
 				   PHY_CFG_ADDR_SHIFT));
 
-	regmap_write(rk_phy->reg_base,
-		     rk_phy->phy_data->pcie_laneoff,
-		     HIWORD_UPDATE(!PHY_LANE_IDLE_OFF,
-				   PHY_LANE_IDLE_MASK,
-				   PHY_LANE_IDLE_A_SHIFT + inst->index));
-
 	/*
 	 * No documented timeout value for phy operation below,
 	 * so we make it large enough here. And we use loop-break
diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c
index 70a31251b..676c213ce 100644
--- a/drivers/phy/rockchip/phy-rockchip-typec.c
+++ b/drivers/phy/rockchip/phy-rockchip-typec.c
@@ -54,6 +54,7 @@
 
 #include <linux/mfd/syscon.h>
 #include <linux/phy/phy.h>
+#include <linux/phy/phy-rockchip-typec.h>
 
 #define CMN_SSM_BANDGAP			(0x21 << 2)
 #define CMN_SSM_BIAS			(0x22 << 2)
@@ -285,13 +286,37 @@
 #define RX_DIAG_SIGDET_TUNE(n)		((0x81dc | ((n) << 9)) << 2)
 #define RX_DIAG_SC2C_DELAY		(0x81e1 << 2)
 
-#define PMA_LANE_CFG			(0xc000 << 2)
+#define PHY_PMA_LANE_CFG		(0xc000 << 2)
+#define PMA_LANE3_DP_LANE_SEL(x)	(((x) & 0x3) << 14)
+#define PMA_LANE3_INTERFACE_SEL(x)	(((x) & 0x1) << 12)
+#define PMA_LANE2_DP_LANE_SEL(x)	(((x) & 0x3) << 10)
+#define PMA_LANE2_INTERFACE_SEL(x)	(((x) & 0x1) << 8)
+#define PMA_LANE1_DP_LANE_SEL(x)	(((x) & 0x3) << 6)
+#define PMA_LANE1_INTERFACE_SEL(x)	(((x) & 0x1) << 4)
+#define PMA_LANE0_DP_LANE_SEL(x)	(((x) & 0x3) << 2)
+#define PMA_LANE0_INTERFACE_SEL(x)	(((x) & 0x1) << 0)
 #define PIPE_CMN_CTRL1			(0xc001 << 2)
 #define PIPE_CMN_CTRL2			(0xc002 << 2)
 #define PIPE_COM_LOCK_CFG1		(0xc003 << 2)
 #define PIPE_COM_LOCK_CFG2		(0xc004 << 2)
 #define PIPE_RCV_DET_INH		(0xc005 << 2)
-#define DP_MODE_CTL			(0xc008 << 2)
+#define PHY_DP_MODE_CTL			(0xc008 << 2)
+#define PHY_DP_LANE_DISABLE		GENMASK(15, 12)
+#define PHY_DP_LANE_3_DISABLE		BIT(15)
+#define PHY_DP_LANE_2_DISABLE		BIT(14)
+#define PHY_DP_LANE_1_DISABLE		BIT(13)
+#define PHY_DP_LANE_0_DISABLE		BIT(12)
+#define PHY_DP_POWER_STATE_ACK_MASK	GENMASK(7, 4)
+#define PHY_DP_POWER_STATE_ACK_SHIFT	4
+#define PHY_DP_POWER_STATE_MASK		GENMASK(3, 0)
+#define PHY_DP_CLK_CTL			(0xc009 << 2)
+#define DP_PLL_CLOCK_ENABLE_ACK		BIT(3)
+#define DP_PLL_CLOCK_ENABLE_MASK	BIT(2)
+#define DP_PLL_CLOCK_DISABLE		0
+#define DP_PLL_READY			BIT(1)
+#define DP_PLL_ENABLE_MASK		BIT(0)
+#define DP_PLL_ENABLE			BIT(0)
+#define DP_PLL_DISABLE			0
 #define DP_CLK_CTL			(0xc009 << 2)
 #define STS				(0xc00F << 2)
 #define PHY_ISO_CMN_CTRL		(0xc010 << 2)
@@ -314,21 +339,29 @@
  * clock 0: PLL 0 div 1
  * clock 1: PLL 1 div 2
  */
-#define CLK_PLL_CONFIG			0X30
+#define CLK_PLL1_DIV1			0x20
+#define CLK_PLL1_DIV2			0x30
 #define CLK_PLL_MASK			0x33
 
 #define CMN_READY			BIT(0)
 
+#define DP_PLL_CLOCK_ENABLE_ACK		BIT(3)
 #define DP_PLL_CLOCK_ENABLE		BIT(2)
+#define DP_PLL_ENABLE_ACK		BIT(1)
 #define DP_PLL_ENABLE			BIT(0)
 #define DP_PLL_DATA_RATE_RBR		((2 << 12) | (4 << 8))
 #define DP_PLL_DATA_RATE_HBR		((2 << 12) | (4 << 8))
 #define DP_PLL_DATA_RATE_HBR2		((1 << 12) | (2 << 8))
+#define DP_PLL_DATA_RATE_MASK		0xff00
 
-#define DP_MODE_A0			BIT(4)
-#define DP_MODE_A2			BIT(6)
-#define DP_MODE_ENTER_A0		0xc101
-#define DP_MODE_ENTER_A2		0xc104
+#define DP_MODE_MASK			0xf
+#define DP_MODE_ENTER_A0		BIT(0)
+#define DP_MODE_ENTER_A2		BIT(2)
+#define DP_MODE_ENTER_A3		BIT(3)
+#define DP_MODE_A0_ACK			BIT(4)
+#define DP_MODE_A2_ACK			BIT(6)
+#define DP_MODE_A3_ACK			BIT(7)
+#define DP_LINK_RESET_DEASSERTED	BIT(8)
 
 #define PHY_MODE_SET_TIMEOUT		100000
 
@@ -340,6 +373,10 @@
 #define MODE_DFP_USB			BIT(1)
 #define MODE_DFP_DP			BIT(2)
 
+#define DP_DEFAULT_RATE			162000
+
+#define POWER_ON_TRIES			5
+
 struct usb3phy_reg {
 	u32 offset;
 	u32 enable_bit;
@@ -368,6 +405,11 @@ struct rockchip_usb3phy_port_cfg {
 	struct usb3phy_reg uphy_dp_sel;
 };
 
+struct phy_config {
+	int swing;
+	int pe;
+};
+
 struct rockchip_typec_phy {
 	struct device *dev;
 	void __iomem *base;
@@ -384,6 +426,7 @@ struct rockchip_typec_phy {
 
 	bool flip;
 	u8 mode;
+	struct phy_config config[3][4];
 };
 
 struct phy_reg {
@@ -408,26 +451,136 @@ static struct phy_reg usb3_pll_cfg[] = {
 	{ 0x8,		CMN_DIAG_PLL0_LF_PROG },
 };
 
-static struct phy_reg dp_pll_cfg[] = {
-	{ 0xf0,		CMN_PLL1_VCOCAL_INIT },
-	{ 0x18,		CMN_PLL1_VCOCAL_ITER },
-	{ 0x30b9,	CMN_PLL1_VCOCAL_START },
-	{ 0x21c,	CMN_PLL1_INTDIV },
-	{ 0,		CMN_PLL1_FRACDIV },
-	{ 0x5,		CMN_PLL1_HIGH_THR },
-	{ 0x35,		CMN_PLL1_SS_CTRL1 },
-	{ 0x7f1e,	CMN_PLL1_SS_CTRL2 },
-	{ 0x20,		CMN_PLL1_DSM_DIAG },
-	{ 0,		CMN_PLLSM1_USER_DEF_CTRL },
-	{ 0,		CMN_DIAG_PLL1_OVRD },
-	{ 0,		CMN_DIAG_PLL1_FBH_OVRD },
-	{ 0,		CMN_DIAG_PLL1_FBL_OVRD },
-	{ 0x6,		CMN_DIAG_PLL1_V2I_TUNE },
-	{ 0x45,		CMN_DIAG_PLL1_CP_TUNE },
-	{ 0x8,		CMN_DIAG_PLL1_LF_PROG },
-	{ 0x100,	CMN_DIAG_PLL1_PTATIS_TUNE1 },
-	{ 0x7,		CMN_DIAG_PLL1_PTATIS_TUNE2 },
-	{ 0x4,		CMN_DIAG_PLL1_INCLK_CTRL },
+static const struct phy_reg dp_pll_rbr_cfg[] = {
+	{ 0x00f0, CMN_PLL1_VCOCAL_INIT },
+	{ 0x0018, CMN_PLL1_VCOCAL_ITER },
+	{ 0x30b9, CMN_PLL1_VCOCAL_START },
+	{ 0x0087, CMN_PLL1_INTDIV },
+	{ 0x0000, CMN_PLL1_FRACDIV },
+	{ 0x0022, CMN_PLL1_HIGH_THR },
+	{ 0x8000, CMN_PLL1_SS_CTRL1 },
+	{ 0x0000, CMN_PLL1_SS_CTRL2 },
+	{ 0x0020, CMN_PLL1_DSM_DIAG },
+	{ 0x0000, CMN_PLLSM1_USER_DEF_CTRL },
+	{ 0x0000, CMN_DIAG_PLL1_OVRD },
+	{ 0x0000, CMN_DIAG_PLL1_FBH_OVRD },
+	{ 0x0000, CMN_DIAG_PLL1_FBL_OVRD },
+	{ 0x0006, CMN_DIAG_PLL1_V2I_TUNE },
+	{ 0x0045, CMN_DIAG_PLL1_CP_TUNE },
+	{ 0x0008, CMN_DIAG_PLL1_LF_PROG },
+	{ 0x0100, CMN_DIAG_PLL1_PTATIS_TUNE1 },
+	{ 0x0007, CMN_DIAG_PLL1_PTATIS_TUNE2 },
+	{ 0x0001, CMN_DIAG_PLL1_INCLK_CTRL },
+};
+
+static const struct phy_reg dp_pll_rbr_ssc_cfg[] = {
+	{ 0x00f0, CMN_PLL1_VCOCAL_INIT },
+	{ 0x0018, CMN_PLL1_VCOCAL_ITER },
+	{ 0x30b9, CMN_PLL1_VCOCAL_START },
+	{ 0x0086, CMN_PLL1_INTDIV },
+	{ 0xf915, CMN_PLL1_FRACDIV },
+	{ 0x0022, CMN_PLL1_HIGH_THR },
+	{ 0x0140, CMN_PLL1_SS_CTRL1 },
+	{ 0x7f03, CMN_PLL1_SS_CTRL2 },
+	{ 0x0020, CMN_PLL1_DSM_DIAG },
+	{ 0x0000, CMN_PLLSM1_USER_DEF_CTRL },
+	{ 0x0000, CMN_DIAG_PLL1_OVRD },
+	{ 0x0000, CMN_DIAG_PLL1_FBH_OVRD },
+	{ 0x0000, CMN_DIAG_PLL1_FBL_OVRD },
+	{ 0x0006, CMN_DIAG_PLL1_V2I_TUNE },
+	{ 0x0045, CMN_DIAG_PLL1_CP_TUNE },
+	{ 0x0008, CMN_DIAG_PLL1_LF_PROG },
+	{ 0x0100, CMN_DIAG_PLL1_PTATIS_TUNE1 },
+	{ 0x0007, CMN_DIAG_PLL1_PTATIS_TUNE2 },
+	{ 0x0001, CMN_DIAG_PLL1_INCLK_CTRL },
+};
+
+static const struct phy_reg dp_pll_hbr_cfg[] = {
+	{ 0x00f0, CMN_PLL1_VCOCAL_INIT },
+	{ 0x0018, CMN_PLL1_VCOCAL_ITER },
+	{ 0x30b4, CMN_PLL1_VCOCAL_START },
+	{ 0x00e1, CMN_PLL1_INTDIV },
+	{ 0x0000, CMN_PLL1_FRACDIV },
+	{ 0x0005, CMN_PLL1_HIGH_THR },
+	{ 0x8000, CMN_PLL1_SS_CTRL1 },
+	{ 0x0000, CMN_PLL1_SS_CTRL2 },
+	{ 0x0020, CMN_PLL1_DSM_DIAG },
+	{ 0x1000, CMN_PLLSM1_USER_DEF_CTRL },
+	{ 0x0000, CMN_DIAG_PLL1_OVRD },
+	{ 0x0000, CMN_DIAG_PLL1_FBH_OVRD },
+	{ 0x0000, CMN_DIAG_PLL1_FBL_OVRD },
+	{ 0x0007, CMN_DIAG_PLL1_V2I_TUNE },
+	{ 0x0045, CMN_DIAG_PLL1_CP_TUNE },
+	{ 0x0008, CMN_DIAG_PLL1_LF_PROG },
+	{ 0x0001, CMN_DIAG_PLL1_PTATIS_TUNE1 },
+	{ 0x0001, CMN_DIAG_PLL1_PTATIS_TUNE2 },
+	{ 0x0001, CMN_DIAG_PLL1_INCLK_CTRL },
+};
+
+static const struct phy_reg dp_pll_hbr_ssc_cfg[] = {
+	{ 0x00f0, CMN_PLL1_VCOCAL_INIT },
+	{ 0x0018, CMN_PLL1_VCOCAL_ITER },
+	{ 0x30b4, CMN_PLL1_VCOCAL_START },
+	{ 0x00e0, CMN_PLL1_INTDIV },
+	{ 0xf479, CMN_PLL1_FRACDIV },
+	{ 0x0038, CMN_PLL1_HIGH_THR },
+	{ 0x0204, CMN_PLL1_SS_CTRL1 },
+	{ 0x7f03, CMN_PLL1_SS_CTRL2 },
+	{ 0x0020, CMN_PLL1_DSM_DIAG },
+	{ 0x1000, CMN_PLLSM1_USER_DEF_CTRL },
+	{ 0x0000, CMN_DIAG_PLL1_OVRD },
+	{ 0x0000, CMN_DIAG_PLL1_FBH_OVRD },
+	{ 0x0000, CMN_DIAG_PLL1_FBL_OVRD },
+	{ 0x0007, CMN_DIAG_PLL1_V2I_TUNE },
+	{ 0x0045, CMN_DIAG_PLL1_CP_TUNE },
+	{ 0x0008, CMN_DIAG_PLL1_LF_PROG },
+	{ 0x0001, CMN_DIAG_PLL1_PTATIS_TUNE1 },
+	{ 0x0001, CMN_DIAG_PLL1_PTATIS_TUNE2 },
+	{ 0x0001, CMN_DIAG_PLL1_INCLK_CTRL },
+};
+
+static const struct phy_reg dp_pll_hbr2_cfg[] = {
+	{ 0x00f0, CMN_PLL1_VCOCAL_INIT },
+	{ 0x0018, CMN_PLL1_VCOCAL_ITER },
+	{ 0x30b4, CMN_PLL1_VCOCAL_START },
+	{ 0x00e1, CMN_PLL1_INTDIV },
+	{ 0x0000, CMN_PLL1_FRACDIV },
+	{ 0x0005, CMN_PLL1_HIGH_THR },
+	{ 0x8000, CMN_PLL1_SS_CTRL1 },
+	{ 0x0000, CMN_PLL1_SS_CTRL2 },
+	{ 0x0020, CMN_PLL1_DSM_DIAG },
+	{ 0x1000, CMN_PLLSM1_USER_DEF_CTRL },
+	{ 0x0000, CMN_DIAG_PLL1_OVRD },
+	{ 0x0000, CMN_DIAG_PLL1_FBH_OVRD },
+	{ 0x0000, CMN_DIAG_PLL1_FBL_OVRD },
+	{ 0x0007, CMN_DIAG_PLL1_V2I_TUNE },
+	{ 0x0045, CMN_DIAG_PLL1_CP_TUNE },
+	{ 0x0008, CMN_DIAG_PLL1_LF_PROG },
+	{ 0x0001, CMN_DIAG_PLL1_PTATIS_TUNE1 },
+	{ 0x0001, CMN_DIAG_PLL1_PTATIS_TUNE2 },
+	{ 0x0001, CMN_DIAG_PLL1_INCLK_CTRL },
+};
+
+static const struct phy_reg dp_pll_hbr2_ssc_cfg[] = {
+	{ 0x00f0, CMN_PLL1_VCOCAL_INIT },
+	{ 0x0018, CMN_PLL1_VCOCAL_ITER },
+	{ 0x30b4, CMN_PLL1_VCOCAL_START },
+	{ 0x00e0, CMN_PLL1_INTDIV },
+	{ 0xf479, CMN_PLL1_FRACDIV },
+	{ 0x0038, CMN_PLL1_HIGH_THR },
+	{ 0x0204, CMN_PLL1_SS_CTRL1 },
+	{ 0x7f03, CMN_PLL1_SS_CTRL2 },
+	{ 0x0020, CMN_PLL1_DSM_DIAG },
+	{ 0x1000, CMN_PLLSM1_USER_DEF_CTRL },
+	{ 0x0000, CMN_DIAG_PLL1_OVRD },
+	{ 0x0000, CMN_DIAG_PLL1_FBH_OVRD },
+	{ 0x0000, CMN_DIAG_PLL1_FBL_OVRD },
+	{ 0x0007, CMN_DIAG_PLL1_V2I_TUNE },
+	{ 0x0045, CMN_DIAG_PLL1_CP_TUNE },
+	{ 0x0008, CMN_DIAG_PLL1_LF_PROG },
+	{ 0x0001, CMN_DIAG_PLL1_PTATIS_TUNE1 },
+	{ 0x0001, CMN_DIAG_PLL1_PTATIS_TUNE2 },
+	{ 0x0001, CMN_DIAG_PLL1_INCLK_CTRL },
 };
 
 static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = {
@@ -454,6 +607,134 @@ static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = {
 	{ /* sentinel */ }
 };
 
+/* default phy config */
+static const struct phy_config tcphy_default_config[3][4] = {
+	{{ .swing = 0x2a, .pe = 0x00 },
+	 { .swing = 0x1f, .pe = 0x15 },
+	 { .swing = 0x14, .pe = 0x22 },
+	 { .swing = 0x02, .pe = 0x2b } },
+
+	{{ .swing = 0x21, .pe = 0x00 },
+	 { .swing = 0x12, .pe = 0x15 },
+	 { .swing = 0x02, .pe = 0x22 },
+	 { .swing = 0,    .pe = 0 } },
+
+	{{ .swing = 0x15, .pe = 0x00 },
+	 { .swing = 0x00, .pe = 0x15 },
+	 { .swing = 0,    .pe = 0 },
+	 { .swing = 0,    .pe = 0 } },
+};
+
+enum phy_dp_power_state {
+	PHY_DP_POWER_STATE_DISABLED = -1,
+	PHY_DP_POWER_STATE_A0,
+	PHY_DP_POWER_STATE_A1,
+	PHY_DP_POWER_STATE_A2,
+	PHY_DP_POWER_STATE_A3,
+};
+
+static int tcphy_dp_set_power_state(struct rockchip_typec_phy *tcphy,
+				    enum phy_dp_power_state state)
+{
+	u32 ack, reg, sts = BIT(state);
+	int ret;
+
+	/*
+	 * Power state changes must not be requested until after the cmn_ready
+	 * signal has gone active.
+	 */
+	reg = readl(tcphy->base + PMA_CMN_CTRL1);
+	if (!(reg & CMN_READY)) {
+		dev_err(tcphy->dev, "cmn_ready in the inactive state\n");
+		return -EINVAL;
+	}
+
+	reg = readl(tcphy->base + PHY_DP_MODE_CTL);
+	reg &= ~PHY_DP_POWER_STATE_MASK;
+	reg |= sts;
+	writel(reg, tcphy->base + PHY_DP_MODE_CTL);
+
+	ret = readl_poll_timeout(tcphy->base + PHY_DP_MODE_CTL,
+				 ack, (((ack & PHY_DP_POWER_STATE_ACK_MASK) >>
+				 PHY_DP_POWER_STATE_ACK_SHIFT) == sts), 10,
+				 PHY_MODE_SET_TIMEOUT);
+	if (ret < 0) {
+		dev_err(tcphy->dev, "failed to enter power state %d\n", state);
+		return ret;
+	}
+
+	return 0;
+}
+
+enum {
+	PHY_DP_LANE_0,
+	PHY_DP_LANE_1,
+	PHY_DP_LANE_2,
+	PHY_DP_LANE_3,
+};
+
+enum {
+	PMA_IF_PIPE_PCS,
+	PMA_IF_PHY_DP,
+};
+
+/*
+ * For the TypeC PHY, the 4 lanes are mapping to the USB TypeC receptacle pins
+ * as follows:
+ *   -------------------------------------------------------------------
+ *	PHY Lanes/Module Pins			TypeC Receptacle Pins
+ *   -------------------------------------------------------------------
+ *	Lane0 (tx_p/m_ln_0)			TX1+/TX1- (pins A2/A3)
+ *	Lane1 (tx_rx_p/m_ln_1)			RX1+/RX1- (pins B11/B10)
+ *	Lane2 (tx_rx_p/m_ln_2)			RX2+/RX2- (pins A11/A10)
+ *	Lane3 (tx_p/m_ln_3)			TX2+/TX2- (pins B2/B3)
+ *   -------------------------------------------------------------------
+ *
+ * USB and DP lanes mapping to TypeC PHY lanes for each of pin assignment
+ * options (normal connector orientation) described in the VESA DisplayPort
+ * Alt Mode on USB TypeC Standard as follows:
+ *
+ * ----------------------------------------------------------------------
+ *	PHY Lanes	A	B	C	D	E	F
+ * ----------------------------------------------------------------------
+ *	  0	       ML1     SSTX    ML2     SSTX    ML2     SSTX
+ *	  1	       ML3     SSRX    ML3     SSRX    ML3     SSRX
+ *	  2	       ML2     ML1     ML0     ML0     ML0     ML0
+ *	  3	       ML0     ML0     ML1     ML1     ML1     ML1
+ * ----------------------------------------------------------------------
+ */
+static void tcphy_set_lane_mapping(struct rockchip_typec_phy *tcphy, u8 mode)
+{
+	/*
+	 * The PHY_PMA_LANE_CFG register is used to select whether a PMA lane
+	 * is mapped for USB or PHY DP. The PHY_PMA_LANE_CFG register is
+	 * configured based on a normal connector orientation. Logic in the
+	 * PHY automatically handles the flipped connector case based on the
+	 * setting of orientation of TypeC PHY.
+	 */
+	if (mode == MODE_DFP_DP) {
+		/* This maps to VESA DP Alt Mode pin assignments C and E. */
+		writel(PMA_LANE3_DP_LANE_SEL(PHY_DP_LANE_1) |
+		       PMA_LANE3_INTERFACE_SEL(PMA_IF_PHY_DP) |
+		       PMA_LANE2_DP_LANE_SEL(PHY_DP_LANE_0) |
+		       PMA_LANE2_INTERFACE_SEL(PMA_IF_PHY_DP) |
+		       PMA_LANE1_DP_LANE_SEL(PHY_DP_LANE_3) |
+		       PMA_LANE1_INTERFACE_SEL(PMA_IF_PHY_DP) |
+		       PMA_LANE0_DP_LANE_SEL(PHY_DP_LANE_2) |
+		       PMA_LANE0_INTERFACE_SEL(PMA_IF_PHY_DP),
+		       tcphy->base + PHY_PMA_LANE_CFG);
+	} else {
+		/* This maps to VESA DP Alt Mode pin assignments D and F. */
+		writel(PMA_LANE3_DP_LANE_SEL(PHY_DP_LANE_1) |
+		       PMA_LANE3_INTERFACE_SEL(PMA_IF_PHY_DP) |
+		       PMA_LANE2_DP_LANE_SEL(PHY_DP_LANE_0) |
+		       PMA_LANE2_INTERFACE_SEL(PMA_IF_PHY_DP) |
+		       PMA_LANE1_INTERFACE_SEL(PMA_IF_PIPE_PCS) |
+		       PMA_LANE0_INTERFACE_SEL(PMA_IF_PIPE_PCS),
+		       tcphy->base + PHY_PMA_LANE_CFG);
+	}
+}
+
 static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy)
 {
 	u32 i, rdata;
@@ -475,7 +756,7 @@ static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy)
 
 	rdata = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
 	rdata &= ~CLK_PLL_MASK;
-	rdata |= CLK_PLL_CONFIG;
+	rdata |= CLK_PLL1_DIV2;
 	writel(rdata, tcphy->base + CMN_DIAG_HSCLK_SEL);
 }
 
@@ -489,17 +770,44 @@ static void tcphy_cfg_usb3_pll(struct rockchip_typec_phy *tcphy)
 		       tcphy->base + usb3_pll_cfg[i].addr);
 }
 
-static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy)
+static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy, int link_rate)
 {
-	u32 i;
+	const struct phy_reg *phy_cfg;
+	u32 clk_ctrl;
+	u32 i, cfg_size, hsclk_sel;
+
+	hsclk_sel = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
+	hsclk_sel &= ~CLK_PLL_MASK;
+
+	switch (link_rate) {
+	case 540000:
+		clk_ctrl = DP_PLL_DATA_RATE_HBR2;
+		hsclk_sel |= CLK_PLL1_DIV1;
+		phy_cfg = dp_pll_hbr2_cfg;
+		cfg_size = ARRAY_SIZE(dp_pll_hbr2_cfg);
+		break;
+	case 270000:
+		clk_ctrl = DP_PLL_DATA_RATE_HBR;
+		hsclk_sel |= CLK_PLL1_DIV2;
+		phy_cfg = dp_pll_hbr_cfg;
+		cfg_size = ARRAY_SIZE(dp_pll_hbr_cfg);
+		break;
+	case 162000:
+	default:
+		clk_ctrl = DP_PLL_DATA_RATE_RBR;
+		hsclk_sel |= CLK_PLL1_DIV2;
+		phy_cfg = dp_pll_rbr_cfg;
+		cfg_size = ARRAY_SIZE(dp_pll_rbr_cfg);
+		break;
+	}
 
-	/* set the default mode to RBR */
-	writel(DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE | DP_PLL_DATA_RATE_RBR,
-	       tcphy->base + DP_CLK_CTL);
+	clk_ctrl |= DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE;
+	writel(clk_ctrl, tcphy->base + PHY_DP_CLK_CTL);
+	writel(hsclk_sel, tcphy->base + CMN_DIAG_HSCLK_SEL);
 
 	/* load the configuration of PLL1 */
-	for (i = 0; i < ARRAY_SIZE(dp_pll_cfg); i++)
-		writel(dp_pll_cfg[i].value, tcphy->base + dp_pll_cfg[i].addr);
+	for (i = 0; i < cfg_size; i++)
+		writel(phy_cfg[i].value, tcphy->base + phy_cfg[i].addr);
 }
 
 static void tcphy_tx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
@@ -526,9 +834,10 @@ static void tcphy_rx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
 	writel(0xfb, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
 }
 
-static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
+static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, int link_rate,
+			      u8 swing, u8 pre_emp, u32 lane)
 {
-	u16 rdata;
+	u16 val;
 
 	writel(0xbefc, tcphy->base + XCVR_PSM_RCTRL(lane));
 	writel(0x6799, tcphy->base + TX_PSC_A0(lane));
@@ -536,27 +845,234 @@ static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
 	writel(0x98, tcphy->base + TX_PSC_A2(lane));
 	writel(0x98, tcphy->base + TX_PSC_A3(lane));
 
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_001(lane));
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_010(lane));
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_011(lane));
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_100(lane));
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_101(lane));
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_110(lane));
-	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_111(lane));
-	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_10(lane));
-	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_01(lane));
-	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
-	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_11(lane));
-
-	writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
-	writel(0x400, tcphy->base + TX_DIAG_TX_DRV(lane));
-
-	rdata = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
-	rdata = (rdata & 0x8fff) | 0x6000;
-	writel(rdata, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
+	writel(tcphy->config[swing][pre_emp].swing,
+	       tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
+	writel(tcphy->config[swing][pre_emp].pe,
+	       tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
+
+	if (swing == 2 && pre_emp == 0 && link_rate != 540000) {
+		writel(0x700, tcphy->base + TX_DIAG_TX_DRV(lane));
+		writel(0x13c, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
+	} else {
+		writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
+		writel(0x0400, tcphy->base + TX_DIAG_TX_DRV(lane));
+	}
+
+	val = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
+	val = val & 0x8fff;
+	switch (link_rate) {
+	case 540000:
+		val |= (5 << 12);
+		break;
+	case 162000:
+	case 270000:
+	default:
+		val |= (6 << 12);
+		break;
+	}
+	writel(val, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
 }
 
+int tcphy_dp_set_phy_config(struct phy *phy, int link_rate,
+			    int lane_count, u8 swing, u8 pre_emp)
+{
+	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+	u8 i;
+
+	if (!phy->power_count)
+		return -EPERM;
+
+	if (tcphy->mode == MODE_DFP_DP) {
+		for (i = 0; i < 4; i++)
+			tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, i);
+	} else {
+		if (tcphy->flip) {
+			tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 0);
+			tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 1);
+		} else {
+			tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 2);
+			tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 3);
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(tcphy_dp_set_phy_config);
+
+int tcphy_dp_set_lane_count(struct phy *phy, u8 lane_count)
+{
+	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+	u32 reg;
+
+	if (!phy->power_count)
+		return -EPERM;
+
+	/*
+	 * In cases where fewer than the configured number of DP lanes are
+	 * being used. PHY_DP_MODE_CTL[15:12] must be set to disable and
+	 * power-down the unused PHY DP lanes (and their mapped PMA lanes).
+	 * Set the bit ([15:12]) associated with each DP PHY lane(s) to be
+	 * disabled.
+	 */
+	reg = readl(tcphy->base + PHY_DP_MODE_CTL);
+	reg |= PHY_DP_LANE_DISABLE;
+
+	switch (lane_count) {
+	case 4:
+		reg &= ~(PHY_DP_LANE_3_DISABLE | PHY_DP_LANE_2_DISABLE |
+			 PHY_DP_LANE_1_DISABLE | PHY_DP_LANE_0_DISABLE);
+		break;
+	case 2:
+		reg &= ~(PHY_DP_LANE_1_DISABLE | PHY_DP_LANE_0_DISABLE);
+		break;
+	case 1:
+		reg &= ~PHY_DP_LANE_0_DISABLE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	writel(reg, tcphy->base + PHY_DP_MODE_CTL);
+
+	return 0;
+}
+EXPORT_SYMBOL(tcphy_dp_set_lane_count);
+
+int tcphy_dp_set_link_rate(struct phy *phy, int link_rate, bool ssc_on)
+{
+	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+	const struct phy_reg *phy_cfg;
+	u32 cmn_diag_hsclk_sel, phy_dp_clk_ctl, reg;
+	u32 i, cfg_size;
+	int ret;
+
+	if (!phy->power_count)
+		return -EPERM;
+
+	/* Place the PHY lanes in the A3 power state. */
+	ret = tcphy_dp_set_power_state(tcphy, PHY_DP_POWER_STATE_A3);
+	if (ret) {
+		dev_err(tcphy->dev, "failed to enter A3 state: %d\n", ret);
+		return ret;
+	}
+
+	/* Gate the PLL clocks from PMA */
+	reg = readl(tcphy->base + PHY_DP_CLK_CTL);
+	reg &= ~DP_PLL_CLOCK_ENABLE_MASK;
+	reg |= DP_PLL_CLOCK_DISABLE;
+	writel(reg, tcphy->base + PHY_DP_CLK_CTL);
+
+	ret = readl_poll_timeout(tcphy->base + PHY_DP_CLK_CTL, reg,
+				 !(reg & DP_PLL_CLOCK_ENABLE_ACK),
+				 10, PHY_MODE_SET_TIMEOUT);
+	if (ret) {
+		dev_err(tcphy->dev, "wait DP PLL clock disabled timeout\n");
+		return ret;
+	}
+
+	/* Disable the PLL */
+	reg = readl(tcphy->base + PHY_DP_CLK_CTL);
+	reg &= ~DP_PLL_ENABLE_MASK;
+	reg |= DP_PLL_DISABLE;
+	writel(reg, tcphy->base + PHY_DP_CLK_CTL);
+
+	ret = readl_poll_timeout(tcphy->base + PHY_DP_CLK_CTL, reg,
+				 !(reg & DP_PLL_READY),
+				 10, PHY_MODE_SET_TIMEOUT);
+	if (ret) {
+		dev_err(tcphy->dev, "wait DP PLL not ready timeout\n");
+		return ret;
+	}
+
+	/* Re-configure PHY registers for the new data rate */
+	cmn_diag_hsclk_sel = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
+	cmn_diag_hsclk_sel &= ~(GENMASK(5, 4) | GENMASK(1, 0));
+
+	phy_dp_clk_ctl = readl(tcphy->base + PHY_DP_CLK_CTL);
+	phy_dp_clk_ctl &= ~(GENMASK(15, 12) | GENMASK(11, 8));
+
+	switch (link_rate) {
+	case 162000:
+		cmn_diag_hsclk_sel |= (3 << 4) | (0 << 0);
+		phy_dp_clk_ctl |= (2 << 12) | (4 << 8);
+
+		phy_cfg = ssc_on ? dp_pll_rbr_ssc_cfg : dp_pll_rbr_cfg;
+		cfg_size = ssc_on ? ARRAY_SIZE(dp_pll_rbr_ssc_cfg) :
+				    ARRAY_SIZE(dp_pll_rbr_cfg);
+		break;
+	case 270000:
+		cmn_diag_hsclk_sel |= (3 << 4) | (0 << 0);
+		phy_dp_clk_ctl |= (2 << 12) | (4 << 8);
+
+		phy_cfg = ssc_on ? dp_pll_hbr_ssc_cfg : dp_pll_hbr_cfg;
+		cfg_size = ssc_on ? ARRAY_SIZE(dp_pll_hbr_ssc_cfg) :
+				    ARRAY_SIZE(dp_pll_hbr_cfg);
+		break;
+	case 540000:
+		cmn_diag_hsclk_sel |= (2 << 4) | (0 << 0);
+		phy_dp_clk_ctl |= (1 << 12) | (2 << 8);
+
+		phy_cfg = ssc_on ? dp_pll_hbr2_ssc_cfg : dp_pll_hbr2_cfg;
+		cfg_size = ssc_on ? ARRAY_SIZE(dp_pll_hbr2_ssc_cfg) :
+				    ARRAY_SIZE(dp_pll_hbr2_cfg);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	writel(cmn_diag_hsclk_sel, tcphy->base + CMN_DIAG_HSCLK_SEL);
+	writel(phy_dp_clk_ctl, tcphy->base + PHY_DP_CLK_CTL);
+
+	/* load the configuration of PLL1 */
+	for (i = 0; i < cfg_size; i++)
+		writel(phy_cfg[i].value, tcphy->base + phy_cfg[i].addr);
+
+	/* Enable the PLL */
+	reg = readl(tcphy->base + PHY_DP_CLK_CTL);
+	reg &= ~DP_PLL_ENABLE_MASK;
+	reg |= DP_PLL_ENABLE;
+	writel(reg, tcphy->base + PHY_DP_CLK_CTL);
+
+	ret = readl_poll_timeout(tcphy->base + PHY_DP_CLK_CTL, reg,
+				 reg & DP_PLL_READY,
+				 10, PHY_MODE_SET_TIMEOUT);
+	if (ret < 0) {
+		dev_err(tcphy->dev, "wait DP PLL ready timeout\n");
+		return ret;
+	}
+
+	/* Enable PMA PLL clocks */
+	reg = readl(tcphy->base + PHY_DP_CLK_CTL);
+	reg &= ~DP_PLL_CLOCK_ENABLE_MASK;
+	reg |= DP_PLL_CLOCK_ENABLE;
+	writel(reg, tcphy->base + PHY_DP_CLK_CTL);
+
+	ret = readl_poll_timeout(tcphy->base + PHY_DP_CLK_CTL, reg,
+				 reg & DP_PLL_CLOCK_ENABLE_ACK,
+				 10, PHY_MODE_SET_TIMEOUT);
+	if (ret) {
+		dev_err(tcphy->dev, "wait DP PLL clock enabled timeout\n");
+		return ret;
+	}
+
+	/* The PMA must go through the A2 power state upon a data rate change */
+	ret = tcphy_dp_set_power_state(tcphy, PHY_DP_POWER_STATE_A2);
+	if (ret) {
+		dev_err(tcphy->dev, "failed to enter A2 state: %d\n", ret);
+		return ret;
+	}
+
+	/* change the PHY power state to A0 */
+	ret = tcphy_dp_set_power_state(tcphy, PHY_DP_POWER_STATE_A0);
+	if (ret) {
+		dev_err(tcphy->dev, "failed to enter A0 state: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(tcphy_dp_set_link_rate);
+
 static inline int property_enable(struct rockchip_typec_phy *tcphy,
 				  const struct usb3phy_reg *reg, bool en)
 {
@@ -719,6 +1235,18 @@ static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy)
 	writel(val, tcphy->base + TX_DIG_CTRL_REG_2);
 }
 
+static int tcphy_cfg_usb3_to_usb2_only(struct rockchip_typec_phy *tcphy,
+				       bool value)
+{
+	const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs;
+
+	property_enable(tcphy, &cfg->usb3tousb2_en, value);
+	property_enable(tcphy, &cfg->usb3_host_disable, value);
+	property_enable(tcphy, &cfg->usb3_host_port, !value);
+
+	return 0;
+}
+
 static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode)
 {
 	const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs;
@@ -743,32 +1271,33 @@ static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode)
 	tcphy_dp_aux_set_flip(tcphy);
 
 	tcphy_cfg_24m(tcphy);
+	tcphy_set_lane_mapping(tcphy, mode);
 
 	if (mode == MODE_DFP_DP) {
-		tcphy_cfg_dp_pll(tcphy);
+		tcphy_cfg_usb3_to_usb2_only(tcphy, true);
+		tcphy_cfg_dp_pll(tcphy, DP_DEFAULT_RATE);
 		for (i = 0; i < 4; i++)
-			tcphy_dp_cfg_lane(tcphy, i);
-
-		writel(PIN_ASSIGN_C_E, tcphy->base + PMA_LANE_CFG);
+			tcphy_dp_cfg_lane(tcphy, DP_DEFAULT_RATE, 0, 0, i);
 	} else {
 		tcphy_cfg_usb3_pll(tcphy);
-		tcphy_cfg_dp_pll(tcphy);
+		tcphy_cfg_dp_pll(tcphy, DP_DEFAULT_RATE);
 		if (tcphy->flip) {
 			tcphy_tx_usb3_cfg_lane(tcphy, 3);
 			tcphy_rx_usb3_cfg_lane(tcphy, 2);
-			tcphy_dp_cfg_lane(tcphy, 0);
-			tcphy_dp_cfg_lane(tcphy, 1);
+			tcphy_dp_cfg_lane(tcphy, DP_DEFAULT_RATE, 0, 0, 0);
+			tcphy_dp_cfg_lane(tcphy, DP_DEFAULT_RATE, 0, 0, 1);
 		} else {
 			tcphy_tx_usb3_cfg_lane(tcphy, 0);
 			tcphy_rx_usb3_cfg_lane(tcphy, 1);
-			tcphy_dp_cfg_lane(tcphy, 2);
-			tcphy_dp_cfg_lane(tcphy, 3);
+			tcphy_dp_cfg_lane(tcphy, DP_DEFAULT_RATE, 0, 0, 2);
+			tcphy_dp_cfg_lane(tcphy, DP_DEFAULT_RATE, 0, 0, 3);
 		}
-
-		writel(PIN_ASSIGN_D_F, tcphy->base + PMA_LANE_CFG);
 	}
 
-	writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
+	val = readl(tcphy->base + PHY_DP_MODE_CTL);
+	val &= ~DP_MODE_MASK;
+	val |= DP_MODE_ENTER_A2 | DP_LINK_RESET_DEASSERTED;
+	writel(val, tcphy->base + PHY_DP_MODE_CTL);
 
 	reset_control_deassert(tcphy->uphy_rst);
 
@@ -851,22 +1380,9 @@ static int tcphy_get_mode(struct rockchip_typec_phy *tcphy)
 	return mode;
 }
 
-static int tcphy_cfg_usb3_to_usb2_only(struct rockchip_typec_phy *tcphy,
-				       bool value)
+static int _rockchip_usb3_phy_power_on(struct rockchip_typec_phy *tcphy)
 {
 	const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs;
-
-	property_enable(tcphy, &cfg->usb3tousb2_en, value);
-	property_enable(tcphy, &cfg->usb3_host_disable, value);
-	property_enable(tcphy, &cfg->usb3_host_port, !value);
-
-	return 0;
-}
-
-static int rockchip_usb3_phy_power_on(struct phy *phy)
-{
-	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
-	const struct rockchip_usb3phy_port_cfg *cfg = tcphy->port_cfgs;
 	const struct usb3phy_reg *reg = &cfg->pipe_status;
 	int timeout, new_mode, ret = 0;
 	u32 val;
@@ -917,6 +1433,24 @@ static int rockchip_usb3_phy_power_on(struct phy *phy)
 	return ret;
 }
 
+static int rockchip_usb3_phy_power_on(struct phy *phy)
+{
+	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+	int ret;
+	int tries;
+
+	for (tries = 0; tries < POWER_ON_TRIES; tries++) {
+		ret = _rockchip_usb3_phy_power_on(tcphy);
+		if (!ret)
+			break;
+	}
+
+	if (tries && !ret)
+		dev_info(tcphy->dev, "Needed %d loops to turn on\n", tries);
+
+	return ret;
+}
+
 static int rockchip_usb3_phy_power_off(struct phy *phy)
 {
 	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
@@ -980,8 +1514,8 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
 
 	property_enable(tcphy, &cfg->uphy_dp_sel, 1);
 
-	ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
-				 val, val & DP_MODE_A2, 1000,
+	ret = readx_poll_timeout(readl, tcphy->base + PHY_DP_MODE_CTL,
+				 val, val & DP_MODE_A2_ACK, 1000,
 				 PHY_MODE_SET_TIMEOUT);
 	if (ret < 0) {
 		dev_err(tcphy->dev, "failed to wait TCPHY enter A2\n");
@@ -990,14 +1524,10 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
 
 	tcphy_dp_aux_calibration(tcphy);
 
-	writel(DP_MODE_ENTER_A0, tcphy->base + DP_MODE_CTL);
-
-	ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
-				 val, val & DP_MODE_A0, 1000,
-				 PHY_MODE_SET_TIMEOUT);
-	if (ret < 0) {
-		writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
-		dev_err(tcphy->dev, "failed to wait TCPHY enter A0\n");
+	/* enter A0 mode */
+	ret = tcphy_dp_set_power_state(tcphy, PHY_DP_POWER_STATE_A0);
+	if (ret) {
+		dev_err(tcphy->dev, "failed to enter A0 power state\n");
 		goto power_on_finish;
 	}
 
@@ -1014,6 +1544,7 @@ static int rockchip_dp_phy_power_on(struct phy *phy)
 static int rockchip_dp_phy_power_off(struct phy *phy)
 {
 	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+	int ret;
 
 	mutex_lock(&tcphy->lock);
 
@@ -1022,7 +1553,11 @@ static int rockchip_dp_phy_power_off(struct phy *phy)
 
 	tcphy->mode &= ~MODE_DFP_DP;
 
-	writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
+	ret = tcphy_dp_set_power_state(tcphy, PHY_DP_POWER_STATE_A2);
+	if (ret) {
+		dev_err(tcphy->dev, "failed to enter A2 power state\n");
+		goto unlock;
+	}
 
 	if (tcphy->mode == MODE_DISCONNECT)
 		tcphy_phy_deinit(tcphy);
@@ -1041,6 +1576,8 @@ static const struct phy_ops rockchip_dp_phy_ops = {
 static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
 			  struct device *dev)
 {
+	int ret;
+
 	tcphy->grf_regs = syscon_regmap_lookup_by_phandle(dev->of_node,
 							  "rockchip,grf");
 	if (IS_ERR(tcphy->grf_regs)) {
@@ -1078,6 +1615,16 @@ static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
 		return PTR_ERR(tcphy->tcphy_rst);
 	}
 
+	/*
+	 * check if phy_config pass from dts, if no,
+	 * use default phy config value.
+	 */
+	ret = of_property_read_u32_array(dev->of_node, "rockchip,phy-config",
+		(u32 *)tcphy->config, sizeof(tcphy->config) / sizeof(u32));
+	if (ret)
+		memcpy(tcphy->config, tcphy_default_config,
+		       sizeof(tcphy->config));
+
 	return 0;
 }
 
diff --git a/drivers/phy/rockchip/phy-rockchip-usb.c b/drivers/phy/rockchip/phy-rockchip-usb.c
index 845428597..a891018fe 100644
--- a/drivers/phy/rockchip/phy-rockchip-usb.c
+++ b/drivers/phy/rockchip/phy-rockchip-usb.c
@@ -8,20 +8,26 @@
 
 #include <linux/clk.h>
 #include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/extcon-provider.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/of_irq.h>
 #include <linux/of_platform.h>
 #include <linux/phy/phy.h>
 #include <linux/platform_device.h>
+#include <linux/power_supply.h>
 #include <linux/regulator/consumer.h>
 #include <linux/reset.h>
 #include <linux/regmap.h>
-#include <linux/mfd/syscon.h>
-#include <linux/delay.h>
+#include <linux/usb/of.h>
+#include <linux/wakelock.h>
 
 static int enable_usb_uart;
 
@@ -45,6 +51,69 @@ static int enable_usb_uart;
 #define UOC_CON3_UTMI_OPMODE_MASK			(3 << 1)
 #define UOC_CON3_UTMI_SUSPENDN				BIT(0)
 
+#define RK3288_UOC0_CON0				0x320
+#define RK3288_UOC0_CON0_COMMON_ON_N			BIT(0)
+#define RK3288_UOC0_CON0_DISABLE			BIT(4)
+
+#define RK3288_UOC0_CON2				0x328
+#define RK3288_UOC0_CON2_SOFT_CON_SEL			BIT(2)
+#define RK3288_UOC0_CON2_CHRGSEL			BIT(5)
+#define RK3288_UOC0_CON2_VDATDETENB			BIT(6)
+#define RK3288_UOC0_CON2_VDATSRCENB			BIT(7)
+#define RK3288_UOC0_CON2_DCDENB				BIT(14)
+
+#define RK3288_UOC0_CON3				0x32c
+#define RK3288_UOC0_CON3_UTMI_SUSPENDN			BIT(0)
+#define RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING		BIT(1)
+#define RK3288_UOC0_CON3_UTMI_OPMODE_MASK		(3 << 1)
+#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC	BIT(3)
+#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK		(3 << 3)
+#define RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED		BIT(5)
+#define RK3288_UOC0_CON3_BYPASSDMEN			BIT(6)
+#define RK3288_UOC0_CON3_BYPASSSEL			BIT(7)
+#define RK3288_UOC0_CON3_IDDIG_SET_OTG			(0 << 12)
+#define RK3288_UOC0_CON3_IDDIG_SET_HOST			(2 << 12)
+#define RK3288_UOC0_CON3_IDDIG_SET_PERIPHERAL		(3 << 12)
+#define RK3288_UOC0_CON3_IDDIG_SET_MASK			(3 << 12)
+
+#define RK3288_UOC0_CON4				0x330
+#define RK3288_UOC0_CON4_BVALID_IRQ_EN			BIT(2)
+#define RK3288_UOC0_CON4_BVALID_IRQ_PD			BIT(3)
+
+#define RK3288_SOC_STATUS2				0x288
+#define RK3288_SOC_STATUS2_UTMISRP_BVALID		BIT(14)
+#define RK3288_SOC_STATUS2_UTMIOTG_IDDIG		BIT(17)
+
+#define RK3288_SOC_STATUS19				0x2cc
+#define RK3288_SOC_STATUS19_CHGDET			BIT(23)
+#define RK3288_SOC_STATUS19_FSVPLUS			BIT(24)
+#define RK3288_SOC_STATUS19_FSVMINUS			BIT(25)
+
+#define OTG_SCHEDULE_DELAY				(1 * HZ)
+#define CHG_DCD_POLL_TIME				(100 * HZ / 1000)
+#define CHG_DCD_MAX_RETRIES				6
+#define CHG_PRIMARY_DET_TIME				(40 * HZ / 1000)
+#define CHG_SECONDARY_DET_TIME				(40 * HZ / 1000)
+
+enum usb_chg_state {
+	USB_CHG_STATE_UNDEFINED = 0,
+	USB_CHG_STATE_WAIT_FOR_DCD,
+	USB_CHG_STATE_DCD_DONE,
+	USB_CHG_STATE_PRIMARY_DONE,
+	USB_CHG_STATE_SECONDARY_DONE,
+	USB_CHG_STATE_DETECTED,
+};
+
+static const unsigned int rockchip_usb_phy_extcon_cable[] = {
+	EXTCON_USB,
+	EXTCON_USB_HOST,
+	EXTCON_USB_VBUS_EN,
+	EXTCON_CHG_USB_SDP,
+	EXTCON_CHG_USB_CDP,
+	EXTCON_CHG_USB_DCP,
+	EXTCON_NONE,
+};
+
 struct rockchip_usb_phys {
 	int reg;
 	const char *pll_name;
@@ -61,20 +130,127 @@ struct rockchip_usb_phy_pdata {
 struct rockchip_usb_phy_base {
 	struct device *dev;
 	struct regmap *reg_base;
+	struct extcon_dev *edev;
 	const struct rockchip_usb_phy_pdata *pdata;
 };
 
 struct rockchip_usb_phy {
 	struct rockchip_usb_phy_base *base;
-	struct device_node *np;
-	unsigned int	reg_offset;
-	struct clk	*clk;
-	struct clk      *clk480m;
-	struct clk_hw	clk480m_hw;
-	struct phy	*phy;
-	bool		uart_enabled;
-	struct reset_control *reset;
-	struct regulator *vbus;
+	struct device_node	*np;
+	unsigned int		reg_offset;
+	struct clk		*clk;
+	struct clk		*clk480m;
+	struct clk_hw		clk480m_hw;
+	struct phy		*phy;
+	bool			uart_enabled;
+	int			bvalid_irq;
+	struct reset_control	*reset;
+	struct regulator	*vbus;
+	struct mutex		mutex; /* protects registers of phy */
+	struct delayed_work	chg_work;
+	struct delayed_work	otg_sm_work;
+	struct wake_lock	wakelock;
+	enum usb_chg_state	chg_state;
+	enum power_supply_type	chg_type;
+	enum usb_dr_mode	mode;
+};
+
+static ssize_t otg_mode_show(struct device *dev,
+			     struct device_attribute *attr, char *buf)
+{
+	struct rockchip_usb_phy *rk_phy = dev_get_drvdata(dev);
+
+	if (!rk_phy) {
+		dev_err(dev, "Fail to get otg phy.\n");
+		return -EINVAL;
+	}
+
+	switch (rk_phy->mode) {
+	case USB_DR_MODE_HOST:
+		return sprintf(buf, "host\n");
+	case USB_DR_MODE_PERIPHERAL:
+		return sprintf(buf, "peripheral\n");
+	case USB_DR_MODE_OTG:
+		return sprintf(buf, "otg\n");
+	case USB_DR_MODE_UNKNOWN:
+		return sprintf(buf, "UNKNOWN\n");
+	default:
+		break;
+	}
+
+	return -EINVAL;
+}
+
+static ssize_t otg_mode_store(struct device *dev, struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct rockchip_usb_phy *rk_phy = dev_get_drvdata(dev);
+	enum usb_dr_mode new_dr_mode;
+	int ret = count;
+	int val = 0;
+
+	if (!rk_phy) {
+		dev_err(dev, "Fail to get otg phy.\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&rk_phy->mutex);
+
+	if (!strncmp(buf, "0", 1) || !strncmp(buf, "otg", 3)) {
+		new_dr_mode = USB_DR_MODE_OTG;
+	} else if (!strncmp(buf, "1", 1) || !strncmp(buf, "host", 4)) {
+		new_dr_mode = USB_DR_MODE_HOST;
+	} else if (!strncmp(buf, "2", 1) || !strncmp(buf, "peripheral", 10)) {
+		new_dr_mode = USB_DR_MODE_PERIPHERAL;
+	} else {
+		dev_err(&rk_phy->phy->dev, "Error mode! Input 'otg' or 'host' or 'peripheral'\n");
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
+	if (rk_phy->mode == new_dr_mode) {
+		dev_warn(&rk_phy->phy->dev, "Same as current mode.\n");
+		goto out_unlock;
+	}
+
+	rk_phy->mode = new_dr_mode;
+
+	switch (rk_phy->mode) {
+	case USB_DR_MODE_HOST:
+		val = HIWORD_UPDATE(RK3288_UOC0_CON3_IDDIG_SET_HOST,
+				    RK3288_UOC0_CON3_IDDIG_SET_MASK);
+		break;
+	case USB_DR_MODE_PERIPHERAL:
+		val = HIWORD_UPDATE(RK3288_UOC0_CON3_IDDIG_SET_PERIPHERAL,
+				    RK3288_UOC0_CON3_IDDIG_SET_MASK);
+		break;
+	case USB_DR_MODE_OTG:
+		val = HIWORD_UPDATE(RK3288_UOC0_CON3_IDDIG_SET_OTG,
+				    RK3288_UOC0_CON3_IDDIG_SET_MASK);
+		break;
+	default:
+		break;
+	}
+
+	regmap_write(rk_phy->base->reg_base, RK3288_UOC0_CON3, val);
+
+out_unlock:
+	mutex_unlock(&rk_phy->mutex);
+
+	return ret;
+}
+
+static DEVICE_ATTR_RW(otg_mode);
+
+/* Group all the usb2 phy attributes */
+static struct attribute *usb2_phy_attrs[] = {
+	&dev_attr_otg_mode.attr,
+	NULL,
+};
+
+static struct attribute_group usb2_phy_attr_group = {
+	.name = NULL, /* we want them in the same directory */
+	.attrs = usb2_phy_attrs,
 };
 
 static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
@@ -136,6 +312,46 @@ static const struct clk_ops rockchip_usb_phy480m_ops = {
 	.recalc_rate = rockchip_usb_phy480m_recalc_rate,
 };
 
+static int rk3288_usb_phy_init(struct phy *_phy)
+{
+	struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
+	int ret = 0;
+	unsigned int val;
+
+	if (phy->bvalid_irq > 0) {
+		mutex_lock(&phy->mutex);
+
+		/* clear bvalid status and enable bvalid detect irq */
+		val = HIWORD_UPDATE(RK3288_UOC0_CON4_BVALID_IRQ_EN
+					| RK3288_UOC0_CON4_BVALID_IRQ_PD,
+				    RK3288_UOC0_CON4_BVALID_IRQ_EN
+					| RK3288_UOC0_CON4_BVALID_IRQ_PD);
+		ret = regmap_write(phy->base->reg_base, RK3288_UOC0_CON4, val);
+		if (ret) {
+			dev_err(phy->base->dev,
+				"failed to enable bvalid irq\n");
+			goto out;
+		}
+
+		schedule_delayed_work(&phy->otg_sm_work, OTG_SCHEDULE_DELAY);
+
+out:
+		mutex_unlock(&phy->mutex);
+	}
+
+	return ret;
+}
+
+static int rk3288_usb_phy_exit(struct phy *_phy)
+{
+	struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
+
+	if (phy->bvalid_irq > 0)
+		flush_delayed_work(&phy->otg_sm_work);
+
+	return 0;
+}
+
 static int rockchip_usb_phy_power_off(struct phy *_phy)
 {
 	struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
@@ -179,7 +395,7 @@ static int rockchip_usb_phy_reset(struct phy *_phy)
 	return 0;
 }
 
-static const struct phy_ops ops = {
+static struct phy_ops ops = {
 	.power_on	= rockchip_usb_phy_power_on,
 	.power_off	= rockchip_usb_phy_power_off,
 	.reset		= rockchip_usb_phy_reset,
@@ -199,13 +415,383 @@ static void rockchip_usb_phy_action(void *data)
 		clk_put(rk_phy->clk);
 }
 
+static int rockchip_usb_phy_extcon_register(struct rockchip_usb_phy_base *base)
+{
+	int ret;
+	struct device_node *node = base->dev->of_node;
+	struct extcon_dev *edev;
+
+	if (of_property_read_bool(node, "extcon")) {
+		edev = extcon_get_edev_by_phandle(base->dev, 0);
+		if (IS_ERR(edev)) {
+			if (PTR_ERR(edev) != -EPROBE_DEFER)
+				dev_err(base->dev,
+					"Invalid or missing extcon\n");
+			return PTR_ERR(edev);
+		}
+	} else {
+		/* Initialize extcon device */
+		edev = devm_extcon_dev_allocate(base->dev,
+						rockchip_usb_phy_extcon_cable);
+
+		if (IS_ERR(edev))
+			return -ENOMEM;
+
+		ret = devm_extcon_dev_register(base->dev, edev);
+		if (ret) {
+			dev_err(base->dev,
+				"failed to register extcon device\n");
+			return ret;
+		}
+	}
+
+	base->edev = edev;
+
+	return 0;
+}
+
+static void rk3288_usb_phy_otg_sm_work(struct work_struct *work)
+{
+	struct rockchip_usb_phy *rk_phy = container_of(work,
+						       struct rockchip_usb_phy,
+						       otg_sm_work.work);
+	unsigned int val;
+	static unsigned int cable;
+	static bool chg_det_completed;
+	bool sch_work;
+	bool vbus_attached;
+	bool id;
+
+	mutex_lock(&rk_phy->mutex);
+
+	sch_work = false;
+
+	regmap_read(rk_phy->base->reg_base, RK3288_SOC_STATUS2, &val);
+	id = (val & RK3288_SOC_STATUS2_UTMIOTG_IDDIG) ? true : false;
+
+	regmap_read(rk_phy->base->reg_base, RK3288_SOC_STATUS2, &val);
+	vbus_attached =
+		(val & RK3288_SOC_STATUS2_UTMISRP_BVALID) ? true : false;
+
+	if (!vbus_attached || !id || rk_phy->mode == USB_DR_MODE_HOST) {
+		dev_dbg(&rk_phy->phy->dev, "peripheral disconnected\n");
+		wake_unlock(&rk_phy->wakelock);
+		extcon_set_state_sync(rk_phy->base->edev, cable, false);
+		rk_phy->chg_state = USB_CHG_STATE_UNDEFINED;
+		chg_det_completed = false;
+		goto out;
+	}
+
+	if (chg_det_completed) {
+		sch_work = true;
+		goto out;
+	}
+
+	switch (rk_phy->chg_state) {
+	case USB_CHG_STATE_UNDEFINED:
+		mutex_unlock(&rk_phy->mutex);
+		schedule_delayed_work(&rk_phy->chg_work, 0);
+		return;
+	case USB_CHG_STATE_DETECTED:
+		switch (rk_phy->chg_type) {
+		case POWER_SUPPLY_TYPE_USB:
+			dev_dbg(&rk_phy->phy->dev, "sdp cable is connected\n");
+			wake_lock(&rk_phy->wakelock);
+			cable = EXTCON_CHG_USB_SDP;
+			sch_work = true;
+			break;
+		case POWER_SUPPLY_TYPE_USB_DCP:
+			dev_dbg(&rk_phy->phy->dev, "dcp cable is connected\n");
+			cable = EXTCON_CHG_USB_DCP;
+			sch_work = true;
+			break;
+		case POWER_SUPPLY_TYPE_USB_CDP:
+			dev_dbg(&rk_phy->phy->dev, "cdp cable is connected\n");
+			wake_lock(&rk_phy->wakelock);
+			cable = EXTCON_CHG_USB_CDP;
+			sch_work = true;
+			break;
+		default:
+			break;
+		}
+		chg_det_completed = true;
+		break;
+	default:
+		break;
+	}
+
+	if (extcon_get_state(rk_phy->base->edev, cable) != vbus_attached)
+		extcon_set_state_sync(rk_phy->base->edev, cable,
+				      vbus_attached);
+
+out:
+	if (sch_work)
+		schedule_delayed_work(&rk_phy->otg_sm_work, OTG_SCHEDULE_DELAY);
+
+	mutex_unlock(&rk_phy->mutex);
+}
+
+static const char *chg_to_string(enum power_supply_type chg_type)
+{
+	switch (chg_type) {
+	case POWER_SUPPLY_TYPE_USB:
+		return "USB_SDP_CHARGER";
+	case POWER_SUPPLY_TYPE_USB_DCP:
+		return "USB_DCP_CHARGER";
+	case POWER_SUPPLY_TYPE_USB_CDP:
+		return "USB_CDP_CHARGER";
+	default:
+		return "INVALID_CHARGER";
+	}
+}
+
+static void rk3288_chg_detect_work(struct work_struct *work)
+{
+	struct rockchip_usb_phy *rk_phy =
+		container_of(work, struct rockchip_usb_phy, chg_work.work);
+	unsigned int val;
+	static int dcd_retries;
+	static int primary_retries;
+	unsigned long delay;
+	bool fsvplus;
+	bool vout;
+	bool tmout;
+
+	dev_dbg(&rk_phy->phy->dev, "chg detection work state = %d\n",
+		rk_phy->chg_state);
+
+	switch (rk_phy->chg_state) {
+	case USB_CHG_STATE_UNDEFINED:
+		mutex_lock(&rk_phy->mutex);
+		/* put the controller in non-driving mode */
+		val = HIWORD_UPDATE(RK3288_UOC0_CON2_SOFT_CON_SEL,
+				    RK3288_UOC0_CON2_SOFT_CON_SEL);
+		regmap_write(rk_phy->base->reg_base, RK3288_UOC0_CON2, val);
+		val = HIWORD_UPDATE(RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING,
+				    RK3288_UOC0_CON3_UTMI_SUSPENDN
+					| RK3288_UOC0_CON3_UTMI_OPMODE_MASK);
+		regmap_write(rk_phy->base->reg_base, RK3288_UOC0_CON3, val);
+		/* Start DCD processing stage 1 */
+		val = HIWORD_UPDATE(RK3288_UOC0_CON2_DCDENB,
+				    RK3288_UOC0_CON2_DCDENB);
+		regmap_write(rk_phy->base->reg_base, RK3288_UOC0_CON2, val);
+		rk_phy->chg_state = USB_CHG_STATE_WAIT_FOR_DCD;
+		dcd_retries = 0;
+		primary_retries = 0;
+		delay = CHG_DCD_POLL_TIME;
+		break;
+	case USB_CHG_STATE_WAIT_FOR_DCD:
+		/* get data contact detection status */
+		regmap_read(rk_phy->base->reg_base, RK3288_SOC_STATUS19, &val);
+		fsvplus = (val & RK3288_SOC_STATUS19_FSVPLUS) ? true : false;
+		tmout = ++dcd_retries == CHG_DCD_MAX_RETRIES;
+		/* stage 2 */
+		if (!fsvplus || tmout) {
+vdpsrc:
+			/* stage 4 */
+			/* Turn off DCD circuitry */
+			val = HIWORD_UPDATE(0, RK3288_UOC0_CON2_DCDENB);
+			regmap_write(rk_phy->base->reg_base,
+				     RK3288_UOC0_CON2, val);
+			/* Voltage Source on DP, Probe on DM */
+			val = HIWORD_UPDATE(RK3288_UOC0_CON2_VDATSRCENB
+						| RK3288_UOC0_CON2_VDATDETENB,
+					    RK3288_UOC0_CON2_VDATSRCENB
+						| RK3288_UOC0_CON2_VDATDETENB
+						| RK3288_UOC0_CON2_CHRGSEL);
+			regmap_write(rk_phy->base->reg_base,
+				     RK3288_UOC0_CON2, val);
+			delay = CHG_PRIMARY_DET_TIME;
+			rk_phy->chg_state = USB_CHG_STATE_DCD_DONE;
+		} else {
+			/* stage 3 */
+			delay = CHG_DCD_POLL_TIME;
+		}
+		break;
+	case USB_CHG_STATE_DCD_DONE:
+		regmap_read(rk_phy->base->reg_base, RK3288_SOC_STATUS19, &val);
+		vout = (val & RK3288_SOC_STATUS19_CHGDET) ? true : false;
+
+		val = HIWORD_UPDATE(0, RK3288_UOC0_CON2_VDATSRCENB
+					| RK3288_UOC0_CON2_VDATDETENB);
+		regmap_write(rk_phy->base->reg_base, RK3288_UOC0_CON2, val);
+		if (vout) {
+			/* Voltage Source on DM, Probe on DP  */
+			val = HIWORD_UPDATE(RK3288_UOC0_CON2_VDATSRCENB
+						| RK3288_UOC0_CON2_VDATDETENB
+						| RK3288_UOC0_CON2_CHRGSEL,
+					    RK3288_UOC0_CON2_VDATSRCENB
+						| RK3288_UOC0_CON2_VDATDETENB
+						| RK3288_UOC0_CON2_CHRGSEL);
+			regmap_write(rk_phy->base->reg_base,
+				     RK3288_UOC0_CON2, val);
+			delay = CHG_SECONDARY_DET_TIME;
+			rk_phy->chg_state = USB_CHG_STATE_PRIMARY_DONE;
+		} else {
+			if (dcd_retries == CHG_DCD_MAX_RETRIES) {
+				/* floating charger found */
+				rk_phy->chg_type = POWER_SUPPLY_TYPE_USB_DCP;
+				rk_phy->chg_state = USB_CHG_STATE_DETECTED;
+				delay = 0;
+			} else if (primary_retries < 2) {
+				primary_retries++;
+				goto vdpsrc;
+			} else {
+				rk_phy->chg_type = POWER_SUPPLY_TYPE_USB;
+				rk_phy->chg_state = USB_CHG_STATE_DETECTED;
+				delay = 0;
+			}
+		}
+		break;
+	case USB_CHG_STATE_PRIMARY_DONE:
+		regmap_read(rk_phy->base->reg_base, RK3288_SOC_STATUS19, &val);
+		vout = (val & RK3288_SOC_STATUS19_CHGDET) ? true : false;
+
+		/* Turn off voltage source */
+		val = HIWORD_UPDATE(0, RK3288_UOC0_CON2_VDATSRCENB
+					| RK3288_UOC0_CON2_VDATDETENB
+					| RK3288_UOC0_CON2_CHRGSEL);
+		regmap_write(rk_phy->base->reg_base, RK3288_UOC0_CON2, val);
+		if (vout)
+			rk_phy->chg_type = POWER_SUPPLY_TYPE_USB_DCP;
+		else
+			rk_phy->chg_type = POWER_SUPPLY_TYPE_USB_CDP;
+		fallthrough;
+	case USB_CHG_STATE_SECONDARY_DONE:
+		rk_phy->chg_state = USB_CHG_STATE_DETECTED;
+		fallthrough;
+	case USB_CHG_STATE_DETECTED:
+		/* put the controller in normal mode */
+		val = HIWORD_UPDATE(0, RK3288_UOC0_CON2_SOFT_CON_SEL);
+		regmap_write(rk_phy->base->reg_base, RK3288_UOC0_CON2, val);
+		val = HIWORD_UPDATE(RK3288_UOC0_CON3_UTMI_SUSPENDN,
+				    RK3288_UOC0_CON3_UTMI_SUSPENDN
+					| RK3288_UOC0_CON3_UTMI_OPMODE_MASK);
+		regmap_write(rk_phy->base->reg_base, RK3288_UOC0_CON3, val);
+		mutex_unlock(&rk_phy->mutex);
+		rk3288_usb_phy_otg_sm_work(&rk_phy->otg_sm_work.work);
+		dev_info(&rk_phy->phy->dev, "charger = %s\n",
+			 chg_to_string(rk_phy->chg_type));
+		return;
+	default:
+		mutex_unlock(&rk_phy->mutex);
+		return;
+	}
+
+	/*
+	 * Hold the mutex lock during the whole charger
+	 * detection stage, and release it after detect
+	 * the charger type.
+	 */
+	schedule_delayed_work(&rk_phy->chg_work, delay);
+}
+
+static irqreturn_t rk3288_usb_phy_bvalid_irq(int irq, void *data)
+{
+	struct rockchip_usb_phy *rk_phy = data;
+	int ret;
+	unsigned int val;
+
+	ret = regmap_read(rk_phy->base->reg_base, RK3288_UOC0_CON4, &val);
+	if (ret < 0 || !(val & RK3288_UOC0_CON4_BVALID_IRQ_PD))
+		return IRQ_NONE;
+
+	mutex_lock(&rk_phy->mutex);
+
+	/* clear bvalid detect irq pending status */
+	val = HIWORD_UPDATE(RK3288_UOC0_CON4_BVALID_IRQ_PD,
+			    RK3288_UOC0_CON4_BVALID_IRQ_PD);
+	regmap_write(rk_phy->base->reg_base, RK3288_UOC0_CON4, val);
+
+	mutex_unlock(&rk_phy->mutex);
+
+	if (rk_phy->uart_enabled)
+		goto out;
+
+	cancel_delayed_work_sync(&rk_phy->otg_sm_work);
+	rk3288_usb_phy_otg_sm_work(&rk_phy->otg_sm_work.work);
+out:
+	return IRQ_HANDLED;
+}
+
+static int rk3288_usb_phy_probe_init(struct rockchip_usb_phy *rk_phy)
+{
+	int ret = 0;
+	unsigned int val;
+
+	if (rk_phy->reg_offset == 0x320) {
+		/* Enable Bvalid interrupt and charge detection */
+		ops.init = rk3288_usb_phy_init;
+		ops.exit = rk3288_usb_phy_exit;
+		rk_phy->bvalid_irq = of_irq_get_byname(rk_phy->np,
+						       "otg-bvalid");
+		regmap_read(rk_phy->base->reg_base, RK3288_UOC0_CON4, &val);
+		if (rk_phy->bvalid_irq <= 0) {
+			dev_err(&rk_phy->phy->dev,
+				"no vbus valid irq provided\n");
+			ret = -EINVAL;
+			goto out;
+		}
+
+		ret = devm_request_threaded_irq(rk_phy->base->dev,
+						rk_phy->bvalid_irq,
+						NULL,
+						rk3288_usb_phy_bvalid_irq,
+						IRQF_ONESHOT,
+						"rockchip_usb_phy_bvalid",
+						rk_phy);
+		if (ret) {
+			dev_err(&rk_phy->phy->dev,
+				"failed to request otg-bvalid irq handle\n");
+			goto out;
+		}
+
+		rk_phy->chg_state = USB_CHG_STATE_UNDEFINED;
+		wake_lock_init(&rk_phy->wakelock, WAKE_LOCK_SUSPEND,
+			       "rockchip_otg");
+		INIT_DELAYED_WORK(&rk_phy->chg_work, rk3288_chg_detect_work);
+		INIT_DELAYED_WORK(&rk_phy->otg_sm_work,
+				  rk3288_usb_phy_otg_sm_work);
+
+		rk_phy->mode = of_usb_get_dr_mode_by_phy(rk_phy->np, -1);
+		if (rk_phy->mode == USB_DR_MODE_OTG ||
+		    rk_phy->mode == USB_DR_MODE_UNKNOWN) {
+			ret = sysfs_create_group(&rk_phy->phy->dev.kobj,
+						 &usb2_phy_attr_group);
+			if (ret) {
+				dev_err(&rk_phy->phy->dev,
+					"Cannot create sysfs group\n");
+				goto out;
+			}
+		}
+	} else if (rk_phy->reg_offset == 0x334) {
+		/*
+		 * Setting the COMMONONN to 1'b0 for EHCI PHY on RK3288 SoC.
+		 *
+		 * EHCI (auto) suspend causes the corresponding usb-phy into
+		 * suspend mode which would power down the inner PLL blocks in
+		 * usb-phy if the COMMONONN is set to 1'b1. The PLL output
+		 * clocks contained CLK480M, CLK12MOHCI, CLK48MOHCI, PHYCLOCK0
+		 * and so on, these clocks are not only supplied for EHCI and
+		 * OHCI, but also supplied for GPU and other external modules,
+		 * so setting COMMONONN to 1'b0 to keep the inner PLL blocks in
+		 * usb-phy always powered.
+		 */
+		regmap_write(rk_phy->base->reg_base, rk_phy->reg_offset,
+			     BIT(16));
+	}
+out:
+	return ret;
+}
+
 static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
 				 struct device_node *child)
 {
+	struct device_node *np = base->dev->of_node;
 	struct rockchip_usb_phy *rk_phy;
 	unsigned int reg_offset;
 	const char *clk_name;
-	struct clk_init_data init;
+	struct clk_init_data init = {};
 	int err, i;
 
 	rk_phy = devm_kzalloc(base->dev, sizeof(*rk_phy), GFP_KERNEL);
@@ -214,6 +800,7 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
 
 	rk_phy->base = base;
 	rk_phy->np = child;
+	mutex_init(&rk_phy->mutex);
 
 	if (of_property_read_u32(child, "reg", &reg_offset)) {
 		dev_err(base->dev, "missing reg property in node %pOFn\n",
@@ -288,6 +875,12 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
 	}
 	phy_set_drvdata(rk_phy->phy, rk_phy);
 
+	if (of_device_is_compatible(np, "rockchip,rk3288-usb-phy")) {
+		err = rk3288_usb_phy_probe_init(rk_phy);
+		if (err)
+			return err;
+	}
+
 	rk_phy->vbus = devm_regulator_get_optional(&rk_phy->phy->dev, "vbus");
 	if (IS_ERR(rk_phy->vbus)) {
 		if (PTR_ERR(rk_phy->vbus) == -EPROBE_DEFER)
@@ -402,10 +995,6 @@ static const struct rockchip_usb_phy_pdata rk3188_pdata = {
 	.usb_uart_phy = 0,
 };
 
-#define RK3288_UOC0_CON3				0x32c
-#define RK3288_UOC0_CON3_BYPASSDMEN			BIT(6)
-#define RK3288_UOC0_CON3_BYPASSSEL			BIT(7)
-
 /*
  * Enable the bypass of uart2 data through the otg usb phy.
  * Original description in the TRM.
@@ -487,6 +1076,10 @@ static int rockchip_usb_phy_probe(struct platform_device *pdev)
 		return PTR_ERR(phy_base->reg_base);
 	}
 
+	err = rockchip_usb_phy_extcon_register(phy_base);
+	if (err)
+		return err;
+
 	for_each_available_child_of_node(dev->of_node, child) {
 		err = rockchip_usb_phy_init(phy_base, child);
 		if (err) {
@@ -496,6 +1089,7 @@ static int rockchip_usb_phy_probe(struct platform_device *pdev)
 	}
 
 	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
 	return PTR_ERR_OR_ZERO(phy_provider);
 }
 
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 815095326..6782f1ae2 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -207,13 +207,18 @@ config PINCTRL_OXNAS
 	select MFD_SYSCON
 
 config PINCTRL_ROCKCHIP
-	bool
-	depends on OF
+	tristate "Rockchip gpio and pinctrl driver"
+	depends on ARCH_ROCKCHIP || COMPILE_TEST
+	depends on OF || ACPI
+	select GPIOLIB
 	select PINMUX
 	select GENERIC_PINCONF
 	select GENERIC_IRQ_CHIP
 	select MFD_SYSCON
 	select OF_GPIO
+	default ARCH_ROCKCHIP
+	help
+          This support pinctrl and gpio driver for Rockchip SoCs.
 
 config PINCTRL_SINGLE
 	tristate "One-register-per-pin type device tree based pinctrl driver"
diff --git a/drivers/pinctrl/pinctrl-rk805.c b/drivers/pinctrl/pinctrl-rk805.c
index c6f4229eb..46e426cf4 100644
--- a/drivers/pinctrl/pinctrl-rk805.c
+++ b/drivers/pinctrl/pinctrl-rk805.c
@@ -78,6 +78,7 @@ struct rk805_pctrl_info {
 
 enum rk805_pinmux_option {
 	RK805_PINMUX_GPIO,
+	RK805_PINMUX_TS,
 };
 
 enum {
@@ -132,12 +133,167 @@ static const struct rk805_pin_config rk805_gpio_cfgs[] = {
 	},
 };
 
+#define RK816_FUN_MASK		BIT(2)
+#define RK816_VAL_MASK		BIT(3)
+#define RK816_DIR_MASK		BIT(4)
+
+enum {
+	RK816_GPIO0,
+};
+
+/* RK816: gpio/ts */
+static const char *const rk816_gpio_groups[] = {
+	"gpio0",
+};
+
+static const struct pinctrl_pin_desc rk816_pins_desc[] = {
+	PINCTRL_PIN(RK816_GPIO0, "gpio0"),
+};
+
+static const struct rk805_pin_function rk816_pin_functions[] = {
+	{
+		.name = "gpio",
+		.groups = rk816_gpio_groups,
+		.ngroups = ARRAY_SIZE(rk816_gpio_groups),
+		.mux_option = RK805_PINMUX_GPIO,
+	},
+	{
+		.name = "ts",
+		.groups = rk816_gpio_groups,
+		.ngroups = ARRAY_SIZE(rk816_gpio_groups),
+		.mux_option = RK805_PINMUX_TS,
+	},
+};
+
+static const struct rk805_pin_group rk816_pin_groups[] = {
+	{
+		.name = "gpio0",
+		.pins = { RK816_GPIO0 },
+		.npins = 1,
+	},
+};
+
+static struct rk805_pin_config rk816_gpio_cfgs[] = {
+	{
+		.reg = RK816_GPIO_IO_POL_REG,
+		.val_msk = RK816_VAL_MASK,
+		.fun_msk = RK816_FUN_MASK,
+		.dir_msk = RK816_DIR_MASK,
+	},
+};
+
+enum rk817_pinmux_option {
+	RK817_PINMUX_FUN0 = 0,
+	RK817_PINMUX_FUN1,
+	RK817_PINMUX_FUN2,
+	RK817_PINMUX_FUN3
+};
+
+enum {
+	RK817_GPIO_SLP,
+	RK817_GPIO_TS,
+	RK817_GPIO_GT
+};
+
+/* for rk809 only a sleep pin */
+static const char *const rk817_gpio_groups[] = {
+	"gpio_slp",
+	"gpio_ts",
+	"gpio_gt",
+};
+
+static const struct pinctrl_pin_desc rk817_pins_desc[] = {
+	PINCTRL_PIN(RK817_GPIO_SLP, "gpio_slp"), /* sleep pin */
+	PINCTRL_PIN(RK817_GPIO_TS, "gpio_ts"), /* ts pin */
+	PINCTRL_PIN(RK817_GPIO_GT, "gpio_gt")/* gate pin */
+};
+
+static const struct rk805_pin_function rk817_pin_functions[] = {
+	{
+		.name = "pin_fun0",
+		.groups = rk817_gpio_groups,
+		.ngroups = ARRAY_SIZE(rk817_gpio_groups),
+		.mux_option = RK817_PINMUX_FUN0,
+	},
+	{
+		.name = "pin_fun1",
+		.groups = rk817_gpio_groups,
+		.ngroups = ARRAY_SIZE(rk817_gpio_groups),
+		.mux_option = RK817_PINMUX_FUN1,
+	},
+	{
+		.name = "pin_fun2",
+		.groups = rk817_gpio_groups,
+		.ngroups = ARRAY_SIZE(rk817_gpio_groups),
+		.mux_option = RK817_PINMUX_FUN2,
+	},
+	{
+		.name = "pin_fun3",
+		.groups = rk817_gpio_groups,
+		.ngroups = ARRAY_SIZE(rk817_gpio_groups),
+		.mux_option = RK817_PINMUX_FUN3,
+	},
+};
+
+/* for rk809 only a sleep pin */
+static const struct rk805_pin_group rk817_pin_groups[] = {
+	{
+		.name = "gpio_slp",
+		.pins = { RK817_GPIO_SLP },
+		.npins = 1,
+	},
+	{
+		.name = "gpio_ts",
+		.pins = { RK817_GPIO_TS },
+		.npins = 1,
+	},
+	{
+		.name = "gpio_gt",
+		.pins = { RK817_GPIO_GT },
+		.npins = 1,
+	}
+};
+
+#define RK817_GPIOTS_VAL_MSK	BIT(3)
+#define RK817_GPIOGT_VAL_MSK	BIT(6)
+#define RK817_GPIOTS_FUNC_MSK	BIT(2)
+#define RK817_GPIOGT_FUNC_MSK	BIT(5)
+#define RK817_GPIOTS_DIR_MSK	BIT(4)
+#define RK817_GPIOGT_DIR_MSK	BIT(7)
+
+static struct rk805_pin_config rk817_gpio_cfgs[] = {
+	{
+		.reg = RK817_SYS_CFG(3),
+		.val_msk = 0,
+		.fun_msk = RK817_SLPPIN_FUNC_MSK,
+		.dir_msk = 0
+	},
+	{
+		.reg = RK817_GPIO_INT_CFG,
+		.val_msk = RK817_GPIOTS_VAL_MSK,
+		.fun_msk = RK817_GPIOTS_FUNC_MSK,
+		.dir_msk = RK817_GPIOTS_DIR_MSK
+	},
+	{
+		.reg = RK817_GPIO_INT_CFG,
+		.val_msk = RK817_GPIOGT_VAL_MSK,
+		.fun_msk = RK817_GPIOGT_FUNC_MSK,
+		.dir_msk = RK817_GPIOGT_DIR_MSK
+	}
+};
+
 /* generic gpio chip */
 static int rk805_gpio_get(struct gpio_chip *chip, unsigned int offset)
 {
 	struct rk805_pctrl_info *pci = gpiochip_get_data(chip);
 	int ret, val;
 
+	if (!pci->pin_cfg[offset].val_msk) {
+		dev_dbg(pci->dev, "getting gpio%d value is not support\n",
+			offset);
+		return -1;
+	}
+
 	ret = regmap_read(pci->rk808->regmap, pci->pin_cfg[offset].reg, &val);
 	if (ret) {
 		dev_err(pci->dev, "get gpio%d value failed\n", offset);
@@ -154,6 +310,9 @@ static void rk805_gpio_set(struct gpio_chip *chip,
 	struct rk805_pctrl_info *pci = gpiochip_get_data(chip);
 	int ret;
 
+	if (!pci->pin_cfg[offset].val_msk)
+		return;
+
 	ret = regmap_update_bits(pci->rk808->regmap,
 				 pci->pin_cfg[offset].reg,
 				 pci->pin_cfg[offset].val_msk,
@@ -214,6 +373,34 @@ static const struct gpio_chip rk805_gpio_chip = {
 	.owner			= THIS_MODULE,
 };
 
+static struct gpio_chip rk816_gpio_chip = {
+	.label			= "rk816-gpio",
+	.request		= gpiochip_generic_request,
+	.free			= gpiochip_generic_free,
+	.get_direction		= rk805_gpio_get_direction,
+	.get			= rk805_gpio_get,
+	.set			= rk805_gpio_set,
+	.direction_input	= rk805_gpio_direction_input,
+	.direction_output	= rk805_gpio_direction_output,
+	.can_sleep		= true,
+	.base			= -1,
+	.owner			= THIS_MODULE,
+};
+
+static struct gpio_chip rk817_gpio_chip = {
+	.label			= "rk817-gpio",
+	.request		= gpiochip_generic_request,
+	.free			= gpiochip_generic_free,
+	.get_direction		= rk805_gpio_get_direction,
+	.get			= rk805_gpio_get,
+	.set			= rk805_gpio_set,
+	.direction_input	= rk805_gpio_direction_input,
+	.direction_output	= rk805_gpio_direction_output,
+	.can_sleep		= true,
+	.base			= -1,
+	.owner			= THIS_MODULE,
+};
+
 /* generic pinctrl */
 static int rk805_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
 {
@@ -289,7 +476,7 @@ static int _rk805_pinctrl_set_mux(struct pinctrl_dev *pctldev,
 	if (!pci->pin_cfg[offset].fun_msk)
 		return 0;
 
-	if (mux == RK805_PINMUX_GPIO) {
+	if (mux == RK805_PINMUX_GPIO)  {
 		ret = regmap_update_bits(pci->rk808->regmap,
 					 pci->pin_cfg[offset].reg,
 					 pci->pin_cfg[offset].fun_msk,
@@ -298,6 +485,15 @@ static int _rk805_pinctrl_set_mux(struct pinctrl_dev *pctldev,
 			dev_err(pci->dev, "set gpio%d GPIO failed\n", offset);
 			return ret;
 		}
+	} else if (mux == RK805_PINMUX_TS) {
+		ret = regmap_update_bits(pci->rk808->regmap,
+					 pci->pin_cfg[offset].reg,
+					 pci->pin_cfg[offset].fun_msk,
+					 0);
+		if (ret) {
+			dev_err(pci->dev, "set gpio%d TS failed\n", offset);
+			return ret;
+		}
 	} else {
 		dev_err(pci->dev, "Couldn't find function mux %d\n", mux);
 		return -EINVAL;
@@ -306,6 +502,27 @@ static int _rk805_pinctrl_set_mux(struct pinctrl_dev *pctldev,
 	return 0;
 }
 
+static int _rk817_pinctrl_set_mux(struct pinctrl_dev *pctldev,
+				  unsigned int offset,
+				  int mux)
+{
+	struct rk805_pctrl_info *pci = pinctrl_dev_get_drvdata(pctldev);
+	int ret;
+
+	if (!pci->pin_cfg[offset].fun_msk)
+		return 0;
+
+	mux <<= ffs(pci->pin_cfg[offset].fun_msk) - 1;
+	ret = regmap_update_bits(pci->rk808->regmap,
+				 pci->pin_cfg[offset].reg,
+				 pci->pin_cfg[offset].fun_msk, mux);
+
+	if (ret)
+		dev_err(pci->dev, "set gpio%d func%d failed\n", offset, mux);
+
+	return ret;
+}
+
 static int rk805_pinctrl_set_mux(struct pinctrl_dev *pctldev,
 				 unsigned int function,
 				 unsigned int group)
@@ -314,7 +531,18 @@ static int rk805_pinctrl_set_mux(struct pinctrl_dev *pctldev,
 	int mux = pci->functions[function].mux_option;
 	int offset = group;
 
-	return _rk805_pinctrl_set_mux(pctldev, offset, mux);
+	switch (pci->rk808->variant) {
+	case RK805_ID:
+	case RK816_ID:
+		return _rk805_pinctrl_set_mux(pctldev, offset, mux);
+
+	case RK809_ID:
+	case RK817_ID:
+		return _rk817_pinctrl_set_mux(pctldev, offset, mux);
+	default:
+		dev_err(pci->dev, "Couldn't find the variant id\n");
+		return -EINVAL;
+	}
 }
 
 static int rk805_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
@@ -324,13 +552,6 @@ static int rk805_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
 	struct rk805_pctrl_info *pci = pinctrl_dev_get_drvdata(pctldev);
 	int ret;
 
-	/* switch to gpio function */
-	ret = _rk805_pinctrl_set_mux(pctldev, offset, RK805_PINMUX_GPIO);
-	if (ret) {
-		dev_err(pci->dev, "set gpio%d mux failed\n", offset);
-		return ret;
-	}
-
 	/* set direction */
 	if (!pci->pin_cfg[offset].dir_msk)
 		return 0;
@@ -347,7 +568,25 @@ static int rk805_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
 	return ret;
 }
 
+static int rk805_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev,
+					     struct pinctrl_gpio_range *range,
+					     unsigned int offset)
+{
+	struct rk805_pctrl_info *pci = pinctrl_dev_get_drvdata(pctldev);
+
+	/* switch to gpio function */
+	switch (pci->rk808->variant) {
+	case RK805_ID:
+	case RK816_ID:
+		return _rk805_pinctrl_set_mux(pctldev, offset,
+					      RK805_PINMUX_GPIO);
+	default:
+		return 0;
+	}
+}
+
 static const struct pinmux_ops rk805_pinmux_ops = {
+	.gpio_request_enable	= rk805_pinctrl_gpio_request_enable,
 	.get_functions_count	= rk805_pinctrl_get_funcs_count,
 	.get_function_name	= rk805_pinctrl_get_func_name,
 	.get_function_groups	= rk805_pinctrl_get_func_groups,
@@ -364,6 +603,7 @@ static int rk805_pinconf_get(struct pinctrl_dev *pctldev,
 
 	switch (param) {
 	case PIN_CONFIG_OUTPUT:
+	case PIN_CONFIG_INPUT_ENABLE:
 		arg = rk805_gpio_get(&pci->gpio_chip, pin);
 		break;
 	default:
@@ -390,8 +630,13 @@ static int rk805_pinconf_set(struct pinctrl_dev *pctldev,
 
 		switch (param) {
 		case PIN_CONFIG_OUTPUT:
-			rk805_gpio_set(&pci->gpio_chip, pin, arg);
 			rk805_pmx_gpio_set_direction(pctldev, NULL, pin, false);
+			rk805_gpio_set(&pci->gpio_chip, pin, arg);
+			break;
+		case PIN_CONFIG_INPUT_ENABLE:
+			if (arg)
+				rk805_pmx_gpio_set_direction(pctldev, NULL,
+							     pin, true);
 			break;
 		default:
 			dev_err(pci->dev, "Properties not supported\n");
@@ -415,9 +660,26 @@ static const struct pinctrl_desc rk805_pinctrl_desc = {
 	.owner = THIS_MODULE,
 };
 
+static struct pinctrl_desc rk816_pinctrl_desc = {
+	.name = "rk816-pinctrl",
+	.pctlops = &rk805_pinctrl_ops,
+	.pmxops = &rk805_pinmux_ops,
+	.confops = &rk805_pinconf_ops,
+	.owner = THIS_MODULE,
+};
+
+static struct pinctrl_desc rk817_pinctrl_desc = {
+	.name = "rk817-pinctrl",
+	.pctlops = &rk805_pinctrl_ops,
+	.pmxops = &rk805_pinmux_ops,
+	.confops = &rk805_pinconf_ops,
+	.owner = THIS_MODULE,
+};
+
 static int rk805_pinctrl_probe(struct platform_device *pdev)
 {
 	struct rk805_pctrl_info *pci;
+	struct device_node *np;
 	int ret;
 
 	pci = devm_kzalloc(&pdev->dev, sizeof(*pci), GFP_KERNEL);
@@ -425,18 +687,19 @@ static int rk805_pinctrl_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	pci->dev = &pdev->dev;
-	pci->dev->of_node = pdev->dev.parent->of_node;
+	np = of_get_child_by_name(pdev->dev.parent->of_node, "pinctrl_rk8xx");
+	if (np)
+		pci->dev->of_node = np;
+	else
+		pci->dev->of_node = pdev->dev.parent->of_node;
 	pci->rk808 = dev_get_drvdata(pdev->dev.parent);
 
-	pci->pinctrl_desc = rk805_pinctrl_desc;
-	pci->gpio_chip = rk805_gpio_chip;
-	pci->gpio_chip.parent = &pdev->dev;
-	pci->gpio_chip.of_node = pdev->dev.parent->of_node;
-
 	platform_set_drvdata(pdev, pci);
 
 	switch (pci->rk808->variant) {
 	case RK805_ID:
+		pci->pinctrl_desc = rk805_pinctrl_desc;
+		pci->gpio_chip = rk805_gpio_chip;
 		pci->pins = rk805_pins_desc;
 		pci->num_pins = ARRAY_SIZE(rk805_pins_desc);
 		pci->functions = rk805_pin_functions;
@@ -448,13 +711,59 @@ static int rk805_pinctrl_probe(struct platform_device *pdev)
 		pci->pin_cfg = rk805_gpio_cfgs;
 		pci->gpio_chip.ngpio = ARRAY_SIZE(rk805_gpio_cfgs);
 		break;
+
+	case RK816_ID:
+		pci->pinctrl_desc = rk816_pinctrl_desc;
+		pci->gpio_chip = rk816_gpio_chip;
+		pci->pins = rk816_pins_desc;
+		pci->num_pins = ARRAY_SIZE(rk816_pins_desc);
+		pci->functions = rk816_pin_functions;
+		pci->num_functions = ARRAY_SIZE(rk816_pin_functions);
+		pci->groups = rk816_pin_groups;
+		pci->num_pin_groups = ARRAY_SIZE(rk816_pin_groups);
+		pci->pinctrl_desc.pins = rk816_pins_desc;
+		pci->pinctrl_desc.npins = ARRAY_SIZE(rk816_pins_desc);
+		pci->pin_cfg = rk816_gpio_cfgs;
+		pci->gpio_chip.ngpio = ARRAY_SIZE(rk816_gpio_cfgs);
+		break;
+
+	case RK809_ID:
+	case RK817_ID:
+		pci->pinctrl_desc = rk817_pinctrl_desc;
+		pci->gpio_chip = rk817_gpio_chip;
+		pci->pins = rk817_pins_desc;
+		pci->num_pins = ARRAY_SIZE(rk817_pins_desc);
+		pci->functions = rk817_pin_functions;
+		pci->num_functions = ARRAY_SIZE(rk817_pin_functions);
+		pci->groups = rk817_pin_groups;
+		pci->num_pin_groups = ARRAY_SIZE(rk817_pin_groups);
+		pci->pinctrl_desc.pins = rk817_pins_desc;
+		pci->pinctrl_desc.npins = ARRAY_SIZE(rk817_pins_desc);
+		pci->pin_cfg = rk817_gpio_cfgs;
+		pci->gpio_chip.ngpio = ARRAY_SIZE(rk817_gpio_cfgs);
+		/* for rk809 only a sleep pin */
+		if (pci->rk808->variant == RK809_ID) {
+			pci->pinctrl_desc.npins = 1;
+			pci->num_pin_groups = 1;
+			pci->num_pins = 1;
+			pci->gpio_chip.ngpio = 1;
+		}
+		break;
+
 	default:
 		dev_err(&pdev->dev, "unsupported RK805 ID %lu\n",
 			pci->rk808->variant);
 		return -EINVAL;
 	}
 
-	/* Add gpio chip */
+	pci->gpio_chip.parent = &pdev->dev;
+
+	if (np)
+		pci->gpio_chip.of_node = np;
+	else
+		pci->gpio_chip.of_node = pdev->dev.parent->of_node;
+
+	/* Add gpiochip */
 	ret = devm_gpiochip_add_data(&pdev->dev, &pci->gpio_chip, pci);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Couldn't add gpiochip\n");
@@ -485,7 +794,12 @@ static struct platform_driver rk805_pinctrl_driver = {
 		.name = "rk805-pinctrl",
 	},
 };
-module_platform_driver(rk805_pinctrl_driver);
+
+static int __init rk805_pinctrl_driver_register(void)
+{
+	return platform_driver_register(&rk805_pinctrl_driver);
+}
+fs_initcall_sync(rk805_pinctrl_driver_register);
 
 MODULE_DESCRIPTION("RK805 pin control and GPIO driver");
 MODULE_AUTHOR("Joseph Chen <chenjh@rock-chips.com>");
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
index 07b120417..1e78070ab 100644
--- a/drivers/pinctrl/pinctrl-rockchip.c
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -16,11 +16,13 @@
  */
 
 #include <linux/init.h>
+#include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
 #include <linux/bitops.h>
 #include <linux/gpio/driver.h>
 #include <linux/of_address.h>
+#include <linux/of_device.h>
 #include <linux/of_irq.h>
 #include <linux/pinctrl/machine.h>
 #include <linux/pinctrl/pinconf.h>
@@ -31,37 +33,19 @@
 #include <linux/clk.h>
 #include <linux/regmap.h>
 #include <linux/mfd/syscon.h>
+#include <linux/rockchip/cpu.h>
 #include <dt-bindings/pinctrl/rockchip.h>
 
 #include "core.h"
 #include "pinconf.h"
+#include <linux/pinctrl-rockchip.h>
 
-/* GPIO control registers */
-#define GPIO_SWPORT_DR		0x00
-#define GPIO_SWPORT_DDR		0x04
-#define GPIO_INTEN		0x30
-#define GPIO_INTMASK		0x34
-#define GPIO_INTTYPE_LEVEL	0x38
-#define GPIO_INT_POLARITY	0x3c
-#define GPIO_INT_STATUS		0x40
-#define GPIO_INT_RAWSTATUS	0x44
-#define GPIO_DEBOUNCE		0x48
-#define GPIO_PORTS_EOI		0x4c
-#define GPIO_EXT_PORT		0x50
-#define GPIO_LS_SYNC		0x60
-
-enum rockchip_pinctrl_type {
-	PX30,
-	RV1108,
-	RK2928,
-	RK3066B,
-	RK3128,
-	RK3188,
-	RK3288,
-	RK3308,
-	RK3368,
-	RK3399,
-};
+/**
+ * Generate a bitmask for setting a value (v) with a write mask bit in hiword
+ * register 31:16 area.
+ */
+#define WRITE_MASK_VAL(h, l, v) \
+	(GENMASK(((h) + 16), ((l) + 16)) | (((v) << (l)) & GENMASK((h), (l))))
 
 /*
  * Encode variants of iomux registers into a type variable
@@ -72,103 +56,8 @@ enum rockchip_pinctrl_type {
 #define IOMUX_UNROUTED		BIT(3)
 #define IOMUX_WIDTH_3BIT	BIT(4)
 #define IOMUX_WIDTH_2BIT	BIT(5)
-
-/**
- * struct rockchip_iomux
- * @type: iomux variant using IOMUX_* constants
- * @offset: if initialized to -1 it will be autocalculated, by specifying
- *	    an initial offset value the relevant source offset can be reset
- *	    to a new value for autocalculating the following iomux registers.
- */
-struct rockchip_iomux {
-	int				type;
-	int				offset;
-};
-
-/*
- * enum type index corresponding to rockchip_perpin_drv_list arrays index.
- */
-enum rockchip_pin_drv_type {
-	DRV_TYPE_IO_DEFAULT = 0,
-	DRV_TYPE_IO_1V8_OR_3V0,
-	DRV_TYPE_IO_1V8_ONLY,
-	DRV_TYPE_IO_1V8_3V0_AUTO,
-	DRV_TYPE_IO_3V3_ONLY,
-	DRV_TYPE_MAX
-};
-
-/*
- * enum type index corresponding to rockchip_pull_list arrays index.
- */
-enum rockchip_pin_pull_type {
-	PULL_TYPE_IO_DEFAULT = 0,
-	PULL_TYPE_IO_1V8_ONLY,
-	PULL_TYPE_MAX
-};
-
-/**
- * struct rockchip_drv
- * @drv_type: drive strength variant using rockchip_perpin_drv_type
- * @offset: if initialized to -1 it will be autocalculated, by specifying
- *	    an initial offset value the relevant source offset can be reset
- *	    to a new value for autocalculating the following drive strength
- *	    registers. if used chips own cal_drv func instead to calculate
- *	    registers offset, the variant could be ignored.
- */
-struct rockchip_drv {
-	enum rockchip_pin_drv_type	drv_type;
-	int				offset;
-};
-
-/**
- * struct rockchip_pin_bank
- * @reg_base: register base of the gpio bank
- * @regmap_pull: optional separate register for additional pull settings
- * @clk: clock of the gpio bank
- * @irq: interrupt of the gpio bank
- * @saved_masks: Saved content of GPIO_INTEN at suspend time.
- * @pin_base: first pin number
- * @nr_pins: number of pins in this bank
- * @name: name of the bank
- * @bank_num: number of the bank, to account for holes
- * @iomux: array describing the 4 iomux sources of the bank
- * @drv: array describing the 4 drive strength sources of the bank
- * @pull_type: array describing the 4 pull type sources of the bank
- * @valid: is all necessary information present
- * @of_node: dt node of this bank
- * @drvdata: common pinctrl basedata
- * @domain: irqdomain of the gpio bank
- * @gpio_chip: gpiolib chip
- * @grange: gpio range
- * @slock: spinlock for the gpio bank
- * @toggle_edge_mode: bit mask to toggle (falling/rising) edge mode
- * @recalced_mask: bit mask to indicate a need to recalulate the mask
- * @route_mask: bits describing the routing pins of per bank
- */
-struct rockchip_pin_bank {
-	void __iomem			*reg_base;
-	struct regmap			*regmap_pull;
-	struct clk			*clk;
-	int				irq;
-	u32				saved_masks;
-	u32				pin_base;
-	u8				nr_pins;
-	char				*name;
-	u8				bank_num;
-	struct rockchip_iomux		iomux[4];
-	struct rockchip_drv		drv[4];
-	enum rockchip_pin_pull_type	pull_type[4];
-	bool				valid;
-	struct device_node		*of_node;
-	struct rockchip_pinctrl		*drvdata;
-	struct irq_domain		*domain;
-	struct gpio_chip		gpio_chip;
-	struct pinctrl_gpio_range	grange;
-	raw_spinlock_t			slock;
-	u32				toggle_edge_mode;
-	u32				recalced_mask;
-	u32				route_mask;
-};
+#define IOMUX_WRITABLE_32BIT	BIT(6)
+#define IOMUX_L_SOURCE_PMU	BIT(7)
 
 #define PIN_BANK(id, pins, label)			\
 	{						\
@@ -196,6 +85,21 @@ struct rockchip_pin_bank {
 		},							\
 	}
 
+#define PIN_BANK_IOMUX_FLAGS_OFFSET(id, pins, label, iom0, iom1, iom2,	\
+				    iom3, offset0, offset1, offset2,	\
+				    offset3)				\
+	{								\
+		.bank_num	= id,					\
+		.nr_pins	= pins,					\
+		.name		= label,				\
+		.iomux		= {					\
+			{ .type = iom0, .offset = offset0 },		\
+			{ .type = iom1, .offset = offset1 },		\
+			{ .type = iom2, .offset = offset2 },		\
+			{ .type = iom3, .offset = offset3 },		\
+		},							\
+	}
+
 #define PIN_BANK_DRV_FLAGS(id, pins, label, type0, type1, type2, type3) \
 	{								\
 		.bank_num	= id,					\
@@ -215,6 +119,25 @@ struct rockchip_pin_bank {
 		},							\
 	}
 
+#define PIN_BANK_IOMUX_FLAGS_PULL_FLAGS(id, pins, label, iom0, iom1,	\
+					iom2, iom3, pull0, pull1,	\
+					pull2, pull3)			\
+	{								\
+		.bank_num	= id,					\
+		.nr_pins	= pins,					\
+		.name		= label,				\
+		.iomux		= {					\
+			{ .type = iom0, .offset = -1 },			\
+			{ .type = iom1, .offset = -1 },			\
+			{ .type = iom2, .offset = -1 },			\
+			{ .type = iom3, .offset = -1 },			\
+		},							\
+		.pull_type[0] = pull0,					\
+		.pull_type[1] = pull1,					\
+		.pull_type[2] = pull2,					\
+		.pull_type[3] = pull3,					\
+	}
+
 #define PIN_BANK_DRV_FLAGS_PULL_FLAGS(id, pins, label, drv0, drv1,	\
 				      drv2, drv3, pull0, pull1,		\
 				      pull2, pull3)			\
@@ -290,118 +213,27 @@ struct rockchip_pin_bank {
 		.pull_type[3] = pull3,					\
 	}
 
-/**
- * struct rockchip_mux_recalced_data: represent a pin iomux data.
- * @num: bank number.
- * @pin: pin number.
- * @bit: index at register.
- * @reg: register offset.
- * @mask: mask bit
- */
-struct rockchip_mux_recalced_data {
-	u8 num;
-	u8 pin;
-	u32 reg;
-	u8 bit;
-	u8 mask;
-};
-
-enum rockchip_mux_route_location {
-	ROCKCHIP_ROUTE_SAME = 0,
-	ROCKCHIP_ROUTE_PMU,
-	ROCKCHIP_ROUTE_GRF,
-};
-
-/**
- * struct rockchip_mux_recalced_data: represent a pin iomux data.
- * @bank_num: bank number.
- * @pin: index at register or used to calc index.
- * @func: the min pin.
- * @route_location: the mux route location (same, pmu, grf).
- * @route_offset: the max pin.
- * @route_val: the register offset.
- */
-struct rockchip_mux_route_data {
-	u8 bank_num;
-	u8 pin;
-	u8 func;
-	enum rockchip_mux_route_location route_location;
-	u32 route_offset;
-	u32 route_val;
-};
-
-struct rockchip_pin_ctrl {
-	struct rockchip_pin_bank	*pin_banks;
-	u32				nr_banks;
-	u32				nr_pins;
-	char				*label;
-	enum rockchip_pinctrl_type	type;
-	int				grf_mux_offset;
-	int				pmu_mux_offset;
-	int				grf_drv_offset;
-	int				pmu_drv_offset;
-	struct rockchip_mux_recalced_data *iomux_recalced;
-	u32				niomux_recalced;
-	struct rockchip_mux_route_data *iomux_routes;
-	u32				niomux_routes;
-
-	void	(*pull_calc_reg)(struct rockchip_pin_bank *bank,
-				    int pin_num, struct regmap **regmap,
-				    int *reg, u8 *bit);
-	void	(*drv_calc_reg)(struct rockchip_pin_bank *bank,
-				    int pin_num, struct regmap **regmap,
-				    int *reg, u8 *bit);
-	int	(*schmitt_calc_reg)(struct rockchip_pin_bank *bank,
-				    int pin_num, struct regmap **regmap,
-				    int *reg, u8 *bit);
-};
+#define PIN_BANK_MUX_ROUTE_FLAGS(ID, PIN, FUNC, REG, VAL, FLAG)		\
+	{								\
+		.bank_num	= ID,					\
+		.pin		= PIN,					\
+		.func		= FUNC,					\
+		.route_offset	= REG,					\
+		.route_val	= VAL,					\
+		.route_location	= FLAG,					\
+	}
 
-struct rockchip_pin_config {
-	unsigned int		func;
-	unsigned long		*configs;
-	unsigned int		nconfigs;
-};
+#define RK_MUXROUTE_SAME(ID, PIN, FUNC, REG, VAL)	\
+	PIN_BANK_MUX_ROUTE_FLAGS(ID, PIN, FUNC, REG, VAL, ROCKCHIP_ROUTE_SAME)
 
-/**
- * struct rockchip_pin_group: represent group of pins of a pinmux function.
- * @name: name of the pin group, used to lookup the group.
- * @pins: the pins included in this group.
- * @npins: number of pins included in this group.
- * @data: local pin configuration
- */
-struct rockchip_pin_group {
-	const char			*name;
-	unsigned int			npins;
-	unsigned int			*pins;
-	struct rockchip_pin_config	*data;
-};
+#define RK_MUXROUTE_GRF(ID, PIN, FUNC, REG, VAL)	\
+	PIN_BANK_MUX_ROUTE_FLAGS(ID, PIN, FUNC, REG, VAL, ROCKCHIP_ROUTE_GRF)
 
-/**
- * struct rockchip_pmx_func: represent a pin function.
- * @name: name of the pin function, used to lookup the function.
- * @groups: one or more names of pin groups that provide this function.
- * @ngroups: number of groups included in @groups.
- */
-struct rockchip_pmx_func {
-	const char		*name;
-	const char		**groups;
-	u8			ngroups;
-};
+#define RK_MUXROUTE_PMU(ID, PIN, FUNC, REG, VAL)	\
+	PIN_BANK_MUX_ROUTE_FLAGS(ID, PIN, FUNC, REG, VAL, ROCKCHIP_ROUTE_PMU)
 
-struct rockchip_pinctrl {
-	struct regmap			*regmap_base;
-	int				reg_size;
-	struct regmap			*regmap_pull;
-	struct regmap			*regmap_pmu;
-	struct device			*dev;
-	struct rockchip_pin_ctrl	*ctrl;
-	struct pinctrl_desc		pctl;
-	struct pinctrl_dev		*pctl_dev;
-	struct rockchip_pin_group	*groups;
-	unsigned int			ngroups;
-	struct rockchip_pmx_func	*functions;
-	unsigned int			nfunctions;
-};
+#define RK3588_PIN_BANK_FLAGS(ID, PIN, LABEL, M, P)			\
+	PIN_BANK_IOMUX_FLAGS_PULL_FLAGS(ID, PIN, LABEL, M, M, M, M, P, P, P, P)
 
 static struct regmap_config rockchip_regmap_config = {
 	.reg_bits = 32,
@@ -627,6 +459,37 @@ static struct rockchip_mux_recalced_data rv1108_mux_recalced_data[] = {
 	},
 };
 
+static struct rockchip_mux_recalced_data rv1126_mux_recalced_data[] = {
+	{
+		.num = 0,
+		.pin = 20,
+		.reg = 0x10000,
+		.bit = 0,
+		.mask = 0xf
+	},
+	{
+		.num = 0,
+		.pin = 21,
+		.reg = 0x10000,
+		.bit = 4,
+		.mask = 0xf
+	},
+	{
+		.num = 0,
+		.pin = 22,
+		.reg = 0x10000,
+		.bit = 8,
+		.mask = 0xf
+	},
+	{
+		.num = 0,
+		.pin = 23,
+		.reg = 0x10000,
+		.bit = 12,
+		.mask = 0xf
+	},
+};
+
 static  struct rockchip_mux_recalced_data rk3128_mux_recalced_data[] = {
 	{
 		.num = 2,
@@ -772,11 +635,47 @@ static struct rockchip_mux_recalced_data rk3308_mux_recalced_data[] = {
 
 static struct rockchip_mux_recalced_data rk3328_mux_recalced_data[] = {
 	{
+		.num = 2,
+		.pin = 8,
+		.reg = 0x24,
+		.bit = 0,
+		.mask = 0x3
+	}, {
+		.num = 2,
+		.pin = 9,
+		.reg = 0x24,
+		.bit = 2,
+		.mask = 0x3
+	}, {
+		.num = 2,
+		.pin = 10,
+		.reg = 0x24,
+		.bit = 4,
+		.mask = 0x3
+	}, {
+		.num = 2,
+		.pin = 11,
+		.reg = 0x24,
+		.bit = 6,
+		.mask = 0x3
+	}, {
 		.num = 2,
 		.pin = 12,
 		.reg = 0x24,
 		.bit = 8,
 		.mask = 0x3
+	}, {
+		.num = 2,
+		.pin = 13,
+		.reg = 0x24,
+		.bit = 10,
+		.mask = 0x3
+	}, {
+		.num = 2,
+		.pin = 14,
+		.reg = 0x24,
+		.bit = 12,
+		.mask = 0x3
 	}, {
 		.num = 2,
 		.pin = 15,
@@ -792,6 +691,103 @@ static struct rockchip_mux_recalced_data rk3328_mux_recalced_data[] = {
 	},
 };
 
+static struct rockchip_mux_route_data rv1126_mux_route_data[] = {
+	RK_MUXROUTE_GRF(3, RK_PD2, 1, 0x10260, WRITE_MASK_VAL(0, 0, 0)), /* I2S0_MCLK_M0 */
+	RK_MUXROUTE_GRF(3, RK_PB0, 3, 0x10260, WRITE_MASK_VAL(0, 0, 1)), /* I2S0_MCLK_M1 */
+
+	RK_MUXROUTE_GRF(0, RK_PD4, 4, 0x10260, WRITE_MASK_VAL(3, 2, 0)), /* I2S1_MCLK_M0 */
+	RK_MUXROUTE_GRF(1, RK_PD5, 2, 0x10260, WRITE_MASK_VAL(3, 2, 1)), /* I2S1_MCLK_M1 */
+	RK_MUXROUTE_GRF(2, RK_PC7, 6, 0x10260, WRITE_MASK_VAL(3, 2, 2)), /* I2S1_MCLK_M2 */
+
+	RK_MUXROUTE_GRF(1, RK_PD0, 1, 0x10260, WRITE_MASK_VAL(4, 4, 0)), /* I2S2_MCLK_M0 */
+	RK_MUXROUTE_GRF(2, RK_PB3, 2, 0x10260, WRITE_MASK_VAL(4, 4, 1)), /* I2S2_MCLK_M1 */
+
+	RK_MUXROUTE_GRF(3, RK_PD4, 2, 0x10260, WRITE_MASK_VAL(12, 12, 0)), /* PDM_CLK0_M0 */
+	RK_MUXROUTE_GRF(3, RK_PC0, 3, 0x10260, WRITE_MASK_VAL(12, 12, 1)), /* PDM_CLK0_M1 */
+
+	RK_MUXROUTE_GRF(3, RK_PC6, 1, 0x10264, WRITE_MASK_VAL(0, 0, 0)), /* CIF_CLKOUT_M0 */
+	RK_MUXROUTE_GRF(2, RK_PD1, 3, 0x10264, WRITE_MASK_VAL(0, 0, 1)), /* CIF_CLKOUT_M1 */
+
+	RK_MUXROUTE_GRF(3, RK_PA4, 5, 0x10264, WRITE_MASK_VAL(5, 4, 0)), /* I2C3_SCL_M0 */
+	RK_MUXROUTE_GRF(2, RK_PD4, 7, 0x10264, WRITE_MASK_VAL(5, 4, 1)), /* I2C3_SCL_M1 */
+	RK_MUXROUTE_GRF(1, RK_PD6, 3, 0x10264, WRITE_MASK_VAL(5, 4, 2)), /* I2C3_SCL_M2 */
+
+	RK_MUXROUTE_GRF(3, RK_PA0, 7, 0x10264, WRITE_MASK_VAL(6, 6, 0)), /* I2C4_SCL_M0 */
+	RK_MUXROUTE_GRF(4, RK_PA0, 4, 0x10264, WRITE_MASK_VAL(6, 6, 1)), /* I2C4_SCL_M1 */
+
+	RK_MUXROUTE_GRF(2, RK_PA5, 7, 0x10264, WRITE_MASK_VAL(9, 8, 0)), /* I2C5_SCL_M0 */
+	RK_MUXROUTE_GRF(3, RK_PB0, 5, 0x10264, WRITE_MASK_VAL(9, 8, 1)), /* I2C5_SCL_M1 */
+	RK_MUXROUTE_GRF(1, RK_PD0, 4, 0x10264, WRITE_MASK_VAL(9, 8, 2)), /* I2C5_SCL_M2 */
+
+	RK_MUXROUTE_GRF(3, RK_PC0, 5, 0x10264, WRITE_MASK_VAL(11, 10, 0)), /* SPI1_CLK_M0 */
+	RK_MUXROUTE_GRF(1, RK_PC6, 3, 0x10264, WRITE_MASK_VAL(11, 10, 1)), /* SPI1_CLK_M1 */
+	RK_MUXROUTE_GRF(2, RK_PD5, 6, 0x10264, WRITE_MASK_VAL(11, 10, 2)), /* SPI1_CLK_M2 */
+
+	RK_MUXROUTE_GRF(3, RK_PC0, 2, 0x10264, WRITE_MASK_VAL(12, 12, 0)), /* RGMII_CLK_M0 */
+	RK_MUXROUTE_GRF(2, RK_PB7, 2, 0x10264, WRITE_MASK_VAL(12, 12, 1)), /* RGMII_CLK_M1 */
+
+	RK_MUXROUTE_GRF(3, RK_PA1, 3, 0x10264, WRITE_MASK_VAL(13, 13, 0)), /* CAN_TXD_M0 */
+	RK_MUXROUTE_GRF(3, RK_PA7, 5, 0x10264, WRITE_MASK_VAL(13, 13, 1)), /* CAN_TXD_M1 */
+
+	RK_MUXROUTE_GRF(3, RK_PA4, 6, 0x10268, WRITE_MASK_VAL(0, 0, 0)), /* PWM8_M0 */
+	RK_MUXROUTE_GRF(2, RK_PD7, 5, 0x10268, WRITE_MASK_VAL(0, 0, 1)), /* PWM8_M1 */
+
+	RK_MUXROUTE_GRF(3, RK_PA5, 6, 0x10268, WRITE_MASK_VAL(2, 2, 0)), /* PWM9_M0 */
+	RK_MUXROUTE_GRF(2, RK_PD6, 5, 0x10268, WRITE_MASK_VAL(2, 2, 1)), /* PWM9_M1 */
+
+	RK_MUXROUTE_GRF(3, RK_PA6, 6, 0x10268, WRITE_MASK_VAL(4, 4, 0)), /* PWM10_M0 */
+	RK_MUXROUTE_GRF(2, RK_PD5, 5, 0x10268, WRITE_MASK_VAL(4, 4, 1)), /* PWM10_M1 */
+
+	RK_MUXROUTE_GRF(3, RK_PA7, 6, 0x10268, WRITE_MASK_VAL(6, 6, 0)), /* PWM11_IR_M0 */
+	RK_MUXROUTE_GRF(3, RK_PA1, 5, 0x10268, WRITE_MASK_VAL(6, 6, 1)), /* PWM11_IR_M1 */
+
+	RK_MUXROUTE_GRF(1, RK_PA5, 3, 0x10268, WRITE_MASK_VAL(8, 8, 0)), /* UART2_TX_M0 */
+	RK_MUXROUTE_GRF(3, RK_PA2, 1, 0x10268, WRITE_MASK_VAL(8, 8, 1)), /* UART2_TX_M1 */
+
+	RK_MUXROUTE_GRF(3, RK_PC6, 3, 0x10268, WRITE_MASK_VAL(11, 10, 0)), /* UART3_TX_M0 */
+	RK_MUXROUTE_GRF(1, RK_PA7, 2, 0x10268, WRITE_MASK_VAL(11, 10, 1)), /* UART3_TX_M1 */
+	RK_MUXROUTE_GRF(3, RK_PA0, 4, 0x10268, WRITE_MASK_VAL(11, 10, 2)), /* UART3_TX_M2 */
+
+	RK_MUXROUTE_GRF(3, RK_PA4, 4, 0x10268, WRITE_MASK_VAL(13, 12, 0)), /* UART4_TX_M0 */
+	RK_MUXROUTE_GRF(2, RK_PA6, 4, 0x10268, WRITE_MASK_VAL(13, 12, 1)), /* UART4_TX_M1 */
+	RK_MUXROUTE_GRF(1, RK_PD5, 3, 0x10268, WRITE_MASK_VAL(13, 12, 2)), /* UART4_TX_M2 */
+
+	RK_MUXROUTE_GRF(3, RK_PA6, 4, 0x10268, WRITE_MASK_VAL(15, 14, 0)), /* UART5_TX_M0 */
+	RK_MUXROUTE_GRF(2, RK_PB0, 4, 0x10268, WRITE_MASK_VAL(15, 14, 1)), /* UART5_TX_M1 */
+	RK_MUXROUTE_GRF(2, RK_PA0, 3, 0x10268, WRITE_MASK_VAL(15, 14, 2)), /* UART5_TX_M2 */
+
+	RK_MUXROUTE_PMU(0, RK_PB6, 3, 0x0114, WRITE_MASK_VAL(0, 0, 0)), /* PWM0_M0 */
+	RK_MUXROUTE_PMU(2, RK_PB3, 5, 0x0114, WRITE_MASK_VAL(0, 0, 1)), /* PWM0_M1 */
+
+	RK_MUXROUTE_PMU(0, RK_PB7, 3, 0x0114, WRITE_MASK_VAL(2, 2, 0)), /* PWM1_M0 */
+	RK_MUXROUTE_PMU(2, RK_PB2, 5, 0x0114, WRITE_MASK_VAL(2, 2, 1)), /* PWM1_M1 */
+
+	RK_MUXROUTE_PMU(0, RK_PC0, 3, 0x0114, WRITE_MASK_VAL(4, 4, 0)), /* PWM2_M0 */
+	RK_MUXROUTE_PMU(2, RK_PB1, 5, 0x0114, WRITE_MASK_VAL(4, 4, 1)), /* PWM2_M1 */
+
+	RK_MUXROUTE_PMU(0, RK_PC1, 3, 0x0114, WRITE_MASK_VAL(6, 6, 0)), /* PWM3_IR_M0 */
+	RK_MUXROUTE_PMU(2, RK_PB0, 5, 0x0114, WRITE_MASK_VAL(6, 6, 1)), /* PWM3_IR_M1 */
+
+	RK_MUXROUTE_PMU(0, RK_PC2, 3, 0x0114, WRITE_MASK_VAL(8, 8, 0)), /* PWM4_M0 */
+	RK_MUXROUTE_PMU(2, RK_PA7, 5, 0x0114, WRITE_MASK_VAL(8, 8, 1)), /* PWM4_M1 */
+
+	RK_MUXROUTE_PMU(0, RK_PC3, 3, 0x0114, WRITE_MASK_VAL(10, 10, 0)), /* PWM5_M0 */
+	RK_MUXROUTE_PMU(2, RK_PA6, 5, 0x0114, WRITE_MASK_VAL(10, 10, 1)), /* PWM5_M1 */
+
+	RK_MUXROUTE_PMU(0, RK_PB2, 3, 0x0114, WRITE_MASK_VAL(12, 12, 0)), /* PWM6_M0 */
+	RK_MUXROUTE_PMU(2, RK_PD4, 5, 0x0114, WRITE_MASK_VAL(12, 12, 1)), /* PWM6_M1 */
+
+	RK_MUXROUTE_PMU(0, RK_PB1, 3, 0x0114, WRITE_MASK_VAL(14, 14, 0)), /* PWM7_IR_M0 */
+	RK_MUXROUTE_PMU(3, RK_PA0, 5, 0x0114, WRITE_MASK_VAL(14, 14, 1)), /* PWM7_IR_M1 */
+
+	RK_MUXROUTE_PMU(0, RK_PB0, 1, 0x0118, WRITE_MASK_VAL(1, 0, 0)), /* SPI0_CLK_M0 */
+	RK_MUXROUTE_PMU(2, RK_PA1, 1, 0x0118, WRITE_MASK_VAL(1, 0, 1)), /* SPI0_CLK_M1 */
+	RK_MUXROUTE_PMU(2, RK_PB2, 6, 0x0118, WRITE_MASK_VAL(1, 0, 2)), /* SPI0_CLK_M2 */
+
+	RK_MUXROUTE_PMU(0, RK_PB6, 2, 0x0118, WRITE_MASK_VAL(2, 2, 0)), /* UART1_TX_M0 */
+	RK_MUXROUTE_PMU(1, RK_PD0, 5, 0x0118, WRITE_MASK_VAL(2, 2, 1)), /* UART1_TX_M1 */
+};
+
 static void rockchip_get_recalced_mux(struct rockchip_pin_bank *bank, int pin,
 				      int *reg, u8 *bit, int *mask)
 {
@@ -815,598 +811,219 @@ static void rockchip_get_recalced_mux(struct rockchip_pin_bank *bank, int pin,
 	*bit = data->bit;
 }
 
+static struct rockchip_mux_route_data rk1808_mux_route_data[] = {
+	RK_MUXROUTE_SAME(3, RK_PB4, 2, 0x190, BIT(16 + 3)), /* i2c2m0_sda */
+	RK_MUXROUTE_SAME(1, RK_PB5, 2, 0x190, BIT(16 + 3) | BIT(3)), /* i2c2m1_sda */
+	RK_MUXROUTE_SAME(1, RK_PA6, 2, 0x190, BIT(16 + 4)), /* spi2m0_miso */
+	RK_MUXROUTE_SAME(2, RK_PA4, 3, 0x190, BIT(16 + 4) | BIT(4)), /* spi2m1_miso */
+	RK_MUXROUTE_SAME(4, RK_PB7, 2, 0x190, BIT(16 + 5)), /* spi1m0_miso */
+	RK_MUXROUTE_SAME(3, RK_PD2, 3, 0x190, BIT(16 + 5) | BIT(5)), /* spi1m1_miso */
+	RK_MUXROUTE_SAME(4, RK_PB0, 2, 0x190, BIT(16 + 13)), /* uart1_rxm0 */
+	RK_MUXROUTE_SAME(1, RK_PB4, 3, 0x190, BIT(16 + 13) | BIT(13)), /* uart1_rxm1 */
+	RK_MUXROUTE_SAME(4, RK_PA3, 2, 0x190, BIT(16 + 14) | BIT(16 + 15)), /* uart2_rxm0 */
+	RK_MUXROUTE_SAME(2, RK_PD1, 2, 0x190, BIT(16 + 14) | BIT(16 + 15) | BIT(14)), /* uart2_rxm1 */
+	RK_MUXROUTE_SAME(3, RK_PA4, 2, 0x190, BIT(16 + 14) | BIT(16 + 15) | BIT(15)), /* uart2_rxm2 */
+};
+
 static struct rockchip_mux_route_data px30_mux_route_data[] = {
-	{
-		/* cif-d2m0 */
-		.bank_num = 2,
-		.pin = 0,
-		.func = 1,
-		.route_offset = 0x184,
-		.route_val = BIT(16 + 7),
-	}, {
-		/* cif-d2m1 */
-		.bank_num = 3,
-		.pin = 3,
-		.func = 3,
-		.route_offset = 0x184,
-		.route_val = BIT(16 + 7) | BIT(7),
-	}, {
-		/* pdm-m0 */
-		.bank_num = 3,
-		.pin = 22,
-		.func = 2,
-		.route_offset = 0x184,
-		.route_val = BIT(16 + 8),
-	}, {
-		/* pdm-m1 */
-		.bank_num = 2,
-		.pin = 22,
-		.func = 1,
-		.route_offset = 0x184,
-		.route_val = BIT(16 + 8) | BIT(8),
-	}, {
-		/* uart2-rxm0 */
-		.bank_num = 1,
-		.pin = 27,
-		.func = 2,
-		.route_offset = 0x184,
-		.route_val = BIT(16 + 10),
-	}, {
-		/* uart2-rxm1 */
-		.bank_num = 2,
-		.pin = 14,
-		.func = 2,
-		.route_offset = 0x184,
-		.route_val = BIT(16 + 10) | BIT(10),
-	}, {
-		/* uart3-rxm0 */
-		.bank_num = 0,
-		.pin = 17,
-		.func = 2,
-		.route_offset = 0x184,
-		.route_val = BIT(16 + 9),
-	}, {
-		/* uart3-rxm1 */
-		.bank_num = 1,
-		.pin = 15,
-		.func = 2,
-		.route_offset = 0x184,
-		.route_val = BIT(16 + 9) | BIT(9),
-	},
+	RK_MUXROUTE_SAME(2, RK_PA0, 1, 0x184, BIT(16 + 7)), /* cif-d2m0 */
+	RK_MUXROUTE_SAME(3, RK_PA3, 3, 0x184, BIT(16 + 7) | BIT(7)), /* cif-d2m1 */
+	RK_MUXROUTE_SAME(3, RK_PC6, 2, 0x184, BIT(16 + 8)), /* pdm-m0 */
+	RK_MUXROUTE_SAME(2, RK_PC6, 1, 0x184, BIT(16 + 8) | BIT(8)), /* pdm-m1 */
+	RK_MUXROUTE_SAME(1, RK_PD3, 2, 0x184, BIT(16 + 10)), /* uart2-rxm0 */
+	RK_MUXROUTE_SAME(2, RK_PB6, 2, 0x184, BIT(16 + 10) | BIT(10)), /* uart2-rxm1 */
+	RK_MUXROUTE_SAME(0, RK_PC1, 2, 0x184, BIT(16 + 9)), /* uart3-rxm0 */
+	RK_MUXROUTE_SAME(1, RK_PB7, 2, 0x184, BIT(16 + 9) | BIT(9)), /* uart3-rxm1 */
 };
 
 static struct rockchip_mux_route_data rk3128_mux_route_data[] = {
-	{
-		/* spi-0 */
-		.bank_num = 1,
-		.pin = 10,
-		.func = 1,
-		.route_offset = 0x144,
-		.route_val = BIT(16 + 3) | BIT(16 + 4),
-	}, {
-		/* spi-1 */
-		.bank_num = 1,
-		.pin = 27,
-		.func = 3,
-		.route_offset = 0x144,
-		.route_val = BIT(16 + 3) | BIT(16 + 4) | BIT(3),
-	}, {
-		/* spi-2 */
-		.bank_num = 0,
-		.pin = 13,
-		.func = 2,
-		.route_offset = 0x144,
-		.route_val = BIT(16 + 3) | BIT(16 + 4) | BIT(4),
-	}, {
-		/* i2s-0 */
-		.bank_num = 1,
-		.pin = 5,
-		.func = 1,
-		.route_offset = 0x144,
-		.route_val = BIT(16 + 5),
-	}, {
-		/* i2s-1 */
-		.bank_num = 0,
-		.pin = 14,
-		.func = 1,
-		.route_offset = 0x144,
-		.route_val = BIT(16 + 5) | BIT(5),
-	}, {
-		/* emmc-0 */
-		.bank_num = 1,
-		.pin = 22,
-		.func = 2,
-		.route_offset = 0x144,
-		.route_val = BIT(16 + 6),
-	}, {
-		/* emmc-1 */
-		.bank_num = 2,
-		.pin = 4,
-		.func = 2,
-		.route_offset = 0x144,
-		.route_val = BIT(16 + 6) | BIT(6),
-	},
+	RK_MUXROUTE_SAME(1, RK_PB2, 1, 0x144, BIT(16 + 3) | BIT(16 + 4)), /* spi-0 */
+	RK_MUXROUTE_SAME(1, RK_PD3, 3, 0x144, BIT(16 + 3) | BIT(16 + 4) | BIT(3)), /* spi-1 */
+	RK_MUXROUTE_SAME(0, RK_PB5, 2, 0x144, BIT(16 + 3) | BIT(16 + 4) | BIT(4)), /* spi-2 */
+	RK_MUXROUTE_SAME(1, RK_PA5, 1, 0x144, BIT(16 + 5)), /* i2s-0 */
+	RK_MUXROUTE_SAME(0, RK_PB6, 1, 0x144, BIT(16 + 5) | BIT(5)), /* i2s-1 */
+	RK_MUXROUTE_SAME(1, RK_PC6, 2, 0x144, BIT(16 + 6)), /* emmc-0 */
+	RK_MUXROUTE_SAME(2, RK_PA4, 2, 0x144, BIT(16 + 6) | BIT(6)), /* emmc-1 */
 };
 
 static struct rockchip_mux_route_data rk3188_mux_route_data[] = {
-	{
-		/* non-iomuxed emmc/flash pins on flash-dqs */
-		.bank_num = 0,
-		.pin = 24,
-		.func = 1,
-		.route_location = ROCKCHIP_ROUTE_GRF,
-		.route_offset = 0xa0,
-		.route_val = BIT(16 + 11),
-	}, {
-		/* non-iomuxed emmc/flash pins on emmc-clk */
-		.bank_num = 0,
-		.pin = 24,
-		.func = 2,
-		.route_location = ROCKCHIP_ROUTE_GRF,
-		.route_offset = 0xa0,
-		.route_val = BIT(16 + 11) | BIT(11),
-	},
+	RK_MUXROUTE_SAME(0, RK_PD0, 1, 0xa0, BIT(16 + 11)), /* non-iomuxed emmc/flash pins on flash-dqs */
+	RK_MUXROUTE_SAME(0, RK_PD0, 2, 0xa0, BIT(16 + 11) | BIT(11)), /* non-iomuxed emmc/flash pins on emmc-clk */
 };
 
 static struct rockchip_mux_route_data rk3228_mux_route_data[] = {
-	{
-		/* pwm0-0 */
-		.bank_num = 0,
-		.pin = 26,
-		.func = 1,
-		.route_offset = 0x50,
-		.route_val = BIT(16),
-	}, {
-		/* pwm0-1 */
-		.bank_num = 3,
-		.pin = 21,
-		.func = 1,
-		.route_offset = 0x50,
-		.route_val = BIT(16) | BIT(0),
-	}, {
-		/* pwm1-0 */
-		.bank_num = 0,
-		.pin = 27,
-		.func = 1,
-		.route_offset = 0x50,
-		.route_val = BIT(16 + 1),
-	}, {
-		/* pwm1-1 */
-		.bank_num = 0,
-		.pin = 30,
-		.func = 2,
-		.route_offset = 0x50,
-		.route_val = BIT(16 + 1) | BIT(1),
-	}, {
-		/* pwm2-0 */
-		.bank_num = 0,
-		.pin = 28,
-		.func = 1,
-		.route_offset = 0x50,
-		.route_val = BIT(16 + 2),
-	}, {
-		/* pwm2-1 */
-		.bank_num = 1,
-		.pin = 12,
-		.func = 2,
-		.route_offset = 0x50,
-		.route_val = BIT(16 + 2) | BIT(2),
-	}, {
-		/* pwm3-0 */
-		.bank_num = 3,
-		.pin = 26,
-		.func = 1,
-		.route_offset = 0x50,
-		.route_val = BIT(16 + 3),
-	}, {
-		/* pwm3-1 */
-		.bank_num = 1,
-		.pin = 11,
-		.func = 2,
-		.route_offset = 0x50,
-		.route_val = BIT(16 + 3) | BIT(3),
-	}, {
-		/* sdio-0_d0 */
-		.bank_num = 1,
-		.pin = 1,
-		.func = 1,
-		.route_offset = 0x50,
-		.route_val = BIT(16 + 4),
-	}, {
-		/* sdio-1_d0 */
-		.bank_num = 3,
-		.pin = 2,
-		.func = 1,
-		.route_offset = 0x50,
-		.route_val = BIT(16 + 4) | BIT(4),
-	}, {
-		/* spi-0_rx */
-		.bank_num = 0,
-		.pin = 13,
-		.func = 2,
-		.route_offset = 0x50,
-		.route_val = BIT(16 + 5),
-	}, {
-		/* spi-1_rx */
-		.bank_num = 2,
-		.pin = 0,
-		.func = 2,
-		.route_offset = 0x50,
-		.route_val = BIT(16 + 5) | BIT(5),
-	}, {
-		/* emmc-0_cmd */
-		.bank_num = 1,
-		.pin = 22,
-		.func = 2,
-		.route_offset = 0x50,
-		.route_val = BIT(16 + 7),
-	}, {
-		/* emmc-1_cmd */
-		.bank_num = 2,
-		.pin = 4,
-		.func = 2,
-		.route_offset = 0x50,
-		.route_val = BIT(16 + 7) | BIT(7),
-	}, {
-		/* uart2-0_rx */
-		.bank_num = 1,
-		.pin = 19,
-		.func = 2,
-		.route_offset = 0x50,
-		.route_val = BIT(16 + 8),
-	}, {
-		/* uart2-1_rx */
-		.bank_num = 1,
-		.pin = 10,
-		.func = 2,
-		.route_offset = 0x50,
-		.route_val = BIT(16 + 8) | BIT(8),
-	}, {
-		/* uart1-0_rx */
-		.bank_num = 1,
-		.pin = 10,
-		.func = 1,
-		.route_offset = 0x50,
-		.route_val = BIT(16 + 11),
-	}, {
-		/* uart1-1_rx */
-		.bank_num = 3,
-		.pin = 13,
-		.func = 1,
-		.route_offset = 0x50,
-		.route_val = BIT(16 + 11) | BIT(11),
-	},
+	RK_MUXROUTE_SAME(0, RK_PD2, 1, 0x50, BIT(16)), /* pwm0-0 */
+	RK_MUXROUTE_SAME(3, RK_PC5, 1, 0x50, BIT(16) | BIT(0)), /* pwm0-1 */
+	RK_MUXROUTE_SAME(0, RK_PD3, 1, 0x50, BIT(16 + 1)), /* pwm1-0 */
+	RK_MUXROUTE_SAME(0, RK_PD6, 2, 0x50, BIT(16 + 1) | BIT(1)), /* pwm1-1 */
+	RK_MUXROUTE_SAME(0, RK_PD4, 1, 0x50, BIT(16 + 2)), /* pwm2-0 */
+	RK_MUXROUTE_SAME(1, RK_PB4, 2, 0x50, BIT(16 + 2) | BIT(2)), /* pwm2-1 */
+	RK_MUXROUTE_SAME(3, RK_PD2, 1, 0x50, BIT(16 + 3)), /* pwm3-0 */
+	RK_MUXROUTE_SAME(1, RK_PB3, 2, 0x50, BIT(16 + 3) | BIT(3)), /* pwm3-1 */
+	RK_MUXROUTE_SAME(1, RK_PA1, 1, 0x50, BIT(16 + 4)), /* sdio-0_d0 */
+	RK_MUXROUTE_SAME(3, RK_PA2, 1, 0x50, BIT(16 + 4) | BIT(4)), /* sdio-1_d0 */
+	RK_MUXROUTE_SAME(0, RK_PB5, 2, 0x50, BIT(16 + 5)), /* spi-0_rx */
+	RK_MUXROUTE_SAME(2, RK_PA0, 2, 0x50, BIT(16 + 5) | BIT(5)), /* spi-1_rx */
+	RK_MUXROUTE_SAME(1, RK_PC6, 2, 0x50, BIT(16 + 7)), /* emmc-0_cmd */
+	RK_MUXROUTE_SAME(2, RK_PA4, 2, 0x50, BIT(16 + 7) | BIT(7)), /* emmc-1_cmd */
+	RK_MUXROUTE_SAME(1, RK_PC3, 2, 0x50, BIT(16 + 8)), /* uart2-0_rx */
+	RK_MUXROUTE_SAME(1, RK_PB2, 2, 0x50, BIT(16 + 8) | BIT(8)), /* uart2-1_rx */
+	RK_MUXROUTE_SAME(1, RK_PB2, 1, 0x50, BIT(16 + 11)), /* uart1-0_rx */
+	RK_MUXROUTE_SAME(3, RK_PB5, 1, 0x50, BIT(16 + 11) | BIT(11)), /* uart1-1_rx */
 };
 
 static struct rockchip_mux_route_data rk3288_mux_route_data[] = {
-	{
-		/* edphdmi_cecinoutt1 */
-		.bank_num = 7,
-		.pin = 16,
-		.func = 2,
-		.route_offset = 0x264,
-		.route_val = BIT(16 + 12) | BIT(12),
-	}, {
-		/* edphdmi_cecinout */
-		.bank_num = 7,
-		.pin = 23,
-		.func = 4,
-		.route_offset = 0x264,
-		.route_val = BIT(16 + 12),
-	},
+	RK_MUXROUTE_SAME(7, RK_PC0, 2, 0x264, BIT(16 + 12) | BIT(12)), /* edphdmi_cecinoutt1 */
+	RK_MUXROUTE_SAME(7, RK_PC7, 4, 0x264, BIT(16 + 12)), /* edphdmi_cecinout */
 };
 
 static struct rockchip_mux_route_data rk3308_mux_route_data[] = {
-	{
-		/* rtc_clk */
-		.bank_num = 0,
-		.pin = 19,
-		.func = 1,
-		.route_offset = 0x314,
-		.route_val = BIT(16 + 0) | BIT(0),
-	}, {
-		/* uart2_rxm0 */
-		.bank_num = 1,
-		.pin = 22,
-		.func = 2,
-		.route_offset = 0x314,
-		.route_val = BIT(16 + 2) | BIT(16 + 3),
-	}, {
-		/* uart2_rxm1 */
-		.bank_num = 4,
-		.pin = 26,
-		.func = 2,
-		.route_offset = 0x314,
-		.route_val = BIT(16 + 2) | BIT(16 + 3) | BIT(2),
-	}, {
-		/* i2c3_sdam0 */
-		.bank_num = 0,
-		.pin = 15,
-		.func = 2,
-		.route_offset = 0x608,
-		.route_val = BIT(16 + 8) | BIT(16 + 9),
-	}, {
-		/* i2c3_sdam1 */
-		.bank_num = 3,
-		.pin = 12,
-		.func = 2,
-		.route_offset = 0x608,
-		.route_val = BIT(16 + 8) | BIT(16 + 9) | BIT(8),
-	}, {
-		/* i2c3_sdam2 */
-		.bank_num = 2,
-		.pin = 0,
-		.func = 3,
-		.route_offset = 0x608,
-		.route_val = BIT(16 + 8) | BIT(16 + 9) | BIT(9),
-	}, {
-		/* i2s-8ch-1-sclktxm0 */
-		.bank_num = 1,
-		.pin = 3,
-		.func = 2,
-		.route_offset = 0x308,
-		.route_val = BIT(16 + 3),
-	}, {
-		/* i2s-8ch-1-sclkrxm0 */
-		.bank_num = 1,
-		.pin = 4,
-		.func = 2,
-		.route_offset = 0x308,
-		.route_val = BIT(16 + 3),
-	}, {
-		/* i2s-8ch-1-sclktxm1 */
-		.bank_num = 1,
-		.pin = 13,
-		.func = 2,
-		.route_offset = 0x308,
-		.route_val = BIT(16 + 3) | BIT(3),
-	}, {
-		/* i2s-8ch-1-sclkrxm1 */
-		.bank_num = 1,
-		.pin = 14,
-		.func = 2,
-		.route_offset = 0x308,
-		.route_val = BIT(16 + 3) | BIT(3),
-	}, {
-		/* pdm-clkm0 */
-		.bank_num = 1,
-		.pin = 4,
-		.func = 3,
-		.route_offset = 0x308,
-		.route_val =  BIT(16 + 12) | BIT(16 + 13),
-	}, {
-		/* pdm-clkm1 */
-		.bank_num = 1,
-		.pin = 14,
-		.func = 4,
-		.route_offset = 0x308,
-		.route_val = BIT(16 + 12) | BIT(16 + 13) | BIT(12),
-	}, {
-		/* pdm-clkm2 */
-		.bank_num = 2,
-		.pin = 6,
-		.func = 2,
-		.route_offset = 0x308,
-		.route_val = BIT(16 + 12) | BIT(16 + 13) | BIT(13),
-	}, {
-		/* pdm-clkm-m2 */
-		.bank_num = 2,
-		.pin = 4,
-		.func = 3,
-		.route_offset = 0x600,
-		.route_val = BIT(16 + 2) | BIT(2),
-	}, {
-		/* spi1_miso */
-		.bank_num = 3,
-		.pin = 10,
-		.func = 3,
-		.route_offset = 0x314,
-		.route_val = BIT(16 + 9),
-	}, {
-		/* spi1_miso_m1 */
-		.bank_num = 2,
-		.pin = 4,
-		.func = 2,
-		.route_offset = 0x314,
-		.route_val = BIT(16 + 9) | BIT(9),
-	}, {
-		/* owire_m0 */
-		.bank_num = 0,
-		.pin = 11,
-		.func = 3,
-		.route_offset = 0x314,
-		.route_val = BIT(16 + 10) | BIT(16 + 11),
-	}, {
-		/* owire_m1 */
-		.bank_num = 1,
-		.pin = 22,
-		.func = 7,
-		.route_offset = 0x314,
-		.route_val = BIT(16 + 10) | BIT(16 + 11) | BIT(10),
-	}, {
-		/* owire_m2 */
-		.bank_num = 2,
-		.pin = 2,
-		.func = 5,
-		.route_offset = 0x314,
-		.route_val = BIT(16 + 10) | BIT(16 + 11) | BIT(11),
-	}, {
-		/* can_rxd_m0 */
-		.bank_num = 0,
-		.pin = 11,
-		.func = 2,
-		.route_offset = 0x314,
-		.route_val = BIT(16 + 12) | BIT(16 + 13),
-	}, {
-		/* can_rxd_m1 */
-		.bank_num = 1,
-		.pin = 22,
-		.func = 5,
-		.route_offset = 0x314,
-		.route_val = BIT(16 + 12) | BIT(16 + 13) | BIT(12),
-	}, {
-		/* can_rxd_m2 */
-		.bank_num = 2,
-		.pin = 2,
-		.func = 4,
-		.route_offset = 0x314,
-		.route_val = BIT(16 + 12) | BIT(16 + 13) | BIT(13),
-	}, {
-		/* mac_rxd0_m0 */
-		.bank_num = 1,
-		.pin = 20,
-		.func = 3,
-		.route_offset = 0x314,
-		.route_val = BIT(16 + 14),
-	}, {
-		/* mac_rxd0_m1 */
-		.bank_num = 4,
-		.pin = 2,
-		.func = 2,
-		.route_offset = 0x314,
-		.route_val = BIT(16 + 14) | BIT(14),
-	}, {
-		/* uart3_rx */
-		.bank_num = 3,
-		.pin = 12,
-		.func = 4,
-		.route_offset = 0x314,
-		.route_val = BIT(16 + 15),
-	}, {
-		/* uart3_rx_m1 */
-		.bank_num = 0,
-		.pin = 17,
-		.func = 3,
-		.route_offset = 0x314,
-		.route_val = BIT(16 + 15) | BIT(15),
-	},
+	RK_MUXROUTE_SAME(0, RK_PC3, 1, 0x314, BIT(16 + 0) | BIT(0)), /* rtc_clk */
+	RK_MUXROUTE_SAME(1, RK_PC6, 2, 0x314, BIT(16 + 2) | BIT(16 + 3)), /* uart2_rxm0 */
+	RK_MUXROUTE_SAME(4, RK_PD2, 2, 0x314, BIT(16 + 2) | BIT(16 + 3) | BIT(2)), /* uart2_rxm1 */
+	RK_MUXROUTE_SAME(0, RK_PB7, 2, 0x608, BIT(16 + 8) | BIT(16 + 9)), /* i2c3_sdam0 */
+	RK_MUXROUTE_SAME(3, RK_PB4, 2, 0x608, BIT(16 + 8) | BIT(16 + 9) | BIT(8)), /* i2c3_sdam1 */
+	RK_MUXROUTE_SAME(2, RK_PA0, 3, 0x608, BIT(16 + 8) | BIT(16 + 9) | BIT(9)), /* i2c3_sdam2 */
+	RK_MUXROUTE_SAME(1, RK_PA3, 2, 0x308, BIT(16 + 3)), /* i2s-8ch-1-sclktxm0 */
+	RK_MUXROUTE_SAME(1, RK_PA4, 2, 0x308, BIT(16 + 3)), /* i2s-8ch-1-sclkrxm0 */
+	RK_MUXROUTE_SAME(1, RK_PB5, 2, 0x308, BIT(16 + 3) | BIT(3)), /* i2s-8ch-1-sclktxm1 */
+	RK_MUXROUTE_SAME(1, RK_PB6, 2, 0x308, BIT(16 + 3) | BIT(3)), /* i2s-8ch-1-sclkrxm1 */
+	RK_MUXROUTE_SAME(1, RK_PA4, 3, 0x308, BIT(16 + 12) | BIT(16 + 13)), /* pdm-clkm0 */
+	RK_MUXROUTE_SAME(1, RK_PB6, 4, 0x308, BIT(16 + 12) | BIT(16 + 13) | BIT(12)), /* pdm-clkm1 */
+	RK_MUXROUTE_SAME(2, RK_PA6, 2, 0x308, BIT(16 + 12) | BIT(16 + 13) | BIT(13)), /* pdm-clkm2 */
+	RK_MUXROUTE_SAME(2, RK_PA4, 3, 0x600, BIT(16 + 2) | BIT(2)), /* pdm-clkm-m2 */
+	RK_MUXROUTE_SAME(3, RK_PB2, 3, 0x314, BIT(16 + 9)), /* spi1_miso */
+	RK_MUXROUTE_SAME(2, RK_PA4, 2, 0x314, BIT(16 + 9) | BIT(9)), /* spi1_miso_m1 */
+	RK_MUXROUTE_SAME(0, RK_PB3, 3, 0x314, BIT(16 + 10) | BIT(16 + 11)), /* owire_m0 */
+	RK_MUXROUTE_SAME(1, RK_PC6, 7, 0x314, BIT(16 + 10) | BIT(16 + 11) | BIT(10)), /* owire_m1 */
+	RK_MUXROUTE_SAME(2, RK_PA2, 5, 0x314, BIT(16 + 10) | BIT(16 + 11) | BIT(11)), /* owire_m2 */
+	RK_MUXROUTE_SAME(0, RK_PB3, 2, 0x314, BIT(16 + 12) | BIT(16 + 13)), /* can_rxd_m0 */
+	RK_MUXROUTE_SAME(1, RK_PC6, 5, 0x314, BIT(16 + 12) | BIT(16 + 13) | BIT(12)), /* can_rxd_m1 */
+	RK_MUXROUTE_SAME(2, RK_PA2, 4, 0x314, BIT(16 + 12) | BIT(16 + 13) | BIT(13)), /* can_rxd_m2 */
+	RK_MUXROUTE_SAME(1, RK_PC4, 3, 0x314, BIT(16 + 14)), /* mac_rxd0_m0 */
+	RK_MUXROUTE_SAME(4, RK_PA2, 2, 0x314, BIT(16 + 14) | BIT(14)), /* mac_rxd0_m1 */
+	RK_MUXROUTE_SAME(3, RK_PB4, 4, 0x314, BIT(16 + 15)), /* uart3_rx */
+	RK_MUXROUTE_SAME(0, RK_PC1, 3, 0x314, BIT(16 + 15) | BIT(15)), /* uart3_rx_m1 */
 };
 
 static struct rockchip_mux_route_data rk3328_mux_route_data[] = {
-	{
-		/* uart2dbg_rxm0 */
-		.bank_num = 1,
-		.pin = 1,
-		.func = 2,
-		.route_offset = 0x50,
-		.route_val = BIT(16) | BIT(16 + 1),
-	}, {
-		/* uart2dbg_rxm1 */
-		.bank_num = 2,
-		.pin = 1,
-		.func = 1,
-		.route_offset = 0x50,
-		.route_val = BIT(16) | BIT(16 + 1) | BIT(0),
-	}, {
-		/* gmac-m1_rxd0 */
-		.bank_num = 1,
-		.pin = 11,
-		.func = 2,
-		.route_offset = 0x50,
-		.route_val = BIT(16 + 2) | BIT(2),
-	}, {
-		/* gmac-m1-optimized_rxd3 */
-		.bank_num = 1,
-		.pin = 14,
-		.func = 2,
-		.route_offset = 0x50,
-		.route_val = BIT(16 + 10) | BIT(10),
-	}, {
-		/* pdm_sdi0m0 */
-		.bank_num = 2,
-		.pin = 19,
-		.func = 2,
-		.route_offset = 0x50,
-		.route_val = BIT(16 + 3),
-	}, {
-		/* pdm_sdi0m1 */
-		.bank_num = 1,
-		.pin = 23,
-		.func = 3,
-		.route_offset = 0x50,
-		.route_val =  BIT(16 + 3) | BIT(3),
-	}, {
-		/* spi_rxdm2 */
-		.bank_num = 3,
-		.pin = 2,
-		.func = 4,
-		.route_offset = 0x50,
-		.route_val =  BIT(16 + 4) | BIT(16 + 5) | BIT(5),
-	}, {
-		/* i2s2_sdim0 */
-		.bank_num = 1,
-		.pin = 24,
-		.func = 1,
-		.route_offset = 0x50,
-		.route_val = BIT(16 + 6),
-	}, {
-		/* i2s2_sdim1 */
-		.bank_num = 3,
-		.pin = 2,
-		.func = 6,
-		.route_offset = 0x50,
-		.route_val =  BIT(16 + 6) | BIT(6),
-	}, {
-		/* card_iom1 */
-		.bank_num = 2,
-		.pin = 22,
-		.func = 3,
-		.route_offset = 0x50,
-		.route_val =  BIT(16 + 7) | BIT(7),
-	}, {
-		/* tsp_d5m1 */
-		.bank_num = 2,
-		.pin = 16,
-		.func = 3,
-		.route_offset = 0x50,
-		.route_val =  BIT(16 + 8) | BIT(8),
-	}, {
-		/* cif_data5m1 */
-		.bank_num = 2,
-		.pin = 16,
-		.func = 4,
-		.route_offset = 0x50,
-		.route_val =  BIT(16 + 9) | BIT(9),
-	},
+	RK_MUXROUTE_SAME(1, RK_PA1, 2, 0x50, BIT(16) | BIT(16 + 1)), /* uart2dbg_rxm0 */
+	RK_MUXROUTE_SAME(2, RK_PA1, 1, 0x50, BIT(16) | BIT(16 + 1) | BIT(0)), /* uart2dbg_rxm1 */
+	RK_MUXROUTE_SAME(1, RK_PB3, 2, 0x50, BIT(16 + 2) | BIT(2)), /* gmac-m1_rxd0 */
+	RK_MUXROUTE_SAME(1, RK_PB6, 2, 0x50, BIT(16 + 10) | BIT(10)), /* gmac-m1-optimized_rxd3 */
+	RK_MUXROUTE_SAME(2, RK_PC3, 2, 0x50, BIT(16 + 3)), /* pdm_sdi0m0 */
+	RK_MUXROUTE_SAME(1, RK_PC7, 3, 0x50, BIT(16 + 3) | BIT(3)), /* pdm_sdi0m1 */
+	RK_MUXROUTE_SAME(3, RK_PA2, 4, 0x50, BIT(16 + 4) | BIT(16 + 5) | BIT(5)), /* spi_rxdm2 */
+	RK_MUXROUTE_SAME(1, RK_PD0, 1, 0x50, BIT(16 + 6)), /* i2s2_sdim0 */
+	RK_MUXROUTE_SAME(3, RK_PA2, 6, 0x50, BIT(16 + 6) | BIT(6)), /* i2s2_sdim1 */
+	RK_MUXROUTE_SAME(2, RK_PC6, 3, 0x50, BIT(16 + 7) | BIT(7)), /* card_iom1 */
+	RK_MUXROUTE_SAME(2, RK_PC0, 3, 0x50, BIT(16 + 8) | BIT(8)), /* tsp_d5m1 */
+	RK_MUXROUTE_SAME(2, RK_PC0, 4, 0x50, BIT(16 + 9) | BIT(9)), /* cif_data5m1 */
 };
 
 static struct rockchip_mux_route_data rk3399_mux_route_data[] = {
-	{
-		/* uart2dbga_rx */
-		.bank_num = 4,
-		.pin = 8,
-		.func = 2,
-		.route_offset = 0xe21c,
-		.route_val = BIT(16 + 10) | BIT(16 + 11),
-	}, {
-		/* uart2dbgb_rx */
-		.bank_num = 4,
-		.pin = 16,
-		.func = 2,
-		.route_offset = 0xe21c,
-		.route_val = BIT(16 + 10) | BIT(16 + 11) | BIT(10),
-	}, {
-		/* uart2dbgc_rx */
-		.bank_num = 4,
-		.pin = 19,
-		.func = 1,
-		.route_offset = 0xe21c,
-		.route_val = BIT(16 + 10) | BIT(16 + 11) | BIT(11),
-	}, {
-		/* pcie_clkreqn */
-		.bank_num = 2,
-		.pin = 26,
-		.func = 2,
-		.route_offset = 0xe21c,
-		.route_val = BIT(16 + 14),
-	}, {
-		/* pcie_clkreqnb */
-		.bank_num = 4,
-		.pin = 24,
-		.func = 1,
-		.route_offset = 0xe21c,
-		.route_val = BIT(16 + 14) | BIT(14),
-	},
+	RK_MUXROUTE_SAME(4, RK_PB0, 2, 0xe21c, BIT(16 + 10) | BIT(16 + 11)), /* uart2dbga_rx */
+	RK_MUXROUTE_SAME(4, RK_PC0, 2, 0xe21c, BIT(16 + 10) | BIT(16 + 11) | BIT(10)), /* uart2dbgb_rx */
+	RK_MUXROUTE_SAME(4, RK_PC3, 1, 0xe21c, BIT(16 + 10) | BIT(16 + 11) | BIT(11)), /* uart2dbgc_rx */
+	RK_MUXROUTE_SAME(2, RK_PD2, 2, 0xe21c, BIT(16 + 14)), /* pcie_clkreqn */
+	RK_MUXROUTE_SAME(4, RK_PD0, 1, 0xe21c, BIT(16 + 14) | BIT(14)), /* pcie_clkreqnb */
+};
+
+static struct rockchip_mux_route_data rk3568_mux_route_data[] = {
+	RK_MUXROUTE_PMU(0, RK_PB7, 1, 0x0110, WRITE_MASK_VAL(1, 0, 0)), /* PWM0 IO mux M0 */
+	RK_MUXROUTE_PMU(0, RK_PC7, 2, 0x0110, WRITE_MASK_VAL(1, 0, 1)), /* PWM0 IO mux M1 */
+	RK_MUXROUTE_PMU(0, RK_PC0, 1, 0x0110, WRITE_MASK_VAL(3, 2, 0)), /* PWM1 IO mux M0 */
+	RK_MUXROUTE_PMU(0, RK_PB5, 4, 0x0110, WRITE_MASK_VAL(3, 2, 1)), /* PWM1 IO mux M1 */
+	RK_MUXROUTE_PMU(0, RK_PC1, 1, 0x0110, WRITE_MASK_VAL(5, 4, 0)), /* PWM2 IO mux M0 */
+	RK_MUXROUTE_PMU(0, RK_PB6, 4, 0x0110, WRITE_MASK_VAL(5, 4, 1)), /* PWM2 IO mux M1 */
+	RK_MUXROUTE_GRF(0, RK_PB3, 2, 0x0300, WRITE_MASK_VAL(0, 0, 0)), /* CAN0 IO mux M0 */
+	RK_MUXROUTE_GRF(2, RK_PA1, 4, 0x0300, WRITE_MASK_VAL(0, 0, 1)), /* CAN0 IO mux M1 */
+	RK_MUXROUTE_GRF(1, RK_PA1, 3, 0x0300, WRITE_MASK_VAL(2, 2, 0)), /* CAN1 IO mux M0 */
+	RK_MUXROUTE_GRF(4, RK_PC3, 3, 0x0300, WRITE_MASK_VAL(2, 2, 1)), /* CAN1 IO mux M1 */
+	RK_MUXROUTE_GRF(4, RK_PB5, 3, 0x0300, WRITE_MASK_VAL(4, 4, 0)), /* CAN2 IO mux M0 */
+	RK_MUXROUTE_GRF(2, RK_PB2, 4, 0x0300, WRITE_MASK_VAL(4, 4, 1)), /* CAN2 IO mux M1 */
+	RK_MUXROUTE_GRF(4, RK_PC4, 1, 0x0300, WRITE_MASK_VAL(6, 6, 0)), /* HPDIN IO mux M0 */
+	RK_MUXROUTE_GRF(0, RK_PC2, 2, 0x0300, WRITE_MASK_VAL(6, 6, 1)), /* HPDIN IO mux M1 */
+	RK_MUXROUTE_GRF(3, RK_PB1, 3, 0x0300, WRITE_MASK_VAL(8, 8, 0)), /* GMAC1 IO mux M0 */
+	RK_MUXROUTE_GRF(4, RK_PA7, 3, 0x0300, WRITE_MASK_VAL(8, 8, 1)), /* GMAC1 IO mux M1 */
+	RK_MUXROUTE_GRF(4, RK_PB7, 3, 0x0300, WRITE_MASK_VAL(8, 8, 1)), /* GMAC1 IO mux M1 */
+	RK_MUXROUTE_GRF(4, RK_PD1, 1, 0x0300, WRITE_MASK_VAL(10, 10, 0)), /* HDMITX IO mux M0 */
+	RK_MUXROUTE_GRF(0, RK_PC7, 1, 0x0300, WRITE_MASK_VAL(10, 10, 1)), /* HDMITX IO mux M1 */
+	RK_MUXROUTE_GRF(0, RK_PB6, 1, 0x0300, WRITE_MASK_VAL(14, 14, 0)), /* I2C2 IO mux M0 */
+	RK_MUXROUTE_GRF(4, RK_PB4, 1, 0x0300, WRITE_MASK_VAL(14, 14, 1)), /* I2C2 IO mux M1 */
+	RK_MUXROUTE_GRF(1, RK_PA0, 1, 0x0304, WRITE_MASK_VAL(0, 0, 0)), /* I2C3 IO mux M0 */
+	RK_MUXROUTE_GRF(3, RK_PB6, 4, 0x0304, WRITE_MASK_VAL(0, 0, 1)), /* I2C3 IO mux M1 */
+	RK_MUXROUTE_GRF(4, RK_PB2, 1, 0x0304, WRITE_MASK_VAL(2, 2, 0)), /* I2C4 IO mux M0 */
+	RK_MUXROUTE_GRF(2, RK_PB1, 2, 0x0304, WRITE_MASK_VAL(2, 2, 1)), /* I2C4 IO mux M1 */
+	RK_MUXROUTE_GRF(3, RK_PB4, 4, 0x0304, WRITE_MASK_VAL(4, 4, 0)), /* I2C5 IO mux M0 */
+	RK_MUXROUTE_GRF(4, RK_PD0, 2, 0x0304, WRITE_MASK_VAL(4, 4, 1)), /* I2C5 IO mux M1 */
+	RK_MUXROUTE_GRF(3, RK_PB1, 5, 0x0304, WRITE_MASK_VAL(14, 14, 0)), /* PWM8 IO mux M0 */
+	RK_MUXROUTE_GRF(1, RK_PD5, 4, 0x0304, WRITE_MASK_VAL(14, 14, 1)), /* PWM8 IO mux M1 */
+	RK_MUXROUTE_GRF(3, RK_PB2, 5, 0x0308, WRITE_MASK_VAL(0, 0, 0)), /* PWM9 IO mux M0 */
+	RK_MUXROUTE_GRF(1, RK_PD6, 4, 0x0308, WRITE_MASK_VAL(0, 0, 1)), /* PWM9 IO mux M1 */
+	RK_MUXROUTE_GRF(3, RK_PB5, 5, 0x0308, WRITE_MASK_VAL(2, 2, 0)), /* PWM10 IO mux M0 */
+	RK_MUXROUTE_GRF(2, RK_PA1, 2, 0x0308, WRITE_MASK_VAL(2, 2, 1)), /* PWM10 IO mux M1 */
+	RK_MUXROUTE_GRF(3, RK_PB6, 5, 0x0308, WRITE_MASK_VAL(4, 4, 0)), /* PWM11 IO mux M0 */
+	RK_MUXROUTE_GRF(4, RK_PC0, 3, 0x0308, WRITE_MASK_VAL(4, 4, 1)), /* PWM11 IO mux M1 */
+	RK_MUXROUTE_GRF(3, RK_PB7, 2, 0x0308, WRITE_MASK_VAL(6, 6, 0)), /* PWM12 IO mux M0 */
+	RK_MUXROUTE_GRF(4, RK_PC5, 1, 0x0308, WRITE_MASK_VAL(6, 6, 1)), /* PWM12 IO mux M1 */
+	RK_MUXROUTE_GRF(3, RK_PC0, 2, 0x0308, WRITE_MASK_VAL(8, 8, 0)), /* PWM13 IO mux M0 */
+	RK_MUXROUTE_GRF(4, RK_PC6, 1, 0x0308, WRITE_MASK_VAL(8, 8, 1)), /* PWM13 IO mux M1 */
+	RK_MUXROUTE_GRF(3, RK_PC4, 1, 0x0308, WRITE_MASK_VAL(10, 10, 0)), /* PWM14 IO mux M0 */
+	RK_MUXROUTE_GRF(4, RK_PC2, 1, 0x0308, WRITE_MASK_VAL(10, 10, 1)), /* PWM14 IO mux M1 */
+	RK_MUXROUTE_GRF(3, RK_PC5, 1, 0x0308, WRITE_MASK_VAL(12, 12, 0)), /* PWM15 IO mux M0 */
+	RK_MUXROUTE_GRF(4, RK_PC3, 1, 0x0308, WRITE_MASK_VAL(12, 12, 1)), /* PWM15 IO mux M1 */
+	RK_MUXROUTE_GRF(3, RK_PD2, 3, 0x0308, WRITE_MASK_VAL(14, 14, 0)), /* SDMMC2 IO mux M0 */
+	RK_MUXROUTE_GRF(3, RK_PA5, 5, 0x0308, WRITE_MASK_VAL(14, 14, 1)), /* SDMMC2 IO mux M1 */
+	RK_MUXROUTE_GRF(0, RK_PB5, 2, 0x030c, WRITE_MASK_VAL(0, 0, 0)), /* SPI0 IO mux M0 */
+	RK_MUXROUTE_GRF(2, RK_PD3, 3, 0x030c, WRITE_MASK_VAL(0, 0, 1)), /* SPI0 IO mux M1 */
+	RK_MUXROUTE_GRF(2, RK_PB5, 3, 0x030c, WRITE_MASK_VAL(2, 2, 0)), /* SPI1 IO mux M0 */
+	RK_MUXROUTE_GRF(3, RK_PC3, 3, 0x030c, WRITE_MASK_VAL(2, 2, 1)), /* SPI1 IO mux M1 */
+	RK_MUXROUTE_GRF(2, RK_PC1, 4, 0x030c, WRITE_MASK_VAL(4, 4, 0)), /* SPI2 IO mux M0 */
+	RK_MUXROUTE_GRF(3, RK_PA0, 3, 0x030c, WRITE_MASK_VAL(4, 4, 1)), /* SPI2 IO mux M1 */
+	RK_MUXROUTE_GRF(4, RK_PB3, 4, 0x030c, WRITE_MASK_VAL(6, 6, 0)), /* SPI3 IO mux M0 */
+	RK_MUXROUTE_GRF(4, RK_PC2, 2, 0x030c, WRITE_MASK_VAL(6, 6, 1)), /* SPI3 IO mux M1 */
+	RK_MUXROUTE_GRF(2, RK_PB4, 2, 0x030c, WRITE_MASK_VAL(8, 8, 0)), /* UART1 IO mux M0 */
+	RK_MUXROUTE_GRF(3, RK_PD6, 4, 0x030c, WRITE_MASK_VAL(8, 8, 1)), /* UART1 IO mux M1 */
+	RK_MUXROUTE_GRF(0, RK_PD1, 1, 0x030c, WRITE_MASK_VAL(10, 10, 0)), /* UART2 IO mux M0 */
+	RK_MUXROUTE_GRF(1, RK_PD5, 2, 0x030c, WRITE_MASK_VAL(10, 10, 1)), /* UART2 IO mux M1 */
+	RK_MUXROUTE_GRF(1, RK_PA1, 2, 0x030c, WRITE_MASK_VAL(12, 12, 0)), /* UART3 IO mux M0 */
+	RK_MUXROUTE_GRF(3, RK_PB7, 4, 0x030c, WRITE_MASK_VAL(12, 12, 1)), /* UART3 IO mux M1 */
+	RK_MUXROUTE_GRF(1, RK_PA6, 2, 0x030c, WRITE_MASK_VAL(14, 14, 0)), /* UART4 IO mux M0 */
+	RK_MUXROUTE_GRF(3, RK_PB2, 4, 0x030c, WRITE_MASK_VAL(14, 14, 1)), /* UART4 IO mux M1 */
+	RK_MUXROUTE_GRF(2, RK_PA2, 3, 0x0310, WRITE_MASK_VAL(0, 0, 0)), /* UART5 IO mux M0 */
+	RK_MUXROUTE_GRF(3, RK_PC2, 4, 0x0310, WRITE_MASK_VAL(0, 0, 1)), /* UART5 IO mux M1 */
+	RK_MUXROUTE_GRF(2, RK_PA4, 3, 0x0310, WRITE_MASK_VAL(2, 2, 0)), /* UART6 IO mux M0 */
+	RK_MUXROUTE_GRF(1, RK_PD5, 3, 0x0310, WRITE_MASK_VAL(2, 2, 1)), /* UART6 IO mux M1 */
+	RK_MUXROUTE_GRF(2, RK_PA6, 3, 0x0310, WRITE_MASK_VAL(5, 4, 0)), /* UART7 IO mux M0 */
+	RK_MUXROUTE_GRF(3, RK_PC4, 4, 0x0310, WRITE_MASK_VAL(5, 4, 1)), /* UART7 IO mux M1 */
+	RK_MUXROUTE_GRF(4, RK_PA2, 4, 0x0310, WRITE_MASK_VAL(5, 4, 2)), /* UART7 IO mux M2 */
+	RK_MUXROUTE_GRF(2, RK_PC5, 3, 0x0310, WRITE_MASK_VAL(6, 6, 0)), /* UART8 IO mux M0 */
+	RK_MUXROUTE_GRF(2, RK_PD7, 4, 0x0310, WRITE_MASK_VAL(6, 6, 1)), /* UART8 IO mux M1 */
+	RK_MUXROUTE_GRF(2, RK_PB0, 3, 0x0310, WRITE_MASK_VAL(9, 8, 0)), /* UART9 IO mux M0 */
+	RK_MUXROUTE_GRF(4, RK_PC5, 4, 0x0310, WRITE_MASK_VAL(9, 8, 1)), /* UART9 IO mux M1 */
+	RK_MUXROUTE_GRF(4, RK_PA4, 4, 0x0310, WRITE_MASK_VAL(9, 8, 2)), /* UART9 IO mux M2 */
+	RK_MUXROUTE_GRF(1, RK_PA2, 1, 0x0310, WRITE_MASK_VAL(11, 10, 0)), /* I2S1 IO mux M0 */
+	RK_MUXROUTE_GRF(3, RK_PC6, 4, 0x0310, WRITE_MASK_VAL(11, 10, 1)), /* I2S1 IO mux M1 */
+	RK_MUXROUTE_GRF(2, RK_PD0, 5, 0x0310, WRITE_MASK_VAL(11, 10, 2)), /* I2S1 IO mux M2 */
+	RK_MUXROUTE_GRF(2, RK_PC1, 1, 0x0310, WRITE_MASK_VAL(12, 12, 0)), /* I2S2 IO mux M0 */
+	RK_MUXROUTE_GRF(4, RK_PB6, 5, 0x0310, WRITE_MASK_VAL(12, 12, 1)), /* I2S2 IO mux M1 */
+	RK_MUXROUTE_GRF(3, RK_PA2, 4, 0x0310, WRITE_MASK_VAL(14, 14, 0)), /* I2S3 IO mux M0 */
+	RK_MUXROUTE_GRF(4, RK_PC2, 5, 0x0310, WRITE_MASK_VAL(14, 14, 1)), /* I2S3 IO mux M1 */
+	RK_MUXROUTE_GRF(1, RK_PA4, 3, 0x0314, WRITE_MASK_VAL(1, 0, 0)), /* PDM IO mux M0 */
+	RK_MUXROUTE_GRF(1, RK_PA6, 3, 0x0314, WRITE_MASK_VAL(1, 0, 0)), /* PDM IO mux M0 */
+	RK_MUXROUTE_GRF(3, RK_PD6, 5, 0x0314, WRITE_MASK_VAL(1, 0, 1)), /* PDM IO mux M1 */
+	RK_MUXROUTE_GRF(4, RK_PA0, 4, 0x0314, WRITE_MASK_VAL(1, 0, 1)), /* PDM IO mux M1 */
+	RK_MUXROUTE_GRF(3, RK_PC4, 5, 0x0314, WRITE_MASK_VAL(1, 0, 2)), /* PDM IO mux M2 */
+	RK_MUXROUTE_GRF(0, RK_PA5, 3, 0x0314, WRITE_MASK_VAL(3, 2, 0)), /* PCIE20 IO mux M0 */
+	RK_MUXROUTE_GRF(2, RK_PD0, 4, 0x0314, WRITE_MASK_VAL(3, 2, 1)), /* PCIE20 IO mux M1 */
+	RK_MUXROUTE_GRF(1, RK_PB0, 4, 0x0314, WRITE_MASK_VAL(3, 2, 2)), /* PCIE20 IO mux M2 */
+	RK_MUXROUTE_GRF(0, RK_PA4, 3, 0x0314, WRITE_MASK_VAL(5, 4, 0)), /* PCIE30X1 IO mux M0 */
+	RK_MUXROUTE_GRF(2, RK_PD2, 4, 0x0314, WRITE_MASK_VAL(5, 4, 1)), /* PCIE30X1 IO mux M1 */
+	RK_MUXROUTE_GRF(1, RK_PA5, 4, 0x0314, WRITE_MASK_VAL(5, 4, 2)), /* PCIE30X1 IO mux M2 */
+	RK_MUXROUTE_GRF(0, RK_PA6, 2, 0x0314, WRITE_MASK_VAL(7, 6, 0)), /* PCIE30X2 IO mux M0 */
+	RK_MUXROUTE_GRF(2, RK_PD4, 4, 0x0314, WRITE_MASK_VAL(7, 6, 1)), /* PCIE30X2 IO mux M1 */
+	RK_MUXROUTE_GRF(4, RK_PC2, 4, 0x0314, WRITE_MASK_VAL(7, 6, 2)), /* PCIE30X2 IO mux M2 */
 };
 
 static bool rockchip_get_mux_route(struct rockchip_pin_bank *bank, int pin,
@@ -1437,6 +1054,7 @@ static bool rockchip_get_mux_route(struct rockchip_pin_bank *bank, int pin,
 static int rockchip_get_mux(struct rockchip_pin_bank *bank, int pin)
 {
 	struct rockchip_pinctrl *info = bank->drvdata;
+	struct rockchip_pin_ctrl *ctrl = info->ctrl;
 	int iomux_num = (pin / 8);
 	struct regmap *regmap;
 	unsigned int val;
@@ -1454,8 +1072,12 @@ static int rockchip_get_mux(struct rockchip_pin_bank *bank, int pin)
 	if (bank->iomux[iomux_num].type & IOMUX_GPIO_ONLY)
 		return RK_FUNC_GPIO;
 
-	regmap = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU)
-				? info->regmap_pmu : info->regmap_base;
+	if (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU)
+		regmap = info->regmap_pmu;
+	else if (bank->iomux[iomux_num].type & IOMUX_L_SOURCE_PMU)
+		regmap = (pin % 8 < 4) ? info->regmap_pmu : info->regmap_base;
+	else
+		regmap = info->regmap_base;
 
 	/* get basic quadrupel of mux registers and the correct reg inside */
 	mux_type = bank->iomux[iomux_num].type;
@@ -1478,6 +1100,27 @@ static int rockchip_get_mux(struct rockchip_pin_bank *bank, int pin)
 	if (bank->recalced_mask & BIT(pin))
 		rockchip_get_recalced_mux(bank, pin, &reg, &bit, &mask);
 
+	if (ctrl->type == RK3588) {
+		if (bank->bank_num == 0) {
+			if ((pin >= RK_PB4) && (pin <= RK_PD7)) {
+				u32 reg0 = 0;
+
+				reg0 = reg + 0x4000 - 0xC; /* PMU2_IOC_BASE */
+				ret = regmap_read(regmap, reg0, &val);
+				if (ret)
+					return ret;
+
+				if (!(val & BIT(8)))
+					return ((val >> bit) & mask);
+
+				reg = reg + 0x8000; /* BUS_IOC_BASE */
+				regmap = info->regmap_base;
+			}
+		} else if (bank->bank_num > 0) {
+			reg += 0x8000; /* BUS_IOC_BASE */
+		}
+	}
+
 	ret = regmap_read(regmap, reg, &val);
 	if (ret)
 		return ret;
@@ -1526,6 +1169,7 @@ static int rockchip_verify_mux(struct rockchip_pin_bank *bank,
 static int rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux)
 {
 	struct rockchip_pinctrl *info = bank->drvdata;
+	struct rockchip_pin_ctrl *ctrl = info->ctrl;
 	int iomux_num = (pin / 8);
 	struct regmap *regmap;
 	int reg, ret, mask, mux_type;
@@ -1542,8 +1186,12 @@ static int rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux)
 	dev_dbg(info->dev, "setting mux of GPIO%d-%d to %d\n",
 						bank->bank_num, pin, mux);
 
-	regmap = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU)
-				? info->regmap_pmu : info->regmap_base;
+	if (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU)
+		regmap = info->regmap_pmu;
+	else if (bank->iomux[iomux_num].type & IOMUX_L_SOURCE_PMU)
+		regmap = (pin % 8 < 4) ? info->regmap_pmu : info->regmap_base;
+	else
+		regmap = info->regmap_base;
 
 	/* get basic quadrupel of mux registers and the correct reg inside */
 	mux_type = bank->iomux[iomux_num].type;
@@ -1566,6 +1214,46 @@ static int rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux)
 	if (bank->recalced_mask & BIT(pin))
 		rockchip_get_recalced_mux(bank, pin, &reg, &bit, &mask);
 
+	if (ctrl->type == RK3588) {
+		if (bank->bank_num == 0) {
+			if ((pin >= RK_PB4) && (pin <= RK_PD7)) {
+				if (mux < 8) {
+					reg += 0x4000 - 0xC; /* PMU2_IOC_BASE */
+					data = (mask << (bit + 16));
+					rmask = data | (data >> 16);
+					data |= (mux & mask) << bit;
+					ret = regmap_update_bits(regmap, reg, rmask, data);
+				} else {
+					u32 reg0 = 0;
+
+					reg0 = reg + 0x4000 - 0xC; /* PMU2_IOC_BASE */
+					data = (mask << (bit + 16));
+					rmask = data | (data >> 16);
+					data |= 8 << bit;
+					ret = regmap_update_bits(regmap, reg0, rmask, data);
+
+					reg0 = reg + 0x8000; /* BUS_IOC_BASE */
+					data = (mask << (bit + 16));
+					rmask = data | (data >> 16);
+					data |= mux << bit;
+					regmap = info->regmap_base;
+					ret |= regmap_update_bits(regmap, reg0, rmask, data);
+				}
+			} else {
+				data = (mask << (bit + 16));
+				rmask = data | (data >> 16);
+				data |= (mux & mask) << bit;
+				ret = regmap_update_bits(regmap, reg, rmask, data);
+			}
+			return ret;
+		} else if (bank->bank_num > 0) {
+			reg += 0x8000; /* BUS_IOC_BASE */
+		}
+	}
+
+	if (mux > mask)
+		return -EINVAL;
+
 	if (bank->route_mask & BIT(pin)) {
 		if (rockchip_get_mux_route(bank, pin, mux, &route_location,
 					   &route_reg, &route_val)) {
@@ -1587,10 +1275,20 @@ static int rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux)
 		}
 	}
 
-	data = (mask << (bit + 16));
-	rmask = data | (data >> 16);
-	data |= (mux & mask) << bit;
-	ret = regmap_update_bits(regmap, reg, rmask, data);
+	if (mux_type & IOMUX_WRITABLE_32BIT) {
+		ret = regmap_read(regmap, reg, &data);
+		if (ret)
+			return ret;
+
+		data &= ~(mask << bit);
+		data |= (mux & mask) << bit;
+		ret = regmap_write(regmap, reg, data);
+	} else {
+		data = (mask << (bit + 16));
+		rmask = data | (data >> 16);
+		data |= (mux & mask) << bit;
+		ret = regmap_update_bits(regmap, reg, rmask, data);
+	}
 
 	return ret;
 }
@@ -1775,6 +1473,115 @@ static int rv1108_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank,
 	return 0;
 }
 
+#define RV1126_PULL_PMU_OFFSET		0x40
+#define RV1126_PULL_GRF_GPIO1A0_OFFSET		0x10108
+#define RV1126_PULL_PINS_PER_REG	8
+#define RV1126_PULL_BITS_PER_PIN	2
+#define RV1126_PULL_BANK_STRIDE		16
+#define RV1126_GPIO_C4_D7(p)	(p >= 20 && p <= 31) /* GPIO0_C4 ~ GPIO0_D7 */
+
+static void rv1126_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+					 int pin_num, struct regmap **regmap,
+					 int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+
+	/* The first 24 pins of the first bank are located in PMU */
+	if (bank->bank_num == 0) {
+		if (RV1126_GPIO_C4_D7(pin_num)) {
+			*regmap = info->regmap_base;
+			*reg = RV1126_PULL_GRF_GPIO1A0_OFFSET;
+			*reg -= (((31 - pin_num) / RV1126_PULL_PINS_PER_REG + 1) * 4);
+			*bit = pin_num % RV1126_PULL_PINS_PER_REG;
+			*bit *= RV1126_PULL_BITS_PER_PIN;
+			return;
+		}
+		*regmap = info->regmap_pmu;
+		*reg = RV1126_PULL_PMU_OFFSET;
+	} else {
+		*reg = RV1126_PULL_GRF_GPIO1A0_OFFSET;
+		*regmap = info->regmap_base;
+		*reg += (bank->bank_num - 1) * RV1126_PULL_BANK_STRIDE;
+	}
+
+	*reg += ((pin_num / RV1126_PULL_PINS_PER_REG) * 4);
+	*bit = (pin_num % RV1126_PULL_PINS_PER_REG);
+	*bit *= RV1126_PULL_BITS_PER_PIN;
+}
+
+#define RV1126_DRV_PMU_OFFSET		0x20
+#define RV1126_DRV_GRF_GPIO1A0_OFFSET		0x10090
+#define RV1126_DRV_BITS_PER_PIN		4
+#define RV1126_DRV_PINS_PER_REG		4
+#define RV1126_DRV_BANK_STRIDE		32
+
+static void rv1126_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+					int pin_num, struct regmap **regmap,
+					int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+
+	/* The first 24 pins of the first bank are located in PMU */
+	if (bank->bank_num == 0) {
+		if (RV1126_GPIO_C4_D7(pin_num)) {
+			*regmap = info->regmap_base;
+			*reg = RV1126_DRV_GRF_GPIO1A0_OFFSET;
+			*reg -= (((31 - pin_num) / RV1126_DRV_PINS_PER_REG + 1) * 4);
+			*reg -= 0x4;
+			*bit = pin_num % RV1126_DRV_PINS_PER_REG;
+			*bit *= RV1126_DRV_BITS_PER_PIN;
+			return;
+		}
+		*regmap = info->regmap_pmu;
+		*reg = RV1126_DRV_PMU_OFFSET;
+	} else {
+		*regmap = info->regmap_base;
+		*reg = RV1126_DRV_GRF_GPIO1A0_OFFSET;
+		*reg += (bank->bank_num - 1) * RV1126_DRV_BANK_STRIDE;
+	}
+
+	*reg += ((pin_num / RV1126_DRV_PINS_PER_REG) * 4);
+	*bit = pin_num % RV1126_DRV_PINS_PER_REG;
+	*bit *= RV1126_DRV_BITS_PER_PIN;
+}
+
+#define RV1126_SCHMITT_PMU_OFFSET		0x60
+#define RV1126_SCHMITT_GRF_GPIO1A0_OFFSET		0x10188
+#define RV1126_SCHMITT_BANK_STRIDE		16
+#define RV1126_SCHMITT_PINS_PER_GRF_REG		8
+#define RV1126_SCHMITT_PINS_PER_PMU_REG		8
+
+static int rv1126_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank,
+					   int pin_num,
+					   struct regmap **regmap,
+					   int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+	int pins_per_reg;
+
+	if (bank->bank_num == 0) {
+		if (RV1126_GPIO_C4_D7(pin_num)) {
+			*regmap = info->regmap_base;
+			*reg = RV1126_SCHMITT_GRF_GPIO1A0_OFFSET;
+			*reg -= (((31 - pin_num) / RV1126_SCHMITT_PINS_PER_GRF_REG + 1) * 4);
+			*bit = pin_num % RV1126_SCHMITT_PINS_PER_GRF_REG;
+			return 0;
+		}
+		*regmap = info->regmap_pmu;
+		*reg = RV1126_SCHMITT_PMU_OFFSET;
+		pins_per_reg = RV1126_SCHMITT_PINS_PER_PMU_REG;
+	} else {
+		*regmap = info->regmap_base;
+		*reg = RV1126_SCHMITT_GRF_GPIO1A0_OFFSET;
+		pins_per_reg = RV1126_SCHMITT_PINS_PER_GRF_REG;
+		*reg += (bank->bank_num - 1) * RV1126_SCHMITT_BANK_STRIDE;
+	}
+	*reg += ((pin_num / pins_per_reg) * 4);
+	*bit = pin_num % pins_per_reg;
+
+	return 0;
+}
+
 #define RK3308_SCHMITT_PINS_PER_REG		8
 #define RK3308_SCHMITT_BANK_STRIDE		16
 #define RK3308_SCHMITT_GRF_OFFSET		0x1a0
@@ -1795,6 +1602,111 @@ static int rk3308_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank,
 	return 0;
 }
 
+#define RK1808_PULL_PMU_OFFSET		0x10
+#define RK1808_PULL_GRF_OFFSET		0x80
+#define RK1808_PULL_PINS_PER_REG	8
+#define RK1808_PULL_BITS_PER_PIN	2
+#define RK1808_PULL_BANK_STRIDE		16
+
+static void rk1808_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+					 int pin_num, struct regmap **regmap,
+					 int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+
+	if (bank->bank_num == 0) {
+		*regmap = info->regmap_pmu;
+		*reg = RK1808_PULL_PMU_OFFSET;
+	} else {
+		*reg = RK1808_PULL_GRF_OFFSET;
+		*regmap = info->regmap_base;
+		*reg += (bank->bank_num - 1) * RK1808_PULL_BANK_STRIDE;
+	}
+
+	*reg += ((pin_num / RK1808_PULL_PINS_PER_REG) * 4);
+	*bit = (pin_num % RK1808_PULL_PINS_PER_REG);
+	*bit *= RK1808_PULL_BITS_PER_PIN;
+}
+
+#define RK1808_DRV_PMU_OFFSET		0x20
+#define RK1808_DRV_GRF_OFFSET		0x140
+#define RK1808_DRV_BITS_PER_PIN		2
+#define RK1808_DRV_PINS_PER_REG		8
+#define RK1808_DRV_BANK_STRIDE		16
+
+static void rk1808_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+					int pin_num,
+					struct regmap **regmap,
+					int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+
+	if (bank->bank_num == 0) {
+		*regmap = info->regmap_pmu;
+		*reg = RK1808_DRV_PMU_OFFSET;
+	} else {
+		*regmap = info->regmap_base;
+		*reg = RK1808_DRV_GRF_OFFSET;
+		*reg += (bank->bank_num - 1) * RK1808_DRV_BANK_STRIDE;
+	}
+
+	*reg += ((pin_num / RK1808_DRV_PINS_PER_REG) * 4);
+	*bit = pin_num % RK1808_DRV_PINS_PER_REG;
+	*bit *= RK1808_DRV_BITS_PER_PIN;
+}
+
+#define RK1808_SR_PMU_OFFSET		0x0030
+#define RK1808_SR_GRF_OFFSET		0x00c0
+#define RK1808_SR_BANK_STRIDE		16
+#define RK1808_SR_PINS_PER_REG		8
+
+static int rk1808_calc_slew_rate_reg_and_bit(struct rockchip_pin_bank *bank,
+					   int pin_num,
+					   struct regmap **regmap,
+					   int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+
+	if (bank->bank_num == 0) {
+		*regmap = info->regmap_pmu;
+		*reg = RK1808_SR_PMU_OFFSET;
+	} else {
+		*regmap = info->regmap_base;
+		*reg = RK1808_SR_GRF_OFFSET;
+		*reg += (bank->bank_num  - 1) * RK1808_SR_BANK_STRIDE;
+	}
+	*reg += ((pin_num / RK1808_SR_PINS_PER_REG) * 4);
+	*bit = pin_num % RK1808_SR_PINS_PER_REG;
+
+	return 0;
+}
+
+#define RK1808_SCHMITT_PMU_OFFSET		0x0040
+#define RK1808_SCHMITT_GRF_OFFSET		0x0100
+#define RK1808_SCHMITT_BANK_STRIDE		16
+#define RK1808_SCHMITT_PINS_PER_REG		8
+
+static int rk1808_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank,
+					   int pin_num,
+					   struct regmap **regmap,
+					   int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+
+	if (bank->bank_num == 0) {
+		*regmap = info->regmap_pmu;
+		*reg = RK1808_SCHMITT_PMU_OFFSET;
+	} else {
+		*regmap = info->regmap_base;
+		*reg = RK1808_SCHMITT_GRF_OFFSET;
+		*reg += (bank->bank_num  - 1) * RK1808_SCHMITT_BANK_STRIDE;
+	}
+	*reg += ((pin_num / RK1808_SCHMITT_PINS_PER_REG) * 4);
+	*bit = pin_num % RK1808_SCHMITT_PINS_PER_REG;
+
+	return 0;
+}
+
 #define RK2928_PULL_OFFSET		0x118
 #define RK2928_PULL_PINS_PER_REG	16
 #define RK2928_PULL_BANK_STRIDE		8
@@ -2117,58 +2029,330 @@ static void rk3399_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
 		*bit = (pin_num % 8) * 2;
 }
 
-static int rockchip_perpin_drv_list[DRV_TYPE_MAX][8] = {
-	{ 2, 4, 8, 12, -1, -1, -1, -1 },
-	{ 3, 6, 9, 12, -1, -1, -1, -1 },
-	{ 5, 10, 15, 20, -1, -1, -1, -1 },
-	{ 4, 6, 8, 10, 12, 14, 16, 18 },
-	{ 4, 7, 10, 13, 16, 19, 22, 26 }
-};
+#define RK3568_SR_PMU_OFFSET		0x60
+#define RK3568_SR_GRF_OFFSET		0x0180
+#define RK3568_SR_BANK_STRIDE		0x10
+#define RK3568_SR_PINS_PER_REG		16
 
-static int rockchip_get_drive_perpin(struct rockchip_pin_bank *bank,
-				     int pin_num)
+static int rk3568_calc_slew_rate_reg_and_bit(struct rockchip_pin_bank *bank,
+					     int pin_num,
+					     struct regmap **regmap,
+					     int *reg, u8 *bit)
 {
 	struct rockchip_pinctrl *info = bank->drvdata;
-	struct rockchip_pin_ctrl *ctrl = info->ctrl;
-	struct regmap *regmap;
-	int reg, ret;
-	u32 data, temp, rmask_bits;
-	u8 bit;
-	int drv_type = bank->drv[pin_num / 8].drv_type;
-
-	ctrl->drv_calc_reg(bank, pin_num, &regmap, &reg, &bit);
 
-	switch (drv_type) {
-	case DRV_TYPE_IO_1V8_3V0_AUTO:
-	case DRV_TYPE_IO_3V3_ONLY:
-		rmask_bits = RK3399_DRV_3BITS_PER_PIN;
-		switch (bit) {
-		case 0 ... 12:
-			/* regular case, nothing to do */
-			break;
-		case 15:
-			/*
-			 * drive-strength offset is special, as it is
-			 * spread over 2 registers
-			 */
-			ret = regmap_read(regmap, reg, &data);
-			if (ret)
-				return ret;
+	if (bank->bank_num == 0) {
+		*regmap = info->regmap_pmu;
+		*reg = RK3568_SR_PMU_OFFSET;
+	} else {
+		*regmap = info->regmap_base;
+		*reg = RK3568_SR_GRF_OFFSET;
+		*reg += (bank->bank_num  - 1) * RK3568_SR_BANK_STRIDE;
+	}
+	*reg += ((pin_num / RK3568_SR_PINS_PER_REG) * 4);
+	*bit = pin_num % RK3568_SR_PINS_PER_REG;
 
-			ret = regmap_read(regmap, reg + 0x4, &temp);
-			if (ret)
-				return ret;
+	return 0;
+}
 
-			/*
-			 * the bit data[15] contains bit 0 of the value
-			 * while temp[1:0] contains bits 2 and 1
-			 */
-			data >>= 15;
-			temp &= 0x3;
-			temp <<= 1;
-			data |= temp;
+#define RK3568_PULL_PMU_OFFSET		0x20
+#define RK3568_PULL_GRF_OFFSET		0x80
+#define RK3568_PULL_BITS_PER_PIN	2
+#define RK3568_PULL_PINS_PER_REG	8
+#define RK3568_PULL_BANK_STRIDE		0x10
 
-			return rockchip_perpin_drv_list[drv_type][data];
+static void rk3568_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+					 int pin_num, struct regmap **regmap,
+					 int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+
+	if (bank->bank_num == 0) {
+		*regmap = info->regmap_pmu;
+		*reg = RK3568_PULL_PMU_OFFSET;
+		*reg += bank->bank_num * RK3568_PULL_BANK_STRIDE;
+		*reg += ((pin_num / RK3568_PULL_PINS_PER_REG) * 4);
+
+		*bit = pin_num % RK3568_PULL_PINS_PER_REG;
+		*bit *= RK3568_PULL_BITS_PER_PIN;
+	} else {
+		*regmap = info->regmap_base;
+		*reg = RK3568_PULL_GRF_OFFSET;
+		*reg += (bank->bank_num - 1) * RK3568_PULL_BANK_STRIDE;
+		*reg += ((pin_num / RK3568_PULL_PINS_PER_REG) * 4);
+
+		*bit = (pin_num % RK3568_PULL_PINS_PER_REG);
+		*bit *= RK3568_PULL_BITS_PER_PIN;
+	}
+}
+
+#define RK3568_DRV_PMU_OFFSET		0x70
+#define RK3568_DRV_GRF_OFFSET		0x200
+#define RK3568_DRV_BITS_PER_PIN		8
+#define RK3568_DRV_PINS_PER_REG		2
+#define RK3568_DRV_BANK_STRIDE		0x40
+
+static void rk3568_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+					int pin_num, struct regmap **regmap,
+					int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+
+	/* The first 32 pins of the first bank are located in PMU */
+	if (bank->bank_num == 0) {
+		*regmap = info->regmap_pmu;
+		*reg = RK3568_DRV_PMU_OFFSET;
+		*reg += ((pin_num / RK3568_DRV_PINS_PER_REG) * 4);
+
+		*bit = pin_num % RK3568_DRV_PINS_PER_REG;
+		*bit *= RK3568_DRV_BITS_PER_PIN;
+	} else {
+		*regmap = info->regmap_base;
+		*reg = RK3568_DRV_GRF_OFFSET;
+		*reg += (bank->bank_num - 1) * RK3568_DRV_BANK_STRIDE;
+		*reg += ((pin_num / RK3568_DRV_PINS_PER_REG) * 4);
+
+		*bit = (pin_num % RK3568_DRV_PINS_PER_REG);
+		*bit *= RK3568_DRV_BITS_PER_PIN;
+	}
+
+	if (rockchip_get_cpu_version() == 0)
+		if ((bank->bank_num == 1 && (pin_num == 15 || pin_num == 23 || pin_num == 31)) ||
+		    ((bank->bank_num == 2 || bank->bank_num == 3 || bank->bank_num == 4) &&
+		     (pin_num == 7 || pin_num == 15 || pin_num == 23 || pin_num == 31)))
+			*bit -= RK3568_DRV_BITS_PER_PIN;
+}
+
+#define PMU1_IOC_REG		(0x0000)
+#define PMU2_IOC_REG		(0x4000)
+#define BUS_IOC_REG		(0x8000)
+#define VCCIO1_4_IOC_REG	(0x9000)
+#define VCCIO3_5_IOC_REG	(0xA000)
+#define VCCIO2_IOC_REG		(0xB000)
+#define VCCIO6_IOC_REG		(0xC000)
+#define EMMC_IOC_REG		(0xD000)
+
+static const u32 rk3588_ds_regs[][2] = {
+	{RK_GPIO0_A0, PMU1_IOC_REG + 0x0010},
+	{RK_GPIO0_A4, PMU1_IOC_REG + 0x0014},
+	{RK_GPIO0_B0, PMU1_IOC_REG + 0x0018},
+	{RK_GPIO0_B4, PMU2_IOC_REG + 0x0014},
+/*	{RK_GPIO0_C0, PMU2_IOC_REG + 0x0018},
+	{RK_GPIO0_C4, PMU2_IOC_REG + 0x001C},
+	{RK_GPIO0_D0, PMU2_IOC_REG + 0x0020},
+	{RK_GPIO0_D4, PMU2_IOC_REG + 0x0024},*/
+	{RK_GPIO1_A0, VCCIO1_4_IOC_REG + 0x0020},
+/*	{RK_GPIO1_A4, VCCIO1_4_IOC_REG + 0x0024},
+	{RK_GPIO1_B0, VCCIO1_4_IOC_REG + 0x0028},
+	{RK_GPIO1_B4, VCCIO1_4_IOC_REG + 0x002C},
+	{RK_GPIO1_C0, VCCIO1_4_IOC_REG + 0x0030},
+	{RK_GPIO1_C4, VCCIO1_4_IOC_REG + 0x0034},
+	{RK_GPIO1_D0, VCCIO1_4_IOC_REG + 0x0038},
+	{RK_GPIO1_D4, VCCIO1_4_IOC_REG + 0x003C},*/
+	{RK_GPIO2_A0, EMMC_IOC_REG + 0x0040},
+/*	{RK_GPIO2_A4, EMMC_IOC_REG + 0x0044},
+	{RK_GPIO2_B0, EMMC_IOC_REG + 0x0048},
+	{RK_GPIO2_B4, EMMC_IOC_REG + 0x004C},
+	{RK_GPIO2_C0, EMMC_IOC_REG + 0x0050},
+	{RK_GPIO2_C4, EMMC_IOC_REG + 0x0054},
+	{RK_GPIO2_D0, EMMC_IOC_REG + 0x0058},
+	{RK_GPIO2_D4, EMMC_IOC_REG + 0x005C},*/
+	{RK_GPIO3_A0, VCCIO3_5_IOC_REG + 0x0060},
+/*	{RK_GPIO3_A4, VCCIO3_5_IOC_REG + 0x0064},
+	{RK_GPIO3_B0, VCCIO3_5_IOC_REG + 0x0068},
+	{RK_GPIO3_B4, VCCIO3_5_IOC_REG + 0x006C},
+	{RK_GPIO3_C0, VCCIO3_5_IOC_REG + 0x0070},
+	{RK_GPIO3_C4, VCCIO3_5_IOC_REG + 0x0074},
+	{RK_GPIO3_D0, VCCIO3_5_IOC_REG + 0x0078},
+	{RK_GPIO3_D4, VCCIO3_5_IOC_REG + 0x007C},*/
+	{RK_GPIO4_A0, VCCIO6_IOC_REG + 0x0080},
+/*	{RK_GPIO4_A4, VCCIO6_IOC_REG + 0x0084},
+	{RK_GPIO4_B0, VCCIO6_IOC_REG + 0x0088},
+	{RK_GPIO4_B4, VCCIO6_IOC_REG + 0x008C},
+	{RK_GPIO4_C0, VCCIO6_IOC_REG + 0x0090},*/
+	{RK_GPIO4_C2, VCCIO3_5_IOC_REG + 0x0090},
+	{RK_GPIO4_D0, VCCIO2_IOC_REG + 0x0098},
+};
+
+static const u32 rk3588_p_regs[][2] = {
+	{RK_GPIO0_A0, PMU1_IOC_REG + 0x0020},
+	{RK_GPIO0_B0, PMU1_IOC_REG + 0x0024},
+	{RK_GPIO0_B5, PMU2_IOC_REG + 0x0028},
+	{RK_GPIO0_C0, PMU2_IOC_REG + 0x002C},
+	{RK_GPIO0_D0, PMU2_IOC_REG + 0x0030},
+	{RK_GPIO1_A0, VCCIO1_4_IOC_REG + 0x0110},
+/*	{RK_GPIO1_B0, VCCIO1_4_IOC_REG + 0x0114},
+	{RK_GPIO1_C0, VCCIO1_4_IOC_REG + 0x0118},
+	{RK_GPIO1_D0, VCCIO1_4_IOC_REG + 0x011C},*/
+	{RK_GPIO2_A0, EMMC_IOC_REG + 0x0120},
+/*	{RK_GPIO2_D0, EMMC_IOC_REG + 0x012C},*/
+	{RK_GPIO3_A0, VCCIO3_5_IOC_REG + 0x0130},
+/*	{RK_GPIO3_B0, VCCIO3_5_IOC_REG + 0x0134},
+	{RK_GPIO3_C0, VCCIO3_5_IOC_REG + 0x0138},
+	{RK_GPIO3_D0, VCCIO3_5_IOC_REG + 0x013C},*/
+	{RK_GPIO4_A0, VCCIO6_IOC_REG + 0x0140},
+/*	{RK_GPIO4_B0, VCCIO6_IOC_REG + 0x0144},
+	{RK_GPIO4_C0, VCCIO6_IOC_REG + 0x0148},*/
+	{RK_GPIO4_C2, VCCIO3_5_IOC_REG + 0x0148},
+	{RK_GPIO4_D0, VCCIO2_IOC_REG + 0x014C},
+};
+
+static const u32 rk3588_smt_regs[][2] = {
+	{RK_GPIO0_A0, PMU1_IOC_REG + 0x0030},
+	{RK_GPIO0_B0, PMU1_IOC_REG + 0x0034},
+	{RK_GPIO0_B5, PMU2_IOC_REG + 0x0040},
+	{RK_GPIO0_C0, PMU2_IOC_REG + 0x0044},
+	{RK_GPIO0_D0, PMU2_IOC_REG + 0x0048},
+	{RK_GPIO1_A0, VCCIO1_4_IOC_REG + 0x0210},
+/*	{RK_GPIO1_B0, VCCIO1_4_IOC_REG + 0x0214},
+	{RK_GPIO1_C0, VCCIO1_4_IOC_REG + 0x0218},
+	{RK_GPIO1_D0, VCCIO1_4_IOC_REG + 0x021C},*/
+	{RK_GPIO2_A0, EMMC_IOC_REG + 0x0220},
+	{RK_GPIO2_D0, EMMC_IOC_REG + 0x022C},
+	{RK_GPIO3_A0, VCCIO3_5_IOC_REG + 0x0230},
+/*	{RK_GPIO3_B0, VCCIO3_5_IOC_REG + 0x0234},
+	{RK_GPIO3_C0, VCCIO3_5_IOC_REG + 0x0238},
+	{RK_GPIO3_D0, VCCIO3_5_IOC_REG + 0x023C},*/
+	{RK_GPIO4_A0, VCCIO6_IOC_REG + 0x0240},
+/*	{RK_GPIO4_B0, VCCIO6_IOC_REG + 0x0244},
+	{RK_GPIO4_C0, VCCIO6_IOC_REG + 0x0248},*/
+	{RK_GPIO4_C2, VCCIO3_5_IOC_REG + 0x0248},
+	{RK_GPIO4_D0, VCCIO2_IOC_REG + 0x024C},
+};
+
+#define RK3588_PULL_BITS_PER_PIN		2
+#define RK3588_PULL_PINS_PER_REG		8
+
+static void rk3588_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+					 int pin_num, struct regmap **regmap,
+					 int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+	u8 bank_num = bank->bank_num;
+	u32 pin = bank_num * 32 + pin_num;
+	int i;
+
+	for (i = ARRAY_SIZE(rk3588_p_regs) - 1; i >= 0; i--) {
+		if (pin >= rk3588_p_regs[i][0]) {
+			*reg = rk3588_p_regs[i][1];
+			break;
+		}
+		BUG_ON(i == 0);
+	}
+
+	*regmap = info->regmap_base;
+	*reg += ((pin - rk3588_p_regs[i][0]) / RK3588_PULL_PINS_PER_REG) * 4;
+	*bit = pin_num % RK3588_PULL_PINS_PER_REG;
+	*bit *= RK3588_PULL_BITS_PER_PIN;
+}
+
+#define RK3588_DRV_BITS_PER_PIN		4
+#define RK3588_DRV_PINS_PER_REG		4
+
+static void rk3588_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+					int pin_num, struct regmap **regmap,
+					int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+	u8 bank_num = bank->bank_num;
+	u32 pin = bank_num * 32 + pin_num;
+	int i;
+
+	for (i = ARRAY_SIZE(rk3588_ds_regs) - 1; i >= 0; i--) {
+		if (pin >= rk3588_ds_regs[i][0]) {
+			*reg = rk3588_ds_regs[i][1];
+			break;
+		}
+		BUG_ON(i == 0);
+	}
+
+	*regmap = info->regmap_base;
+	*reg += ((pin - rk3588_ds_regs[i][0]) / RK3588_DRV_PINS_PER_REG) * 4;
+	*bit = pin_num % RK3588_DRV_PINS_PER_REG;
+	*bit *= RK3588_DRV_BITS_PER_PIN;
+}
+
+#define RK3588_SMT_BITS_PER_PIN		1
+#define RK3588_SMT_PINS_PER_REG		8
+
+static int rk3588_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank,
+					   int pin_num,
+					   struct regmap **regmap,
+					   int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+	u8 bank_num = bank->bank_num;
+	u32 pin = bank_num * 32 + pin_num;
+	int i;
+
+	for (i = ARRAY_SIZE(rk3588_smt_regs) - 1; i >= 0; i--) {
+		if (pin >= rk3588_smt_regs[i][0]) {
+			*reg = rk3588_smt_regs[i][1];
+			break;
+		}
+		BUG_ON(i == 0);
+	}
+
+	*regmap = info->regmap_base;
+	*reg += ((pin - rk3588_smt_regs[i][0]) / RK3588_SMT_PINS_PER_REG) * 4;
+	*bit = pin_num % RK3588_SMT_PINS_PER_REG;
+	*bit *= RK3588_SMT_BITS_PER_PIN;
+
+	return 0;
+}
+
+static int rockchip_perpin_drv_list[DRV_TYPE_MAX][8] = {
+	{ 2, 4, 8, 12, -1, -1, -1, -1 },
+	{ 3, 6, 9, 12, -1, -1, -1, -1 },
+	{ 5, 10, 15, 20, -1, -1, -1, -1 },
+	{ 4, 6, 8, 10, 12, 14, 16, 18 },
+	{ 4, 7, 10, 13, 16, 19, 22, 26 }
+};
+
+static int rockchip_get_drive_perpin(struct rockchip_pin_bank *bank,
+				     int pin_num)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+	struct rockchip_pin_ctrl *ctrl = info->ctrl;
+	struct regmap *regmap;
+	int reg, ret;
+	u32 data, temp, rmask_bits;
+	u8 bit;
+	int drv_type = bank->drv[pin_num / 8].drv_type;
+
+	ctrl->drv_calc_reg(bank, pin_num, &regmap, &reg, &bit);
+
+	switch (drv_type) {
+	case DRV_TYPE_IO_1V8_3V0_AUTO:
+	case DRV_TYPE_IO_3V3_ONLY:
+		rmask_bits = RK3399_DRV_3BITS_PER_PIN;
+		switch (bit) {
+		case 0 ... 12:
+			/* regular case, nothing to do */
+			break;
+		case 15:
+			/*
+			 * drive-strength offset is special, as it is
+			 * spread over 2 registers
+			 */
+			ret = regmap_read(regmap, reg, &data);
+			if (ret)
+				return ret;
+
+			ret = regmap_read(regmap, reg + 0x4, &temp);
+			if (ret)
+				return ret;
+
+			/*
+			 * the bit data[15] contains bit 0 of the value
+			 * while temp[1:0] contains bits 2 and 1
+			 */
+			data >>= 15;
+			temp &= 0x3;
+			temp <<= 1;
+			data |= temp;
+
+			return rockchip_perpin_drv_list[drv_type][data];
 		case 18 ... 21:
 			/* setting fully enclosed in the second register */
 			reg += 4;
@@ -2217,6 +2401,15 @@ static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
 		bank->bank_num, pin_num, strength);
 
 	ctrl->drv_calc_reg(bank, pin_num, &regmap, &reg, &bit);
+	if (ctrl->type == RV1126 || ctrl->type == RK3588) {
+		rmask_bits = RV1126_DRV_BITS_PER_PIN;
+		ret = strength;
+		goto config;
+	} else if (ctrl->type == RK3568) {
+		rmask_bits = RK3568_DRV_BITS_PER_PIN;
+		ret = (1 << (strength + 1)) - 1;
+		goto config;
+	}
 
 	ret = -EINVAL;
 	for (i = 0; i < ARRAY_SIZE(rockchip_perpin_drv_list[drv_type]); i++) {
@@ -2286,14 +2479,42 @@ static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
 		return -EINVAL;
 	}
 
+config:
 	/* enable the write to the equivalent lower bits */
 	data = ((1 << rmask_bits) - 1) << (bit + 16);
 	rmask = data | (data >> 16);
 	data |= (ret << bit);
 
 	ret = regmap_update_bits(regmap, reg, rmask, data);
+	if (ret)
+		return ret;
 
-	return ret;
+	if (ctrl->type == RK3568 && rockchip_get_cpu_version() == 0) {
+		if (bank->bank_num == 1 && pin_num == 21)
+			reg = 0x0840;
+		else if (bank->bank_num == 2 && pin_num == 2)
+			reg = 0x0844;
+		else if (bank->bank_num == 2 && pin_num == 8)
+			reg = 0x0848;
+		else if (bank->bank_num == 3 && pin_num == 0)
+			reg = 0x084c;
+		else if (bank->bank_num == 3 && pin_num == 6)
+			reg = 0x0850;
+		else if (bank->bank_num == 4 && pin_num == 0)
+			reg = 0x0854;
+		else
+			return 0;
+
+		data = ((1 << rmask_bits) - 1) << 16;
+		rmask = data | (data >> 16);
+		data |= (1 << (strength + 1)) - 1;
+
+		ret = regmap_update_bits(regmap, reg, rmask, data);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
 }
 
 static int rockchip_pull_list[PULL_TYPE_MAX][4] = {
@@ -2338,11 +2559,15 @@ static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num)
 				: PIN_CONFIG_BIAS_DISABLE;
 	case PX30:
 	case RV1108:
+	case RV1126:
+	case RK1808:
 	case RK3188:
 	case RK3288:
 	case RK3308:
 	case RK3368:
 	case RK3399:
+	case RK3568:
+	case RK3588:
 		pull_type = bank->pull_type[pin_num / 8];
 		data >>= bit;
 		data &= (1 << RK3188_PULL_BITS_PER_PIN) - 1;
@@ -2383,11 +2608,15 @@ static int rockchip_set_pull(struct rockchip_pin_bank *bank,
 		break;
 	case PX30:
 	case RV1108:
+	case RV1126:
+	case RK1808:
 	case RK3188:
 	case RK3288:
 	case RK3308:
 	case RK3368:
 	case RK3399:
+	case RK3568:
+	case RK3588:
 		pull_type = bank->pull_type[pin_num / 8];
 		ret = -EINVAL;
 		for (i = 0; i < ARRAY_SIZE(rockchip_pull_list[pull_type]);
@@ -2397,6 +2626,14 @@ static int rockchip_set_pull(struct rockchip_pin_bank *bank,
 				break;
 			}
 		}
+		/*
+		 * In the TRM, pull-up being 1 for everything except the GPIO0_D0-D6,
+		 * where that pull up value becomes 3.
+		 */
+		if (ctrl->type == RK3568 && bank->bank_num == 0 && pin_num >= 27 && pin_num <= 30) {
+			if (ret == 1)
+				ret = 3;
+		}
 
 		if (ret < 0) {
 			dev_err(info->dev, "unsupported pull setting %d\n",
@@ -2441,6 +2678,35 @@ static int rk3328_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank,
 	return 0;
 }
 
+#define RK3568_SCHMITT_BITS_PER_PIN		2
+#define RK3568_SCHMITT_PINS_PER_REG		8
+#define RK3568_SCHMITT_BANK_STRIDE		0x10
+#define RK3568_SCHMITT_GRF_OFFSET		0xc0
+#define RK3568_SCHMITT_PMUGRF_OFFSET		0x30
+
+static int rk3568_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank,
+					   int pin_num,
+					   struct regmap **regmap,
+					   int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+
+	if (bank->bank_num == 0) {
+		*regmap = info->regmap_pmu;
+		*reg = RK3568_SCHMITT_PMUGRF_OFFSET;
+	} else {
+		*regmap = info->regmap_base;
+		*reg = RK3568_SCHMITT_GRF_OFFSET;
+		*reg += (bank->bank_num - 1) * RK3568_SCHMITT_BANK_STRIDE;
+	}
+
+	*reg += ((pin_num / RK3568_SCHMITT_PINS_PER_REG) * 4);
+	*bit = pin_num % RK3568_SCHMITT_PINS_PER_REG;
+	*bit *= RK3568_SCHMITT_BITS_PER_PIN;
+
+	return 0;
+}
+
 static int rockchip_get_schmitt(struct rockchip_pin_bank *bank, int pin_num)
 {
 	struct rockchip_pinctrl *info = bank->drvdata;
@@ -2459,6 +2725,13 @@ static int rockchip_get_schmitt(struct rockchip_pin_bank *bank, int pin_num)
 		return ret;
 
 	data >>= bit;
+	switch (ctrl->type) {
+	case RK3568:
+		return data & ((1 << RK3568_SCHMITT_BITS_PER_PIN) - 1);
+	default:
+		break;
+	}
+
 	return data & 0x1;
 }
 
@@ -2480,7 +2753,91 @@ static int rockchip_set_schmitt(struct rockchip_pin_bank *bank,
 		return ret;
 
 	/* enable the write to the equivalent lower bits */
-	data = BIT(bit + 16) | (enable << bit);
+	switch (ctrl->type) {
+	case RK3568:
+		data = ((1 << RK3568_SCHMITT_BITS_PER_PIN) - 1) << (bit + 16);
+		rmask = data | (data >> 16);
+		data |= ((enable ? 0x2 : 0x1) << bit);
+		break;
+	default:
+		data = BIT(bit + 16) | (enable << bit);
+		rmask = BIT(bit + 16) | BIT(bit);
+		break;
+	}
+
+	return regmap_update_bits(regmap, reg, rmask, data);
+}
+
+#define PX30_SLEW_RATE_PMU_OFFSET		0x30
+#define PX30_SLEW_RATE_GRF_OFFSET		0x90
+#define PX30_SLEW_RATE_PINS_PER_PMU_REG		16
+#define PX30_SLEW_RATE_BANK_STRIDE		16
+#define PX30_SLEW_RATE_PINS_PER_GRF_REG		8
+
+static int px30_calc_slew_rate_reg_and_bit(struct rockchip_pin_bank *bank,
+					   int pin_num,
+					   struct regmap **regmap,
+					   int *reg, u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+	int pins_per_reg;
+
+	if (bank->bank_num == 0) {
+		*regmap = info->regmap_pmu;
+		*reg = PX30_SLEW_RATE_PMU_OFFSET;
+		pins_per_reg = PX30_SLEW_RATE_PINS_PER_PMU_REG;
+	} else {
+		*regmap = info->regmap_base;
+		*reg = PX30_SLEW_RATE_GRF_OFFSET;
+		pins_per_reg = PX30_SLEW_RATE_PINS_PER_GRF_REG;
+		*reg += (bank->bank_num  - 1) * PX30_SLEW_RATE_BANK_STRIDE;
+	}
+	*reg += ((pin_num / pins_per_reg) * 4);
+	*bit = pin_num % pins_per_reg;
+
+	return 0;
+}
+
+static int rockchip_get_slew_rate(struct rockchip_pin_bank *bank, int pin_num)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+	struct rockchip_pin_ctrl *ctrl = info->ctrl;
+	struct regmap *regmap;
+	int reg, ret;
+	u8 bit;
+	u32 data;
+
+	ret = ctrl->slew_rate_calc_reg(bank, pin_num, &regmap, &reg, &bit);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(regmap, reg, &data);
+	if (ret)
+		return ret;
+
+	data >>= bit;
+	return data & 0x1;
+}
+
+static int rockchip_set_slew_rate(struct rockchip_pin_bank *bank,
+				  int pin_num, int speed)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+	struct rockchip_pin_ctrl *ctrl = info->ctrl;
+	struct regmap *regmap;
+	int reg, ret;
+	u8 bit;
+	u32 data, rmask;
+
+	dev_dbg(info->dev, "setting slew rate of GPIO%d-%d to %d\n",
+		bank->bank_num, pin_num, speed);
+
+	ret = ctrl->slew_rate_calc_reg(bank, pin_num, &regmap, &reg, &bit);
+	if (ret)
+		return ret;
+
+	/* enable the write to the equivalent lower bits */
+	data = BIT(bit + 16) | (speed << bit);
 	rmask = BIT(bit + 16) | BIT(bit);
 
 	return regmap_update_bits(regmap, reg, rmask, data);
@@ -2541,9 +2898,9 @@ static int rockchip_pmx_set(struct pinctrl_dev *pctldev, unsigned selector,
 			break;
 	}
 
-	if (ret) {
+	if (ret && cnt) {
 		/* revert the already done pin settings */
-		for (cnt--; cnt >= 0; cnt--)
+		for (cnt--; cnt >= 0 && !data[cnt].func; cnt--)
 			rockchip_set_mux(bank, pins[cnt] - bank->pin_base, 0);
 
 		return ret;
@@ -2552,90 +2909,15 @@ static int rockchip_pmx_set(struct pinctrl_dev *pctldev, unsigned selector,
 	return 0;
 }
 
-static int rockchip_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
-{
-	struct rockchip_pin_bank *bank = gpiochip_get_data(chip);
-	u32 data;
-	int ret;
-
-	ret = clk_enable(bank->clk);
-	if (ret < 0) {
-		dev_err(bank->drvdata->dev,
-			"failed to enable clock for bank %s\n", bank->name);
-		return ret;
-	}
-	data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
-	clk_disable(bank->clk);
-
-	if (data & BIT(offset))
-		return GPIO_LINE_DIRECTION_OUT;
-
-	return GPIO_LINE_DIRECTION_IN;
-}
+static const struct pinmux_ops rockchip_pmx_ops = {
+	.get_functions_count	= rockchip_pmx_get_funcs_count,
+	.get_function_name	= rockchip_pmx_get_func_name,
+	.get_function_groups	= rockchip_pmx_get_groups,
+	.set_mux		= rockchip_pmx_set,
+};
 
 /*
- * The calls to gpio_direction_output() and gpio_direction_input()
- * leads to this function call (via the pinctrl_gpio_direction_{input|output}()
- * function called from the gpiolib interface).
- */
-static int _rockchip_pmx_gpio_set_direction(struct gpio_chip *chip,
-					    int pin, bool input)
-{
-	struct rockchip_pin_bank *bank;
-	int ret;
-	unsigned long flags;
-	u32 data;
-
-	bank = gpiochip_get_data(chip);
-
-	ret = rockchip_set_mux(bank, pin, RK_FUNC_GPIO);
-	if (ret < 0)
-		return ret;
-
-	clk_enable(bank->clk);
-	raw_spin_lock_irqsave(&bank->slock, flags);
-
-	data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
-	/* set bit to 1 for output, 0 for input */
-	if (!input)
-		data |= BIT(pin);
-	else
-		data &= ~BIT(pin);
-	writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR);
-
-	raw_spin_unlock_irqrestore(&bank->slock, flags);
-	clk_disable(bank->clk);
-
-	return 0;
-}
-
-static int rockchip_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
-					      struct pinctrl_gpio_range *range,
-					      unsigned offset, bool input)
-{
-	struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
-	struct gpio_chip *chip;
-	int pin;
-
-	chip = range->gc;
-	pin = offset - chip->base;
-	dev_dbg(info->dev, "gpio_direction for pin %u as %s-%d to %s\n",
-		 offset, range->name, pin, input ? "input" : "output");
-
-	return _rockchip_pmx_gpio_set_direction(chip, offset - chip->base,
-						input);
-}
-
-static const struct pinmux_ops rockchip_pmx_ops = {
-	.get_functions_count	= rockchip_pmx_get_funcs_count,
-	.get_function_name	= rockchip_pmx_get_func_name,
-	.get_function_groups	= rockchip_pmx_get_groups,
-	.set_mux		= rockchip_pmx_set,
-	.gpio_set_direction	= rockchip_pmx_gpio_set_direction,
-};
-
-/*
- * Pinconf_ops handling
+ * Pinconf_ops handling
  */
 
 static bool rockchip_pinconf_pull_valid(struct rockchip_pin_ctrl *ctrl,
@@ -2650,19 +2932,37 @@ static bool rockchip_pinconf_pull_valid(struct rockchip_pin_ctrl *ctrl,
 		return pull ? false : true;
 	case PX30:
 	case RV1108:
+	case RV1126:
+	case RK1808:
 	case RK3188:
 	case RK3288:
 	case RK3308:
 	case RK3368:
 	case RK3399:
+	case RK3568:
+	case RK3588:
 		return (pull != PIN_CONFIG_BIAS_PULL_PIN_DEFAULT);
 	}
 
 	return false;
 }
 
-static void rockchip_gpio_set(struct gpio_chip *gc, unsigned offset, int value);
-static int rockchip_gpio_get(struct gpio_chip *gc, unsigned offset);
+static int rockchip_pinconf_defer_output(struct rockchip_pin_bank *bank,
+					 unsigned int pin, u32 arg)
+{
+	struct rockchip_pin_output_deferred *cfg;
+
+	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+	if (!cfg)
+		return -ENOMEM;
+
+	cfg->pin = pin;
+	cfg->arg = arg;
+
+	list_add_tail(&cfg->head, &bank->deferred_output);
+
+	return 0;
+}
 
 /* set the pin config settings for a specified pin */
 static int rockchip_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
@@ -2670,6 +2970,7 @@ static int rockchip_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
 {
 	struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
 	struct rockchip_pin_bank *bank = pin_to_bank(info, pin);
+	struct gpio_chip *gpio = &bank->gpio_chip;
 	enum pin_config_param param;
 	u32 arg;
 	int i;
@@ -2702,10 +3003,29 @@ static int rockchip_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
 				return rc;
 			break;
 		case PIN_CONFIG_OUTPUT:
-			rockchip_gpio_set(&bank->gpio_chip,
-					  pin - bank->pin_base, arg);
-			rc = _rockchip_pmx_gpio_set_direction(&bank->gpio_chip,
-					  pin - bank->pin_base, false);
+			rc = rockchip_set_mux(bank, pin - bank->pin_base,
+					      RK_FUNC_GPIO);
+			if (rc != RK_FUNC_GPIO)
+				return -EINVAL;
+
+			/*
+			 * Check for gpio driver not being probed yet.
+			 * The lock makes sure that either gpio-probe has completed
+			 * or the gpio driver hasn't probed yet.
+			 */
+			mutex_lock(&bank->deferred_lock);
+			if (!gpio || !gpio->direction_output) {
+				rc = rockchip_pinconf_defer_output(bank, pin - bank->pin_base, arg);
+				mutex_unlock(&bank->deferred_lock);
+				if (rc)
+					return rc;
+
+				break;
+			}
+			mutex_unlock(&bank->deferred_lock);
+
+			rc = gpio->direction_output(gpio, pin - bank->pin_base,
+						    arg);
 			if (rc)
 				return rc;
 			break;
@@ -2728,9 +3048,17 @@ static int rockchip_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
 			if (rc < 0)
 				return rc;
 			break;
+		case PIN_CONFIG_SLEW_RATE:
+			if (!info->ctrl->slew_rate_calc_reg)
+				return -ENOTSUPP;
+
+			rc = rockchip_set_slew_rate(bank,
+						    pin - bank->pin_base, arg);
+			if (rc < 0)
+				return rc;
+			break;
 		default:
 			return -ENOTSUPP;
-			break;
 		}
 	} /* for each config */
 
@@ -2743,6 +3071,7 @@ static int rockchip_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
 {
 	struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev);
 	struct rockchip_pin_bank *bank = pin_to_bank(info, pin);
+	struct gpio_chip *gpio = &bank->gpio_chip;
 	enum pin_config_param param = pinconf_to_config_param(*config);
 	u16 arg;
 	int rc;
@@ -2771,7 +3100,12 @@ static int rockchip_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
 		if (rc != RK_FUNC_GPIO)
 			return -EINVAL;
 
-		rc = rockchip_gpio_get(&bank->gpio_chip, pin - bank->pin_base);
+		if (!gpio || !gpio->get) {
+			arg = 0;
+			break;
+		}
+
+		rc = gpio->get(gpio, pin - bank->pin_base);
 		if (rc < 0)
 			return rc;
 
@@ -2796,11 +3130,20 @@ static int rockchip_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
 		if (rc < 0)
 			return rc;
 
+		arg = rc;
+		break;
+	case PIN_CONFIG_SLEW_RATE:
+		if (!info->ctrl->slew_rate_calc_reg)
+			return -ENOTSUPP;
+
+		rc = rockchip_get_slew_rate(bank, pin - bank->pin_base);
+		if (rc < 0)
+			return rc;
+
 		arg = rc;
 		break;
 	default:
 		return -ENOTSUPP;
-		break;
 	}
 
 	*config = pinconf_to_config_packed(param, arg);
@@ -2880,699 +3223,159 @@ static int rockchip_pinctrl_parse_groups(struct device_node *np,
 
 		num = be32_to_cpu(*list++);
 		bank = bank_num_to_bank(info, num);
-		if (IS_ERR(bank))
-			return PTR_ERR(bank);
-
-		grp->pins[j] = bank->pin_base + be32_to_cpu(*list++);
-		grp->data[j].func = be32_to_cpu(*list++);
-
-		phandle = list++;
-		if (!phandle)
-			return -EINVAL;
-
-		np_config = of_find_node_by_phandle(be32_to_cpup(phandle));
-		ret = pinconf_generic_parse_dt_config(np_config, NULL,
-				&grp->data[j].configs, &grp->data[j].nconfigs);
-		if (ret)
-			return ret;
-	}
-
-	return 0;
-}
-
-static int rockchip_pinctrl_parse_functions(struct device_node *np,
-						struct rockchip_pinctrl *info,
-						u32 index)
-{
-	struct device_node *child;
-	struct rockchip_pmx_func *func;
-	struct rockchip_pin_group *grp;
-	int ret;
-	static u32 grp_index;
-	u32 i = 0;
-
-	dev_dbg(info->dev, "parse function(%d): %pOFn\n", index, np);
-
-	func = &info->functions[index];
-
-	/* Initialise function */
-	func->name = np->name;
-	func->ngroups = of_get_child_count(np);
-	if (func->ngroups <= 0)
-		return 0;
-
-	func->groups = devm_kcalloc(info->dev,
-			func->ngroups, sizeof(char *), GFP_KERNEL);
-	if (!func->groups)
-		return -ENOMEM;
-
-	for_each_child_of_node(np, child) {
-		func->groups[i] = child->name;
-		grp = &info->groups[grp_index++];
-		ret = rockchip_pinctrl_parse_groups(child, grp, info, i++);
-		if (ret) {
-			of_node_put(child);
-			return ret;
-		}
-	}
-
-	return 0;
-}
-
-static int rockchip_pinctrl_parse_dt(struct platform_device *pdev,
-					      struct rockchip_pinctrl *info)
-{
-	struct device *dev = &pdev->dev;
-	struct device_node *np = dev->of_node;
-	struct device_node *child;
-	int ret;
-	int i;
-
-	rockchip_pinctrl_child_count(info, np);
-
-	dev_dbg(&pdev->dev, "nfunctions = %d\n", info->nfunctions);
-	dev_dbg(&pdev->dev, "ngroups = %d\n", info->ngroups);
-
-	info->functions = devm_kcalloc(dev,
-					      info->nfunctions,
-					      sizeof(struct rockchip_pmx_func),
-					      GFP_KERNEL);
-	if (!info->functions)
-		return -ENOMEM;
-
-	info->groups = devm_kcalloc(dev,
-					    info->ngroups,
-					    sizeof(struct rockchip_pin_group),
-					    GFP_KERNEL);
-	if (!info->groups)
-		return -ENOMEM;
-
-	i = 0;
-
-	for_each_child_of_node(np, child) {
-		if (of_match_node(rockchip_bank_match, child))
-			continue;
-
-		ret = rockchip_pinctrl_parse_functions(child, info, i++);
-		if (ret) {
-			dev_err(&pdev->dev, "failed to parse function\n");
-			of_node_put(child);
-			return ret;
-		}
-	}
-
-	return 0;
-}
-
-static int rockchip_pinctrl_register(struct platform_device *pdev,
-					struct rockchip_pinctrl *info)
-{
-	struct pinctrl_desc *ctrldesc = &info->pctl;
-	struct pinctrl_pin_desc *pindesc, *pdesc;
-	struct rockchip_pin_bank *pin_bank;
-	int pin, bank, ret;
-	int k;
-
-	ctrldesc->name = "rockchip-pinctrl";
-	ctrldesc->owner = THIS_MODULE;
-	ctrldesc->pctlops = &rockchip_pctrl_ops;
-	ctrldesc->pmxops = &rockchip_pmx_ops;
-	ctrldesc->confops = &rockchip_pinconf_ops;
-
-	pindesc = devm_kcalloc(&pdev->dev,
-			       info->ctrl->nr_pins, sizeof(*pindesc),
-			       GFP_KERNEL);
-	if (!pindesc)
-		return -ENOMEM;
-
-	ctrldesc->pins = pindesc;
-	ctrldesc->npins = info->ctrl->nr_pins;
-
-	pdesc = pindesc;
-	for (bank = 0 , k = 0; bank < info->ctrl->nr_banks; bank++) {
-		pin_bank = &info->ctrl->pin_banks[bank];
-		for (pin = 0; pin < pin_bank->nr_pins; pin++, k++) {
-			pdesc->number = k;
-			pdesc->name = kasprintf(GFP_KERNEL, "%s-%d",
-						pin_bank->name, pin);
-			pdesc++;
-		}
-	}
-
-	ret = rockchip_pinctrl_parse_dt(pdev, info);
-	if (ret)
-		return ret;
-
-	info->pctl_dev = devm_pinctrl_register(&pdev->dev, ctrldesc, info);
-	if (IS_ERR(info->pctl_dev)) {
-		dev_err(&pdev->dev, "could not register pinctrl driver\n");
-		return PTR_ERR(info->pctl_dev);
-	}
-
-	for (bank = 0; bank < info->ctrl->nr_banks; ++bank) {
-		pin_bank = &info->ctrl->pin_banks[bank];
-		pin_bank->grange.name = pin_bank->name;
-		pin_bank->grange.id = bank;
-		pin_bank->grange.pin_base = pin_bank->pin_base;
-		pin_bank->grange.base = pin_bank->gpio_chip.base;
-		pin_bank->grange.npins = pin_bank->gpio_chip.ngpio;
-		pin_bank->grange.gc = &pin_bank->gpio_chip;
-		pinctrl_add_gpio_range(info->pctl_dev, &pin_bank->grange);
-	}
-
-	return 0;
-}
-
-/*
- * GPIO handling
- */
-
-static void rockchip_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
-{
-	struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
-	void __iomem *reg = bank->reg_base + GPIO_SWPORT_DR;
-	unsigned long flags;
-	u32 data;
-
-	clk_enable(bank->clk);
-	raw_spin_lock_irqsave(&bank->slock, flags);
-
-	data = readl(reg);
-	data &= ~BIT(offset);
-	if (value)
-		data |= BIT(offset);
-	writel(data, reg);
-
-	raw_spin_unlock_irqrestore(&bank->slock, flags);
-	clk_disable(bank->clk);
-}
-
-/*
- * Returns the level of the pin for input direction and setting of the DR
- * register for output gpios.
- */
-static int rockchip_gpio_get(struct gpio_chip *gc, unsigned offset)
-{
-	struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
-	u32 data;
-
-	clk_enable(bank->clk);
-	data = readl(bank->reg_base + GPIO_EXT_PORT);
-	clk_disable(bank->clk);
-	data >>= offset;
-	data &= 1;
-	return data;
-}
-
-/*
- * gpiolib gpio_direction_input callback function. The setting of the pin
- * mux function as 'gpio input' will be handled by the pinctrl subsystem
- * interface.
- */
-static int rockchip_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
-{
-	return pinctrl_gpio_direction_input(gc->base + offset);
-}
-
-/*
- * gpiolib gpio_direction_output callback function. The setting of the pin
- * mux function as 'gpio output' will be handled by the pinctrl subsystem
- * interface.
- */
-static int rockchip_gpio_direction_output(struct gpio_chip *gc,
-					  unsigned offset, int value)
-{
-	rockchip_gpio_set(gc, offset, value);
-	return pinctrl_gpio_direction_output(gc->base + offset);
-}
-
-static void rockchip_gpio_set_debounce(struct gpio_chip *gc,
-				       unsigned int offset, bool enable)
-{
-	struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
-	void __iomem *reg = bank->reg_base + GPIO_DEBOUNCE;
-	unsigned long flags;
-	u32 data;
-
-	clk_enable(bank->clk);
-	raw_spin_lock_irqsave(&bank->slock, flags);
-
-	data = readl(reg);
-	if (enable)
-		data |= BIT(offset);
-	else
-		data &= ~BIT(offset);
-	writel(data, reg);
-
-	raw_spin_unlock_irqrestore(&bank->slock, flags);
-	clk_disable(bank->clk);
-}
-
-/*
- * gpiolib set_config callback function. The setting of the pin
- * mux function as 'gpio output' will be handled by the pinctrl subsystem
- * interface.
- */
-static int rockchip_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
-				  unsigned long config)
-{
-	enum pin_config_param param = pinconf_to_config_param(config);
-
-	switch (param) {
-	case PIN_CONFIG_INPUT_DEBOUNCE:
-		rockchip_gpio_set_debounce(gc, offset, true);
-		/*
-		 * Rockchip's gpio could only support up to one period
-		 * of the debounce clock(pclk), which is far away from
-		 * satisftying the requirement, as pclk is usually near
-		 * 100MHz shared by all peripherals. So the fact is it
-		 * has crippled debounce capability could only be useful
-		 * to prevent any spurious glitches from waking up the system
-		 * if the gpio is conguired as wakeup interrupt source. Let's
-		 * still return -ENOTSUPP as before, to make sure the caller
-		 * of gpiod_set_debounce won't change its behaviour.
-		 */
-		return -ENOTSUPP;
-	default:
-		return -ENOTSUPP;
-	}
-}
-
-/*
- * gpiolib gpio_to_irq callback function. Creates a mapping between a GPIO pin
- * and a virtual IRQ, if not already present.
- */
-static int rockchip_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
-{
-	struct rockchip_pin_bank *bank = gpiochip_get_data(gc);
-	unsigned int virq;
-
-	if (!bank->domain)
-		return -ENXIO;
-
-	clk_enable(bank->clk);
-	virq = irq_create_mapping(bank->domain, offset);
-	clk_disable(bank->clk);
-
-	return (virq) ? : -ENXIO;
-}
-
-static const struct gpio_chip rockchip_gpiolib_chip = {
-	.request = gpiochip_generic_request,
-	.free = gpiochip_generic_free,
-	.set = rockchip_gpio_set,
-	.get = rockchip_gpio_get,
-	.get_direction	= rockchip_gpio_get_direction,
-	.direction_input = rockchip_gpio_direction_input,
-	.direction_output = rockchip_gpio_direction_output,
-	.set_config = rockchip_gpio_set_config,
-	.to_irq = rockchip_gpio_to_irq,
-	.owner = THIS_MODULE,
-};
-
-/*
- * Interrupt handling
- */
-
-static void rockchip_irq_demux(struct irq_desc *desc)
-{
-	struct irq_chip *chip = irq_desc_get_chip(desc);
-	struct rockchip_pin_bank *bank = irq_desc_get_handler_data(desc);
-	u32 pend;
-
-	dev_dbg(bank->drvdata->dev, "got irq for bank %s\n", bank->name);
-
-	chained_irq_enter(chip, desc);
-
-	pend = readl_relaxed(bank->reg_base + GPIO_INT_STATUS);
-
-	while (pend) {
-		unsigned int irq, virq;
-
-		irq = __ffs(pend);
-		pend &= ~BIT(irq);
-		virq = irq_find_mapping(bank->domain, irq);
-
-		if (!virq) {
-			dev_err(bank->drvdata->dev, "unmapped irq %d\n", irq);
-			continue;
-		}
-
-		dev_dbg(bank->drvdata->dev, "handling irq %d\n", irq);
-
-		/*
-		 * Triggering IRQ on both rising and falling edge
-		 * needs manual intervention.
-		 */
-		if (bank->toggle_edge_mode & BIT(irq)) {
-			u32 data, data_old, polarity;
-			unsigned long flags;
-
-			data = readl_relaxed(bank->reg_base + GPIO_EXT_PORT);
-			do {
-				raw_spin_lock_irqsave(&bank->slock, flags);
-
-				polarity = readl_relaxed(bank->reg_base +
-							 GPIO_INT_POLARITY);
-				if (data & BIT(irq))
-					polarity &= ~BIT(irq);
-				else
-					polarity |= BIT(irq);
-				writel(polarity,
-				       bank->reg_base + GPIO_INT_POLARITY);
-
-				raw_spin_unlock_irqrestore(&bank->slock, flags);
-
-				data_old = data;
-				data = readl_relaxed(bank->reg_base +
-						     GPIO_EXT_PORT);
-			} while ((data & BIT(irq)) != (data_old & BIT(irq)));
-		}
-
-		generic_handle_irq(virq);
-	}
-
-	chained_irq_exit(chip, desc);
-}
-
-static int rockchip_irq_set_type(struct irq_data *d, unsigned int type)
-{
-	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
-	struct rockchip_pin_bank *bank = gc->private;
-	u32 mask = BIT(d->hwirq);
-	u32 polarity;
-	u32 level;
-	u32 data;
-	unsigned long flags;
-	int ret;
-
-	/* make sure the pin is configured as gpio input */
-	ret = rockchip_set_mux(bank, d->hwirq, RK_FUNC_GPIO);
-	if (ret < 0)
-		return ret;
-
-	clk_enable(bank->clk);
-	raw_spin_lock_irqsave(&bank->slock, flags);
-
-	data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR);
-	data &= ~mask;
-	writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR);
-
-	raw_spin_unlock_irqrestore(&bank->slock, flags);
-
-	if (type & IRQ_TYPE_EDGE_BOTH)
-		irq_set_handler_locked(d, handle_edge_irq);
-	else
-		irq_set_handler_locked(d, handle_level_irq);
-
-	raw_spin_lock_irqsave(&bank->slock, flags);
-	irq_gc_lock(gc);
-
-	level = readl_relaxed(gc->reg_base + GPIO_INTTYPE_LEVEL);
-	polarity = readl_relaxed(gc->reg_base + GPIO_INT_POLARITY);
-
-	switch (type) {
-	case IRQ_TYPE_EDGE_BOTH:
-		bank->toggle_edge_mode |= mask;
-		level |= mask;
-
-		/*
-		 * Determine gpio state. If 1 next interrupt should be falling
-		 * otherwise rising.
-		 */
-		data = readl(bank->reg_base + GPIO_EXT_PORT);
-		if (data & mask)
-			polarity &= ~mask;
-		else
-			polarity |= mask;
-		break;
-	case IRQ_TYPE_EDGE_RISING:
-		bank->toggle_edge_mode &= ~mask;
-		level |= mask;
-		polarity |= mask;
-		break;
-	case IRQ_TYPE_EDGE_FALLING:
-		bank->toggle_edge_mode &= ~mask;
-		level |= mask;
-		polarity &= ~mask;
-		break;
-	case IRQ_TYPE_LEVEL_HIGH:
-		bank->toggle_edge_mode &= ~mask;
-		level &= ~mask;
-		polarity |= mask;
-		break;
-	case IRQ_TYPE_LEVEL_LOW:
-		bank->toggle_edge_mode &= ~mask;
-		level &= ~mask;
-		polarity &= ~mask;
-		break;
-	default:
-		irq_gc_unlock(gc);
-		raw_spin_unlock_irqrestore(&bank->slock, flags);
-		clk_disable(bank->clk);
-		return -EINVAL;
-	}
-
-	writel_relaxed(level, gc->reg_base + GPIO_INTTYPE_LEVEL);
-	writel_relaxed(polarity, gc->reg_base + GPIO_INT_POLARITY);
-
-	irq_gc_unlock(gc);
-	raw_spin_unlock_irqrestore(&bank->slock, flags);
-	clk_disable(bank->clk);
-
-	return 0;
-}
-
-static void rockchip_irq_suspend(struct irq_data *d)
-{
-	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
-	struct rockchip_pin_bank *bank = gc->private;
-
-	clk_enable(bank->clk);
-	bank->saved_masks = irq_reg_readl(gc, GPIO_INTMASK);
-	irq_reg_writel(gc, ~gc->wake_active, GPIO_INTMASK);
-	clk_disable(bank->clk);
-}
-
-static void rockchip_irq_resume(struct irq_data *d)
-{
-	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
-	struct rockchip_pin_bank *bank = gc->private;
-
-	clk_enable(bank->clk);
-	irq_reg_writel(gc, bank->saved_masks, GPIO_INTMASK);
-	clk_disable(bank->clk);
-}
+		if (IS_ERR(bank))
+			return PTR_ERR(bank);
 
-static void rockchip_irq_enable(struct irq_data *d)
-{
-	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
-	struct rockchip_pin_bank *bank = gc->private;
+		grp->pins[j] = bank->pin_base + be32_to_cpu(*list++);
+		grp->data[j].func = be32_to_cpu(*list++);
 
-	clk_enable(bank->clk);
-	irq_gc_mask_clr_bit(d);
-}
+		phandle = list++;
+		if (!phandle)
+			return -EINVAL;
 
-static void rockchip_irq_disable(struct irq_data *d)
-{
-	struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
-	struct rockchip_pin_bank *bank = gc->private;
+		np_config = of_find_node_by_phandle(be32_to_cpup(phandle));
+		ret = pinconf_generic_parse_dt_config(np_config, NULL,
+				&grp->data[j].configs, &grp->data[j].nconfigs);
+		if (ret)
+			return ret;
+	}
 
-	irq_gc_mask_set_bit(d);
-	clk_disable(bank->clk);
+	return 0;
 }
 
-static int rockchip_interrupts_register(struct platform_device *pdev,
-						struct rockchip_pinctrl *info)
+static int rockchip_pinctrl_parse_functions(struct device_node *np,
+						struct rockchip_pinctrl *info,
+						u32 index)
 {
-	struct rockchip_pin_ctrl *ctrl = info->ctrl;
-	struct rockchip_pin_bank *bank = ctrl->pin_banks;
-	unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
-	struct irq_chip_generic *gc;
+	struct device_node *child;
+	struct rockchip_pmx_func *func;
+	struct rockchip_pin_group *grp;
 	int ret;
-	int i;
+	static u32 grp_index;
+	u32 i = 0;
 
-	for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
-		if (!bank->valid) {
-			dev_warn(&pdev->dev, "bank %s is not valid\n",
-				 bank->name);
-			continue;
-		}
+	dev_dbg(info->dev, "parse function(%d): %pOFn\n", index, np);
 
-		ret = clk_enable(bank->clk);
-		if (ret) {
-			dev_err(&pdev->dev, "failed to enable clock for bank %s\n",
-				bank->name);
-			continue;
-		}
+	func = &info->functions[index];
 
-		bank->domain = irq_domain_add_linear(bank->of_node, 32,
-						&irq_generic_chip_ops, NULL);
-		if (!bank->domain) {
-			dev_warn(&pdev->dev, "could not initialize irq domain for bank %s\n",
-				 bank->name);
-			clk_disable(bank->clk);
-			continue;
-		}
+	/* Initialise function */
+	func->name = np->name;
+	func->ngroups = of_get_child_count(np);
+	if (func->ngroups <= 0)
+		return 0;
+
+	func->groups = devm_kcalloc(info->dev,
+			func->ngroups, sizeof(char *), GFP_KERNEL);
+	if (!func->groups)
+		return -ENOMEM;
 
-		ret = irq_alloc_domain_generic_chips(bank->domain, 32, 1,
-					 "rockchip_gpio_irq", handle_level_irq,
-					 clr, 0, 0);
+	for_each_child_of_node(np, child) {
+		func->groups[i] = child->name;
+		grp = &info->groups[grp_index++];
+		ret = rockchip_pinctrl_parse_groups(child, grp, info, i++);
 		if (ret) {
-			dev_err(&pdev->dev, "could not alloc generic chips for bank %s\n",
-				bank->name);
-			irq_domain_remove(bank->domain);
-			clk_disable(bank->clk);
-			continue;
+			of_node_put(child);
+			return ret;
 		}
-
-		gc = irq_get_domain_generic_chip(bank->domain, 0);
-		gc->reg_base = bank->reg_base;
-		gc->private = bank;
-		gc->chip_types[0].regs.mask = GPIO_INTMASK;
-		gc->chip_types[0].regs.ack = GPIO_PORTS_EOI;
-		gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
-		gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit;
-		gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit;
-		gc->chip_types[0].chip.irq_enable = rockchip_irq_enable;
-		gc->chip_types[0].chip.irq_disable = rockchip_irq_disable;
-		gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake;
-		gc->chip_types[0].chip.irq_suspend = rockchip_irq_suspend;
-		gc->chip_types[0].chip.irq_resume = rockchip_irq_resume;
-		gc->chip_types[0].chip.irq_set_type = rockchip_irq_set_type;
-		gc->wake_enabled = IRQ_MSK(bank->nr_pins);
-
-		/*
-		 * Linux assumes that all interrupts start out disabled/masked.
-		 * Our driver only uses the concept of masked and always keeps
-		 * things enabled, so for us that's all masked and all enabled.
-		 */
-		writel_relaxed(0xffffffff, bank->reg_base + GPIO_INTMASK);
-		writel_relaxed(0xffffffff, bank->reg_base + GPIO_INTEN);
-		gc->mask_cache = 0xffffffff;
-
-		irq_set_chained_handler_and_data(bank->irq,
-						 rockchip_irq_demux, bank);
-		clk_disable(bank->clk);
 	}
 
 	return 0;
 }
 
-static int rockchip_gpiolib_register(struct platform_device *pdev,
-						struct rockchip_pinctrl *info)
+static int rockchip_pinctrl_parse_dt(struct platform_device *pdev,
+					      struct rockchip_pinctrl *info)
 {
-	struct rockchip_pin_ctrl *ctrl = info->ctrl;
-	struct rockchip_pin_bank *bank = ctrl->pin_banks;
-	struct gpio_chip *gc;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct device_node *child;
 	int ret;
 	int i;
 
-	for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
-		if (!bank->valid) {
-			dev_warn(&pdev->dev, "bank %s is not valid\n",
-				 bank->name);
-			continue;
-		}
-
-		bank->gpio_chip = rockchip_gpiolib_chip;
+	rockchip_pinctrl_child_count(info, np);
 
-		gc = &bank->gpio_chip;
-		gc->base = bank->pin_base;
-		gc->ngpio = bank->nr_pins;
-		gc->parent = &pdev->dev;
-		gc->of_node = bank->of_node;
-		gc->label = bank->name;
+	dev_dbg(&pdev->dev, "nfunctions = %d\n", info->nfunctions);
+	dev_dbg(&pdev->dev, "ngroups = %d\n", info->ngroups);
 
-		ret = gpiochip_add_data(gc, bank);
-		if (ret) {
-			dev_err(&pdev->dev, "failed to register gpio_chip %s, error code: %d\n",
-							gc->label, ret);
-			goto fail;
-		}
-	}
+	info->functions = devm_kcalloc(dev,
+					      info->nfunctions,
+					      sizeof(struct rockchip_pmx_func),
+					      GFP_KERNEL);
+	if (!info->functions)
+		return -ENOMEM;
 
-	rockchip_interrupts_register(pdev, info);
+	info->groups = devm_kcalloc(dev,
+					    info->ngroups,
+					    sizeof(struct rockchip_pin_group),
+					    GFP_KERNEL);
+	if (!info->groups)
+		return -ENOMEM;
 
-	return 0;
+	i = 0;
 
-fail:
-	for (--i, --bank; i >= 0; --i, --bank) {
-		if (!bank->valid)
+	for_each_child_of_node(np, child) {
+		if (of_match_node(rockchip_bank_match, child))
 			continue;
-		gpiochip_remove(&bank->gpio_chip);
-	}
-	return ret;
-}
-
-static int rockchip_gpiolib_unregister(struct platform_device *pdev,
-						struct rockchip_pinctrl *info)
-{
-	struct rockchip_pin_ctrl *ctrl = info->ctrl;
-	struct rockchip_pin_bank *bank = ctrl->pin_banks;
-	int i;
 
-	for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
-		if (!bank->valid)
-			continue;
-		gpiochip_remove(&bank->gpio_chip);
+		ret = rockchip_pinctrl_parse_functions(child, info, i++);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to parse function\n");
+			of_node_put(child);
+			return ret;
+		}
 	}
 
 	return 0;
 }
 
-static int rockchip_get_bank_data(struct rockchip_pin_bank *bank,
-				  struct rockchip_pinctrl *info)
+static int rockchip_pinctrl_register(struct platform_device *pdev,
+					struct rockchip_pinctrl *info)
 {
-	struct resource res;
-	void __iomem *base;
+	struct pinctrl_desc *ctrldesc = &info->pctl;
+	struct pinctrl_pin_desc *pindesc, *pdesc;
+	struct rockchip_pin_bank *pin_bank;
+	int pin, bank, ret;
+	int k;
 
-	if (of_address_to_resource(bank->of_node, 0, &res)) {
-		dev_err(info->dev, "cannot find IO resource for bank\n");
-		return -ENOENT;
-	}
+	ctrldesc->name = "rockchip-pinctrl";
+	ctrldesc->owner = THIS_MODULE;
+	ctrldesc->pctlops = &rockchip_pctrl_ops;
+	ctrldesc->pmxops = &rockchip_pmx_ops;
+	ctrldesc->confops = &rockchip_pinconf_ops;
 
-	bank->reg_base = devm_ioremap_resource(info->dev, &res);
-	if (IS_ERR(bank->reg_base))
-		return PTR_ERR(bank->reg_base);
+	pindesc = devm_kcalloc(&pdev->dev,
+			       info->ctrl->nr_pins, sizeof(*pindesc),
+			       GFP_KERNEL);
+	if (!pindesc)
+		return -ENOMEM;
 
-	/*
-	 * special case, where parts of the pull setting-registers are
-	 * part of the PMU register space
-	 */
-	if (of_device_is_compatible(bank->of_node,
-				    "rockchip,rk3188-gpio-bank0")) {
-		struct device_node *node;
-
-		node = of_parse_phandle(bank->of_node->parent,
-					"rockchip,pmu", 0);
-		if (!node) {
-			if (of_address_to_resource(bank->of_node, 1, &res)) {
-				dev_err(info->dev, "cannot find IO resource for bank\n");
-				return -ENOENT;
-			}
+	ctrldesc->pins = pindesc;
+	ctrldesc->npins = info->ctrl->nr_pins;
 
-			base = devm_ioremap_resource(info->dev, &res);
-			if (IS_ERR(base))
-				return PTR_ERR(base);
-			rockchip_regmap_config.max_register =
-						    resource_size(&res) - 4;
-			rockchip_regmap_config.name =
-					    "rockchip,rk3188-gpio-bank0-pull";
-			bank->regmap_pull = devm_regmap_init_mmio(info->dev,
-						    base,
-						    &rockchip_regmap_config);
+	pdesc = pindesc;
+	for (bank = 0, k = 0; bank < info->ctrl->nr_banks; bank++) {
+		pin_bank = &info->ctrl->pin_banks[bank];
+		for (pin = 0; pin < pin_bank->nr_pins; pin++, k++) {
+			pdesc->number = k;
+			pdesc->name = kasprintf(GFP_KERNEL, "%s-%d",
+						pin_bank->name, pin);
+			pdesc++;
 		}
-		of_node_put(node);
+
+		INIT_LIST_HEAD(&pin_bank->deferred_output);
+		mutex_init(&pin_bank->deferred_lock);
 	}
 
-	bank->irq = irq_of_parse_and_map(bank->of_node, 0);
+	ret = rockchip_pinctrl_parse_dt(pdev, info);
+	if (ret)
+		return ret;
 
-	bank->clk = of_clk_get(bank->of_node, 0);
-	if (IS_ERR(bank->clk))
-		return PTR_ERR(bank->clk);
+	info->pctl_dev = devm_pinctrl_register(&pdev->dev, ctrldesc, info);
+	if (IS_ERR(info->pctl_dev)) {
+		dev_err(&pdev->dev, "could not register pinctrl driver\n");
+		return PTR_ERR(info->pctl_dev);
+	}
 
-	return clk_prepare(bank->clk);
+	return 0;
 }
 
 static const struct of_device_id rockchip_pinctrl_dt_match[];
@@ -3584,7 +3387,6 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 {
 	const struct of_device_id *match;
 	struct device_node *node = pdev->dev.of_node;
-	struct device_node *np;
 	struct rockchip_pin_ctrl *ctrl;
 	struct rockchip_pin_bank *bank;
 	int grf_offs, pmu_offs, drv_grf_offs, drv_pmu_offs, i, j;
@@ -3592,23 +3394,6 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 	match = of_match_node(rockchip_pinctrl_dt_match, node);
 	ctrl = (struct rockchip_pin_ctrl *)match->data;
 
-	for_each_child_of_node(node, np) {
-		if (!of_find_property(np, "gpio-controller", NULL))
-			continue;
-
-		bank = ctrl->pin_banks;
-		for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
-			if (!strcmp(bank->name, np->name)) {
-				bank->of_node = np;
-
-				if (!rockchip_get_bank_data(bank, d))
-					bank->valid = true;
-
-				break;
-			}
-		}
-	}
-
 	grf_offs = ctrl->grf_mux_offset;
 	pmu_offs = ctrl->pmu_mux_offset;
 	drv_pmu_offs = ctrl->pmu_drv_offset;
@@ -3633,12 +3418,13 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 
 			/* preset iomux offset value, set new start value */
 			if (iom->offset >= 0) {
-				if (iom->type & IOMUX_SOURCE_PMU)
+				if ((iom->type & IOMUX_SOURCE_PMU) || (iom->type & IOMUX_L_SOURCE_PMU))
 					pmu_offs = iom->offset;
 				else
 					grf_offs = iom->offset;
 			} else { /* set current iomux offset */
-				iom->offset = (iom->type & IOMUX_SOURCE_PMU) ?
+				iom->offset = ((iom->type & IOMUX_SOURCE_PMU) ||
+					       (iom->type & IOMUX_L_SOURCE_PMU)) ?
 							pmu_offs : grf_offs;
 			}
 
@@ -3663,7 +3449,7 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
 			inc = (iom->type & (IOMUX_WIDTH_4BIT |
 					    IOMUX_WIDTH_3BIT |
 					    IOMUX_WIDTH_2BIT)) ? 8 : 4;
-			if (iom->type & IOMUX_SOURCE_PMU)
+			if ((iom->type & IOMUX_SOURCE_PMU) || (iom->type & IOMUX_L_SOURCE_PMU))
 				pmu_offs += inc;
 			else
 				grf_offs += inc;
@@ -3758,6 +3544,46 @@ static int __maybe_unused rockchip_pinctrl_resume(struct device *dev)
 static SIMPLE_DEV_PM_OPS(rockchip_pinctrl_dev_pm_ops, rockchip_pinctrl_suspend,
 			 rockchip_pinctrl_resume);
 
+/* SoC data specially handle */
+
+/* rk3308 SoC data initialize */
+#define RK3308_GRF_SOC_CON13			0x608
+#define RK3308_GRF_SOC_CON15			0x610
+
+/* RK3308_GRF_SOC_CON13 */
+#define RK3308_GRF_I2C3_IOFUNC_SRC_CTRL	(BIT(16 + 10) | BIT(10))
+#define RK3308_GRF_GPIO2A3_SEL_SRC_CTRL	(BIT(16 + 7)  | BIT(7))
+#define RK3308_GRF_GPIO2A2_SEL_SRC_CTRL	(BIT(16 + 3)  | BIT(3))
+
+/* RK3308_GRF_SOC_CON15 */
+#define RK3308_GRF_GPIO2C0_SEL_SRC_CTRL	(BIT(16 + 11) | BIT(11))
+#define RK3308_GRF_GPIO3B3_SEL_SRC_CTRL	(BIT(16 + 7)  | BIT(7))
+#define RK3308_GRF_GPIO3B2_SEL_SRC_CTRL	(BIT(16 + 3)  | BIT(3))
+
+static int rk3308_soc_data_init(struct rockchip_pinctrl *info)
+{
+	int ret;
+
+	/*
+	 * Enable the special ctrl of selected sources.
+	 */
+
+	ret = regmap_write(info->regmap_base, RK3308_GRF_SOC_CON13,
+			   RK3308_GRF_I2C3_IOFUNC_SRC_CTRL |
+			   RK3308_GRF_GPIO2A3_SEL_SRC_CTRL |
+			   RK3308_GRF_GPIO2A2_SEL_SRC_CTRL);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(info->regmap_base, RK3308_GRF_SOC_CON15,
+			   RK3308_GRF_GPIO2C0_SEL_SRC_CTRL |
+			   RK3308_GRF_GPIO3B3_SEL_SRC_CTRL |
+			   RK3308_GRF_GPIO3B2_SEL_SRC_CTRL);
+
+	return ret;
+
+}
+
 static int rockchip_pinctrl_probe(struct platform_device *pdev)
 {
 	struct rockchip_pinctrl *info;
@@ -3831,17 +3657,50 @@ static int rockchip_pinctrl_probe(struct platform_device *pdev)
 			return PTR_ERR(info->regmap_pmu);
 	}
 
-	ret = rockchip_gpiolib_register(pdev, info);
+	/* Special handle for some Socs */
+	if (ctrl->soc_data_init) {
+		ret = ctrl->soc_data_init(info);
+		if (ret)
+			return ret;
+	}
+
+	ret = rockchip_pinctrl_register(pdev, info);
 	if (ret)
 		return ret;
 
-	ret = rockchip_pinctrl_register(pdev, info);
+	platform_set_drvdata(pdev, info);
+
+	ret = of_platform_populate(np, rockchip_bank_match, NULL, NULL);
 	if (ret) {
-		rockchip_gpiolib_unregister(pdev, info);
+		dev_err(&pdev->dev, "failed to register gpio device\n");
 		return ret;
 	}
+	dev_info(dev, "probed %s\n", dev_name(dev));
 
-	platform_set_drvdata(pdev, info);
+	return 0;
+}
+
+static int rockchip_pinctrl_remove(struct platform_device *pdev)
+{
+	struct rockchip_pinctrl *info = platform_get_drvdata(pdev);
+	struct rockchip_pin_bank *bank;
+	struct rockchip_pin_output_deferred *cfg;
+	int i;
+
+	of_platform_depopulate(&pdev->dev);
+
+	for (i = 0; i < info->ctrl->nr_banks; i++) {
+		bank = &info->ctrl->pin_banks[i];
+
+		mutex_lock(&bank->deferred_lock);
+		while (!list_empty(&bank->deferred_output)) {
+			cfg = list_first_entry(&bank->deferred_output,
+					       struct rockchip_pin_output_deferred, head);
+			list_del(&cfg->head);
+			kfree(cfg);
+		}
+		mutex_unlock(&bank->deferred_lock);
+	}
 
 	return 0;
 }
@@ -3881,6 +3740,7 @@ static struct rockchip_pin_ctrl px30_pin_ctrl = {
 		.pull_calc_reg		= px30_calc_pull_reg_and_bit,
 		.drv_calc_reg		= px30_calc_drv_reg_and_bit,
 		.schmitt_calc_reg	= px30_calc_schmitt_reg_and_bit,
+		.slew_rate_calc_reg	= px30_calc_slew_rate_reg_and_bit,
 };
 
 static struct rockchip_pin_bank rv1108_pin_banks[] = {
@@ -3907,6 +3767,86 @@ static struct rockchip_pin_ctrl rv1108_pin_ctrl = {
 	.schmitt_calc_reg	= rv1108_calc_schmitt_reg_and_bit,
 };
 
+static struct rockchip_pin_bank rv1126_pin_banks[] = {
+	PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0",
+			     IOMUX_WIDTH_4BIT | IOMUX_SOURCE_PMU,
+			     IOMUX_WIDTH_4BIT | IOMUX_SOURCE_PMU,
+			     IOMUX_WIDTH_4BIT | IOMUX_L_SOURCE_PMU,
+			     IOMUX_WIDTH_4BIT),
+	PIN_BANK_IOMUX_FLAGS_OFFSET(1, 32, "gpio1",
+			     IOMUX_WIDTH_4BIT,
+			     IOMUX_WIDTH_4BIT,
+			     IOMUX_WIDTH_4BIT,
+			     IOMUX_WIDTH_4BIT,
+			     0x10010, 0x10018, 0x10020, 0x10028),
+	PIN_BANK_IOMUX_FLAGS(2, 32, "gpio2",
+			     IOMUX_WIDTH_4BIT,
+			     IOMUX_WIDTH_4BIT,
+			     IOMUX_WIDTH_4BIT,
+			     IOMUX_WIDTH_4BIT),
+	PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3",
+			     IOMUX_WIDTH_4BIT,
+			     IOMUX_WIDTH_4BIT,
+			     IOMUX_WIDTH_4BIT,
+			     IOMUX_WIDTH_4BIT),
+	PIN_BANK_IOMUX_FLAGS(4, 2, "gpio4",
+			     IOMUX_WIDTH_4BIT, 0, 0, 0),
+};
+
+static struct rockchip_pin_ctrl rv1126_pin_ctrl = {
+	.pin_banks		= rv1126_pin_banks,
+	.nr_banks		= ARRAY_SIZE(rv1126_pin_banks),
+	.label			= "RV1126-GPIO",
+	.type			= RV1126,
+	.grf_mux_offset		= 0x10004, /* mux offset from GPIO0_D0 */
+	.pmu_mux_offset		= 0x0,
+	.iomux_routes		= rv1126_mux_route_data,
+	.niomux_routes		= ARRAY_SIZE(rv1126_mux_route_data),
+	.iomux_recalced		= rv1126_mux_recalced_data,
+	.niomux_recalced	= ARRAY_SIZE(rv1126_mux_recalced_data),
+	.pull_calc_reg		= rv1126_calc_pull_reg_and_bit,
+	.drv_calc_reg		= rv1126_calc_drv_reg_and_bit,
+	.schmitt_calc_reg	= rv1126_calc_schmitt_reg_and_bit,
+};
+
+static struct rockchip_pin_bank rk1808_pin_banks[] = {
+	PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_SOURCE_PMU,
+					     IOMUX_SOURCE_PMU,
+					     IOMUX_SOURCE_PMU,
+					     IOMUX_SOURCE_PMU),
+	PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT),
+	PIN_BANK_IOMUX_FLAGS(2, 32, "gpio2", IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT),
+	PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3", IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT),
+	PIN_BANK_IOMUX_FLAGS(4, 32, "gpio4", IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT),
+};
+
+static struct rockchip_pin_ctrl rk1808_pin_ctrl = {
+	.pin_banks		= rk1808_pin_banks,
+	.nr_banks		= ARRAY_SIZE(rk1808_pin_banks),
+	.label			= "RK1808-GPIO",
+	.type			= RK1808,
+	.iomux_routes		= rk1808_mux_route_data,
+	.niomux_routes		= ARRAY_SIZE(rk1808_mux_route_data),
+	.grf_mux_offset		= 0x0,
+	.pmu_mux_offset		= 0x0,
+	.pull_calc_reg		= rk1808_calc_pull_reg_and_bit,
+	.drv_calc_reg		= rk1808_calc_drv_reg_and_bit,
+	.schmitt_calc_reg	= rk1808_calc_schmitt_reg_and_bit,
+	.slew_rate_calc_reg	= rk1808_calc_slew_rate_reg_and_bit,
+};
+
 static struct rockchip_pin_bank rk2928_pin_banks[] = {
 	PIN_BANK(0, 32, "gpio0"),
 	PIN_BANK(1, 32, "gpio1"),
@@ -4029,9 +3969,9 @@ static struct rockchip_pin_ctrl rk3228_pin_ctrl = {
 };
 
 static struct rockchip_pin_bank rk3288_pin_banks[] = {
-	PIN_BANK_IOMUX_FLAGS(0, 24, "gpio0", IOMUX_SOURCE_PMU,
-					     IOMUX_SOURCE_PMU,
-					     IOMUX_SOURCE_PMU,
+	PIN_BANK_IOMUX_FLAGS(0, 24, "gpio0", IOMUX_SOURCE_PMU | IOMUX_WRITABLE_32BIT,
+					     IOMUX_SOURCE_PMU | IOMUX_WRITABLE_32BIT,
+					     IOMUX_SOURCE_PMU | IOMUX_WRITABLE_32BIT,
 					     IOMUX_UNROUTED
 			    ),
 	PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", IOMUX_UNROUTED,
@@ -4106,6 +4046,7 @@ static struct rockchip_pin_ctrl rk3308_pin_ctrl = {
 		.niomux_recalced	= ARRAY_SIZE(rk3308_mux_recalced_data),
 		.iomux_routes		= rk3308_mux_route_data,
 		.niomux_routes		= ARRAY_SIZE(rk3308_mux_route_data),
+		.soc_data_init		= rk3308_soc_data_init,
 		.pull_calc_reg		= rk3308_calc_pull_reg_and_bit,
 		.drv_calc_reg		= rk3308_calc_drv_reg_and_bit,
 		.schmitt_calc_reg	= rk3308_calc_schmitt_reg_and_bit,
@@ -4230,11 +4171,78 @@ static struct rockchip_pin_ctrl rk3399_pin_ctrl = {
 		.drv_calc_reg		= rk3399_calc_drv_reg_and_bit,
 };
 
+static struct rockchip_pin_bank rk3568_pin_banks[] = {
+	PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_SOURCE_PMU | IOMUX_WIDTH_4BIT,
+					     IOMUX_SOURCE_PMU | IOMUX_WIDTH_4BIT,
+					     IOMUX_SOURCE_PMU | IOMUX_WIDTH_4BIT,
+					     IOMUX_SOURCE_PMU | IOMUX_WIDTH_4BIT),
+	PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT),
+	PIN_BANK_IOMUX_FLAGS(2, 32, "gpio2", IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT),
+	PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3", IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT),
+	PIN_BANK_IOMUX_FLAGS(4, 32, "gpio4", IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT,
+					     IOMUX_WIDTH_4BIT),
+};
+
+static struct rockchip_pin_ctrl rk3568_pin_ctrl = {
+	.pin_banks		= rk3568_pin_banks,
+	.nr_banks		= ARRAY_SIZE(rk3568_pin_banks),
+	.label			= "RK3568-GPIO",
+	.type			= RK3568,
+	.grf_mux_offset		= 0x0,
+	.pmu_mux_offset		= 0x0,
+	.grf_drv_offset		= 0x0200,
+	.pmu_drv_offset		= 0x0070,
+	.iomux_routes		= rk3568_mux_route_data,
+	.niomux_routes		= ARRAY_SIZE(rk3568_mux_route_data),
+	.pull_calc_reg		= rk3568_calc_pull_reg_and_bit,
+	.drv_calc_reg		= rk3568_calc_drv_reg_and_bit,
+	.slew_rate_calc_reg	= rk3568_calc_slew_rate_reg_and_bit,
+	.schmitt_calc_reg	= rk3568_calc_schmitt_reg_and_bit,
+};
+
+static struct rockchip_pin_bank rk3588_pin_banks[] = {
+	RK3588_PIN_BANK_FLAGS(0, 32, "gpio0",
+			      IOMUX_WIDTH_4BIT, PULL_TYPE_IO_1V8_ONLY),
+	RK3588_PIN_BANK_FLAGS(1, 32, "gpio1",
+			      IOMUX_WIDTH_4BIT, PULL_TYPE_IO_1V8_ONLY),
+	RK3588_PIN_BANK_FLAGS(2, 32, "gpio2",
+			      IOMUX_WIDTH_4BIT, PULL_TYPE_IO_1V8_ONLY),
+	RK3588_PIN_BANK_FLAGS(3, 32, "gpio3",
+			      IOMUX_WIDTH_4BIT, PULL_TYPE_IO_1V8_ONLY),
+	RK3588_PIN_BANK_FLAGS(4, 32, "gpio4",
+			      IOMUX_WIDTH_4BIT, PULL_TYPE_IO_1V8_ONLY),
+};
+
+static struct rockchip_pin_ctrl rk3588_pin_ctrl = {
+	.pin_banks		= rk3588_pin_banks,
+	.nr_banks		= ARRAY_SIZE(rk3588_pin_banks),
+	.label			= "RK3588-GPIO",
+	.type			= RK3588,
+	.pull_calc_reg		= rk3588_calc_pull_reg_and_bit,
+	.drv_calc_reg		= rk3588_calc_drv_reg_and_bit,
+	.schmitt_calc_reg	= rk3588_calc_schmitt_reg_and_bit,
+};
+
 static const struct of_device_id rockchip_pinctrl_dt_match[] = {
 	{ .compatible = "rockchip,px30-pinctrl",
 		.data = &px30_pin_ctrl },
 	{ .compatible = "rockchip,rv1108-pinctrl",
 		.data = &rv1108_pin_ctrl },
+	{ .compatible = "rockchip,rv1126-pinctrl",
+		.data = &rv1126_pin_ctrl },
+	{ .compatible = "rockchip,rk1808-pinctrl",
+		.data = &rk1808_pin_ctrl },
 	{ .compatible = "rockchip,rk2928-pinctrl",
 		.data = &rk2928_pin_ctrl },
 	{ .compatible = "rockchip,rk3036-pinctrl",
@@ -4259,11 +4267,16 @@ static const struct of_device_id rockchip_pinctrl_dt_match[] = {
 		.data = &rk3368_pin_ctrl },
 	{ .compatible = "rockchip,rk3399-pinctrl",
 		.data = &rk3399_pin_ctrl },
+	{ .compatible = "rockchip,rk3568-pinctrl",
+		.data = &rk3568_pin_ctrl },
+	{ .compatible = "rockchip,rk3588-pinctrl",
+		.data = &rk3588_pin_ctrl },
 	{},
 };
 
 static struct platform_driver rockchip_pinctrl_driver = {
 	.probe		= rockchip_pinctrl_probe,
+	.remove		= rockchip_pinctrl_remove,
 	.driver = {
 		.name	= "rockchip-pinctrl",
 		.pm = &rockchip_pinctrl_dev_pm_ops,
@@ -4276,3 +4289,14 @@ static int __init rockchip_pinctrl_drv_register(void)
 	return platform_driver_register(&rockchip_pinctrl_driver);
 }
 postcore_initcall(rockchip_pinctrl_drv_register);
+
+static void __exit rockchip_pinctrl_drv_unregister(void)
+{
+	platform_driver_unregister(&rockchip_pinctrl_driver);
+}
+module_exit(rockchip_pinctrl_drv_unregister);
+
+MODULE_DESCRIPTION("ROCKCHIP Pin Controller Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pinctrl-rockchip");
+MODULE_DEVICE_TABLE(of, rockchip_pinctrl_dt_match);
diff --git a/drivers/power/reset/reboot-mode.c b/drivers/power/reset/reboot-mode.c
index b4076b10b..feaaa80ca 100644
--- a/drivers/power/reset/reboot-mode.c
+++ b/drivers/power/reset/reboot-mode.c
@@ -6,10 +6,12 @@
 #include <linux/device.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
+#include <linux/kobject.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/reboot.h>
 #include <linux/reboot-mode.h>
+#include <linux/sysfs.h>
 
 #define PREFIX "mode-"
 
@@ -19,14 +21,24 @@ struct mode_info {
 	struct list_head list;
 };
 
-static unsigned int get_reboot_mode_magic(struct reboot_mode_driver *reboot,
-					  const char *cmd)
+static const char *boot_mode = "coldboot";
+
+static ssize_t boot_mode_show(struct kobject *kobj, struct kobj_attribute *attr,
+			      char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "%s\n", boot_mode);
+}
+
+static struct kobj_attribute kobj_boot_mode = __ATTR_RO(boot_mode);
+
+static int get_reboot_mode_magic(struct reboot_mode_driver *reboot,
+				 const char *cmd)
 {
 	const char *normal = "normal";
 	int magic = 0;
 	struct mode_info *info;
 
-	if (!cmd)
+	if (!cmd || !cmd[0])
 		cmd = normal;
 
 	list_for_each_entry(info, &reboot->head, list) {
@@ -39,20 +51,56 @@ static unsigned int get_reboot_mode_magic(struct reboot_mode_driver *reboot,
 	return magic;
 }
 
+static void reboot_mode_write(struct reboot_mode_driver *reboot,
+			      const void *cmd)
+{
+	int magic;
+
+	magic = get_reboot_mode_magic(reboot, cmd);
+	if (!magic)
+		magic = get_reboot_mode_magic(reboot, NULL);
+	if (magic)
+		reboot->write(reboot, magic);
+}
+
 static int reboot_mode_notify(struct notifier_block *this,
 			      unsigned long mode, void *cmd)
 {
 	struct reboot_mode_driver *reboot;
-	unsigned int magic;
 
 	reboot = container_of(this, struct reboot_mode_driver, reboot_notifier);
-	magic = get_reboot_mode_magic(reboot, cmd);
-	if (magic)
-		reboot->write(reboot, magic);
+	reboot_mode_write(reboot, cmd);
 
 	return NOTIFY_DONE;
 }
 
+static int reboot_mode_panic_notify(struct notifier_block *this,
+				      unsigned long ev, void *ptr)
+{
+	struct reboot_mode_driver *reboot;
+	const char *cmd = "panic";
+
+	reboot = container_of(this, struct reboot_mode_driver, panic_notifier);
+	reboot_mode_write(reboot, cmd);
+
+	return NOTIFY_DONE;
+}
+
+static int boot_mode_parse(struct reboot_mode_driver *reboot)
+{
+	struct mode_info *info;
+	unsigned int magic = reboot->read(reboot);
+
+	list_for_each_entry(info, &reboot->head, list) {
+		if (info->magic == magic) {
+			boot_mode = info->mode;
+			break;
+		}
+	}
+
+	return 0;
+}
+
 /**
  * reboot_mode_register - register a reboot mode driver
  * @reboot: reboot mode driver
@@ -101,10 +149,16 @@ int reboot_mode_register(struct reboot_mode_driver *reboot)
 		list_add_tail(&info->list, &reboot->head);
 	}
 
+	boot_mode_parse(reboot);
 	reboot->reboot_notifier.notifier_call = reboot_mode_notify;
+	reboot->panic_notifier.notifier_call = reboot_mode_panic_notify;
 	register_reboot_notifier(&reboot->reboot_notifier);
+	register_pre_restart_handler(&reboot->reboot_notifier);
+	atomic_notifier_chain_register(&panic_notifier_list,
+				       &reboot->panic_notifier);
+	ret = sysfs_create_file(kernel_kobj, &kobj_boot_mode.attr);
 
-	return 0;
+	return ret;
 
 error:
 	list_for_each_entry(info, &reboot->head, list)
diff --git a/drivers/power/reset/syscon-reboot-mode.c b/drivers/power/reset/syscon-reboot-mode.c
index e0772c9f7..2fe0089c4 100644
--- a/drivers/power/reset/syscon-reboot-mode.c
+++ b/drivers/power/reset/syscon-reboot-mode.c
@@ -36,6 +36,17 @@ static int syscon_reboot_mode_write(struct reboot_mode_driver *reboot,
 	return ret;
 }
 
+static int syscon_reboot_mode_read(struct reboot_mode_driver *reboot)
+{
+	struct syscon_reboot_mode *syscon_rbm;
+	u32 val = 0;
+
+	syscon_rbm = container_of(reboot, struct syscon_reboot_mode, reboot);
+	regmap_read(syscon_rbm->map, syscon_rbm->offset, &val);
+
+	return val;
+}
+
 static int syscon_reboot_mode_probe(struct platform_device *pdev)
 {
 	int ret;
@@ -47,6 +58,7 @@ static int syscon_reboot_mode_probe(struct platform_device *pdev)
 
 	syscon_rbm->reboot.dev = &pdev->dev;
 	syscon_rbm->reboot.write = syscon_reboot_mode_write;
+	syscon_rbm->reboot.read = syscon_reboot_mode_read;
 	syscon_rbm->mask = 0xffffffff;
 
 	syscon_rbm->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
index 2b644590f..8cdd19de0 100644
--- a/drivers/power/supply/power_supply_core.c
+++ b/drivers/power/supply/power_supply_core.c
@@ -32,6 +32,13 @@ EXPORT_SYMBOL_GPL(power_supply_notifier);
 
 static struct device_type power_supply_dev_type;
 
+struct match_device_node_array_param {
+	struct device_node *parent_of_node;
+	struct power_supply **psy;
+	ssize_t psy_size;
+	ssize_t psy_count;
+};
+
 #define POWER_SUPPLY_DEFERRED_REGISTER_TIME	msecs_to_jiffies(10)
 
 static bool __power_supply_is_supplied_by(struct power_supply *supplier,
@@ -522,6 +529,77 @@ struct power_supply *power_supply_get_by_phandle(struct device_node *np,
 }
 EXPORT_SYMBOL_GPL(power_supply_get_by_phandle);
 
+static int power_supply_match_device_node_array(struct device *dev,
+						void *data)
+{
+	struct match_device_node_array_param *param =
+		(struct match_device_node_array_param *)data;
+	struct power_supply **psy = param->psy;
+	ssize_t size = param->psy_size;
+	ssize_t *count = &param->psy_count;
+
+	if (!dev->parent || dev->parent->of_node != param->parent_of_node)
+		return 0;
+
+	if (*count >= size)
+		return -EOVERFLOW;
+
+	psy[*count] = dev_get_drvdata(dev);
+	atomic_inc(&psy[*count]->use_cnt);
+	(*count)++;
+
+	return 0;
+}
+
+/**
+ * power_supply_get_by_phandle_array() - Similar to
+ * power_supply_get_by_phandle but returns an array of power supply
+ * objects which are associated with the phandle.
+ * @np: Pointer to device node holding phandle property.
+ * @property: Name of property holding a power supply name.
+ * @psy: Array of power_supply pointers provided by the client which is
+ * filled by power_supply_get_by_phandle_array.
+ * @size: size of power_supply pointer array.
+ *
+ * If power supply was found, it increases reference count for the
+ * internal power supply's device. The user should power_supply_put()
+ * after usage.
+ *
+ * Return: On success returns the number of power supply objects filled
+ * in the @psy array.
+ * -EOVERFLOW when size of @psy array is not suffice.
+ * -EINVAL when @psy is NULL or @size is 0.
+ * -ENODEV when matching device_node is not found.
+ */
+int power_supply_get_by_phandle_array(struct device_node *np,
+				      const char *property,
+				      struct power_supply **psy,
+				      ssize_t size)
+{
+	struct device_node *power_supply_np;
+	int ret;
+	struct match_device_node_array_param param;
+
+	if (!psy || !size)
+		return -EINVAL;
+
+	power_supply_np = of_parse_phandle(np, property, 0);
+	if (!power_supply_np)
+		return -ENODEV;
+
+	param.parent_of_node = power_supply_np;
+	param.psy = psy;
+	param.psy_size = size;
+	param.psy_count = 0;
+	ret = class_for_each_device(power_supply_class, NULL, &param,
+				    power_supply_match_device_node_array);
+
+	of_node_put(power_supply_np);
+
+	return param.psy_count;
+}
+EXPORT_SYMBOL_GPL(power_supply_get_by_phandle_array);
+
 static void devm_power_supply_put(struct device *dev, void *res)
 {
 	struct power_supply **psy = res;
diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c
index a616b9d8f..a5e656360 100644
--- a/drivers/power/supply/power_supply_sysfs.c
+++ b/drivers/power/supply/power_supply_sysfs.c
@@ -89,6 +89,7 @@ static const char * const POWER_SUPPLY_CHARGE_TYPE_TEXT[] = {
 	[POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE]	= "Adaptive",
 	[POWER_SUPPLY_CHARGE_TYPE_CUSTOM]	= "Custom",
 	[POWER_SUPPLY_CHARGE_TYPE_LONGLIFE]	= "Long Life",
+	[POWER_SUPPLY_CHARGE_TYPE_TAPER]	= "Taper",
 };
 
 static const char * const POWER_SUPPLY_HEALTH_TEXT[] = {
diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c
index 1f3079562..c6b3ce057 100644
--- a/drivers/pwm/pwm-rockchip.c
+++ b/drivers/pwm/pwm-rockchip.c
@@ -11,6 +11,7 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
 #include <linux/time.h>
@@ -26,15 +27,25 @@
 #define PWM_INACTIVE_POSITIVE	(1 << 4)
 #define PWM_POLARITY_MASK	(PWM_DUTY_POSITIVE | PWM_INACTIVE_POSITIVE)
 #define PWM_OUTPUT_LEFT		(0 << 5)
+#define PWM_OUTPUT_CENTER	(1 << 5)
 #define PWM_LOCK_EN		(1 << 6)
 #define PWM_LP_DISABLE		(0 << 8)
 
+#define PWM_ONESHOT_COUNT_SHIFT	24
+#define PWM_ONESHOT_COUNT_MAX	256
+
 struct rockchip_pwm_chip {
 	struct pwm_chip chip;
 	struct clk *clk;
 	struct clk *pclk;
+	struct pinctrl *pinctrl;
+	struct pinctrl_state *active_state;
 	const struct rockchip_pwm_data *data;
 	void __iomem *base;
+	unsigned long clk_rate;
+	bool vop_pwm_en; /* indicate voppwm mirror register state */
+	bool center_aligned;
+	bool oneshot;
 };
 
 struct rockchip_pwm_regs {
@@ -49,7 +60,9 @@ struct rockchip_pwm_data {
 	unsigned int prescaler;
 	bool supports_polarity;
 	bool supports_lock;
+	bool vop_pwm;
 	u32 enable_conf;
+	u32 enable_conf_mask;
 };
 
 static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c)
@@ -63,7 +76,6 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip,
 {
 	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
 	u32 enable_conf = pc->data->enable_conf;
-	unsigned long clk_rate;
 	u64 tmp;
 	u32 val;
 	int ret;
@@ -72,15 +84,13 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip,
 	if (ret)
 		return;
 
-	clk_rate = clk_get_rate(pc->clk);
-
 	tmp = readl_relaxed(pc->base + pc->data->regs.period);
 	tmp *= pc->data->prescaler * NSEC_PER_SEC;
-	state->period = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate);
+	state->period = DIV_ROUND_CLOSEST_ULL(tmp, pc->clk_rate);
 
 	tmp = readl_relaxed(pc->base + pc->data->regs.duty);
 	tmp *= pc->data->prescaler * NSEC_PER_SEC;
-	state->duty_cycle =  DIV_ROUND_CLOSEST_ULL(tmp, clk_rate);
+	state->duty_cycle =  DIV_ROUND_CLOSEST_ULL(tmp, pc->clk_rate);
 
 	val = readl_relaxed(pc->base + pc->data->regs.ctrl);
 	state->enabled = (val & enable_conf) == enable_conf;
@@ -98,28 +108,48 @@ static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 {
 	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
 	unsigned long period, duty;
-	u64 clk_rate, div;
+	unsigned long flags;
+	u64 div;
 	u32 ctrl;
 
-	clk_rate = clk_get_rate(pc->clk);
-
 	/*
 	 * Since period and duty cycle registers have a width of 32
 	 * bits, every possible input period can be obtained using the
 	 * default prescaler value for all practical clock rate values.
 	 */
-	div = clk_rate * state->period;
+	div = (u64)pc->clk_rate * state->period;
 	period = DIV_ROUND_CLOSEST_ULL(div,
 				       pc->data->prescaler * NSEC_PER_SEC);
 
-	div = clk_rate * state->duty_cycle;
+	div = (u64)pc->clk_rate * state->duty_cycle;
 	duty = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC);
 
+	local_irq_save(flags);
 	/*
 	 * Lock the period and duty of previous configuration, then
 	 * change the duty and period, that would not be effective.
 	 */
 	ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl);
+	if (pc->data->vop_pwm) {
+		if (pc->vop_pwm_en)
+			ctrl |= PWM_ENABLE;
+		else
+			ctrl &= ~PWM_ENABLE;
+	}
+
+#ifdef CONFIG_PWM_ROCKCHIP_ONESHOT
+	if (state->oneshot_count > PWM_ONESHOT_COUNT_MAX) {
+		pc->oneshot = false;
+		dev_err(chip->dev, "Oneshot_count value overflow.\n");
+	} else if (state->oneshot_count > 0) {
+		pc->oneshot = true;
+		ctrl |= (state->oneshot_count - 1) << PWM_ONESHOT_COUNT_SHIFT;
+	} else {
+		pc->oneshot = false;
+		ctrl |= PWM_CONTINUOUS;
+	}
+#endif
+
 	if (pc->data->supports_lock) {
 		ctrl |= PWM_LOCK_EN;
 		writel_relaxed(ctrl, pc->base + pc->data->regs.ctrl);
@@ -145,6 +175,7 @@ static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 		ctrl &= ~PWM_LOCK_EN;
 
 	writel(ctrl, pc->base + pc->data->regs.ctrl);
+	local_irq_restore(flags);
 }
 
 static int rockchip_pwm_enable(struct pwm_chip *chip,
@@ -163,13 +194,24 @@ static int rockchip_pwm_enable(struct pwm_chip *chip,
 	}
 
 	val = readl_relaxed(pc->base + pc->data->regs.ctrl);
+	val &= ~pc->data->enable_conf_mask;
+
+	if (PWM_OUTPUT_CENTER & pc->data->enable_conf_mask) {
+		if (pc->center_aligned)
+			val |= PWM_OUTPUT_CENTER;
+	}
 
-	if (enable)
+	if (enable) {
 		val |= enable_conf;
-	else
+		if (pc->oneshot)
+			val &= ~PWM_CONTINUOUS;
+	} else {
 		val &= ~enable_conf;
+	}
 
 	writel_relaxed(val, pc->base + pc->data->regs.ctrl);
+	if (pc->data->vop_pwm)
+		pc->vop_pwm_en = enable;
 
 	if (!enable)
 		clk_disable(pc->clk);
@@ -207,6 +249,8 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 			goto out;
 	}
 
+	if (state->enabled)
+		ret = pinctrl_select_state(pc->pinctrl, pc->active_state);
 out:
 	clk_disable(pc->pclk);
 
@@ -229,7 +273,9 @@ static const struct rockchip_pwm_data pwm_data_v1 = {
 	.prescaler = 2,
 	.supports_polarity = false,
 	.supports_lock = false,
+	.vop_pwm = false,
 	.enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN,
+	.enable_conf_mask = BIT(1) | BIT(3),
 };
 
 static const struct rockchip_pwm_data pwm_data_v2 = {
@@ -242,8 +288,10 @@ static const struct rockchip_pwm_data pwm_data_v2 = {
 	.prescaler = 1,
 	.supports_polarity = true,
 	.supports_lock = false,
+	.vop_pwm = false,
 	.enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
 		       PWM_CONTINUOUS,
+	.enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8),
 };
 
 static const struct rockchip_pwm_data pwm_data_vop = {
@@ -256,8 +304,10 @@ static const struct rockchip_pwm_data pwm_data_vop = {
 	.prescaler = 1,
 	.supports_polarity = true,
 	.supports_lock = false,
+	.vop_pwm = true,
 	.enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
 		       PWM_CONTINUOUS,
+	.enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8),
 };
 
 static const struct rockchip_pwm_data pwm_data_v3 = {
@@ -270,8 +320,10 @@ static const struct rockchip_pwm_data pwm_data_v3 = {
 	.prescaler = 1,
 	.supports_polarity = true,
 	.supports_lock = true,
+	.vop_pwm = false,
 	.enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE |
 		       PWM_CONTINUOUS,
+	.enable_conf_mask = GENMASK(2, 0) | BIT(5) | BIT(8),
 };
 
 static const struct of_device_id rockchip_pwm_dt_ids[] = {
@@ -301,7 +353,8 @@ static int rockchip_pwm_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	pc->base = devm_ioremap_resource(&pdev->dev, r);
+	pc->base = devm_ioremap(&pdev->dev, r->start,
+				resource_size(r));
 	if (IS_ERR(pc->base))
 		return PTR_ERR(pc->base);
 
@@ -339,6 +392,18 @@ static int rockchip_pwm_probe(struct platform_device *pdev)
 		goto err_clk;
 	}
 
+	pc->pinctrl = devm_pinctrl_get(&pdev->dev);
+	if (IS_ERR(pc->pinctrl)) {
+		dev_err(&pdev->dev, "Get pinctrl failed!\n");
+		return PTR_ERR(pc->pinctrl);
+	}
+
+	pc->active_state = pinctrl_lookup_state(pc->pinctrl, "active");
+	if (IS_ERR(pc->active_state)) {
+		dev_err(&pdev->dev, "No active pinctrl state\n");
+		return PTR_ERR(pc->active_state);
+	}
+
 	platform_set_drvdata(pdev, pc);
 
 	pc->data = id->data;
@@ -346,6 +411,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev)
 	pc->chip.ops = &rockchip_pwm_ops;
 	pc->chip.base = -1;
 	pc->chip.npwm = 1;
+	pc->clk_rate = clk_get_rate(pc->clk);
 
 	if (pc->data->supports_polarity) {
 		pc->chip.of_xlate = of_pwm_xlate_with_flags;
@@ -356,6 +422,9 @@ static int rockchip_pwm_probe(struct platform_device *pdev)
 	ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl);
 	enabled = (ctrl & enable_conf) == enable_conf;
 
+	pc->center_aligned =
+		device_property_read_bool(&pdev->dev, "center-aligned");
+
 	ret = pwmchip_add(&pc->chip);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
@@ -382,6 +451,20 @@ static int rockchip_pwm_remove(struct platform_device *pdev)
 {
 	struct rockchip_pwm_chip *pc = platform_get_drvdata(pdev);
 
+	/*
+	 * Disable the PWM clk before unpreparing it if the PWM device is still
+	 * running. This should only happen when the last PWM user left it
+	 * enabled, or when nobody requested a PWM that was previously enabled
+	 * by the bootloader.
+	 *
+	 * FIXME: Maybe the core should disable all PWM devices in
+	 * pwmchip_remove(). In this case we'd only have to call
+	 * clk_unprepare() after pwmchip_remove().
+	 *
+	 */
+	if (pwm_is_enabled(pc->chip.pwms))
+		clk_disable(pc->clk);
+
 	clk_unprepare(pc->pclk);
 	clk_unprepare(pc->clk);
 
@@ -396,7 +479,21 @@ static struct platform_driver rockchip_pwm_driver = {
 	.probe = rockchip_pwm_probe,
 	.remove = rockchip_pwm_remove,
 };
+#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT
+static int __init rockchip_pwm_driver_init(void)
+{
+	return platform_driver_register(&rockchip_pwm_driver);
+}
+subsys_initcall(rockchip_pwm_driver_init);
+
+static void __exit rockchip_pwm_driver_exit(void)
+{
+	platform_driver_unregister(&rockchip_pwm_driver);
+}
+module_exit(rockchip_pwm_driver_exit);
+#else
 module_platform_driver(rockchip_pwm_driver);
+#endif
 
 MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
 MODULE_DESCRIPTION("Rockchip SoC PWM driver");
diff --git a/drivers/pwm/sysfs.c b/drivers/pwm/sysfs.c
index 9903c3a7e..ca47570cb 100644
--- a/drivers/pwm/sysfs.c
+++ b/drivers/pwm/sysfs.c
@@ -103,6 +103,43 @@ static ssize_t duty_cycle_store(struct device *child,
 	return ret ? : size;
 }
 
+#ifdef CONFIG_PWM_ROCKCHIP_ONESHOT
+static ssize_t oneshot_count_show(struct device *child,
+			       struct device_attribute *attr,
+			       char *buf)
+{
+	const struct pwm_device *pwm = child_to_pwm_device(child);
+	struct pwm_state state;
+
+	pwm_get_state(pwm, &state);
+
+	return sprintf(buf, "%llu\n", state.oneshot_count);
+}
+
+static ssize_t oneshot_count_store(struct device *child,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	struct pwm_export *export = child_to_pwm_export(child);
+	struct pwm_device *pwm = export->pwm;
+	struct pwm_state state;
+	unsigned int val;
+	int ret;
+
+	ret = kstrtouint(buf, 0, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&export->lock);
+	pwm_get_state(pwm, &state);
+	state.oneshot_count = val;
+	ret = pwm_apply_state(pwm, &state);
+	mutex_unlock(&export->lock);
+
+	return ret ? : size;
+}
+#endif
+
 static ssize_t enable_show(struct device *child,
 			   struct device_attribute *attr,
 			   char *buf)
@@ -215,18 +252,49 @@ static ssize_t capture_show(struct device *child,
 	return sprintf(buf, "%u %u\n", result.period, result.duty_cycle);
 }
 
+static ssize_t output_type_show(struct device *child,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	const struct pwm_device *pwm = child_to_pwm_device(child);
+	const char *output_type = "unknown";
+	struct pwm_state state;
+
+	pwm_get_state(pwm, &state);
+	switch (state.output_type) {
+	case PWM_OUTPUT_FIXED:
+		output_type = "fixed";
+		break;
+	case PWM_OUTPUT_MODULATED:
+		output_type = "modulated";
+		break;
+	default:
+		break;
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", output_type);
+}
+
 static DEVICE_ATTR_RW(period);
 static DEVICE_ATTR_RW(duty_cycle);
+#ifdef CONFIG_PWM_ROCKCHIP_ONESHOT
+static DEVICE_ATTR_RW(oneshot_count);
+#endif
 static DEVICE_ATTR_RW(enable);
 static DEVICE_ATTR_RW(polarity);
 static DEVICE_ATTR_RO(capture);
+static DEVICE_ATTR_RO(output_type);
 
 static struct attribute *pwm_attrs[] = {
 	&dev_attr_period.attr,
 	&dev_attr_duty_cycle.attr,
+#ifdef CONFIG_PWM_ROCKCHIP_ONESHOT
+	&dev_attr_oneshot_count.attr,
+#endif
 	&dev_attr_enable.attr,
 	&dev_attr_polarity.attr,
 	&dev_attr_capture.attr,
+	&dev_attr_output_type.attr,
 	NULL
 };
 ATTRIBUTE_GROUPS(pwm);
diff --git a/drivers/soc/rockchip/grf.c b/drivers/soc/rockchip/grf.c
index 343ff61cc..14f85382d 100644
--- a/drivers/soc/rockchip/grf.c
+++ b/drivers/soc/rockchip/grf.c
@@ -7,6 +7,7 @@
 
 #include <linux/err.h>
 #include <linux/mfd/syscon.h>
+#include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
@@ -25,6 +26,21 @@ struct rockchip_grf_info {
 	int num_values;
 };
 
+#define PX30_GRF_SOC_CON5		0x414
+
+static const struct rockchip_grf_value px30_defaults[] __initconst = {
+	/*
+	 * Postponing auto jtag/sdmmc switching by 5 seconds.
+	 * The counter value is calculated based on 24MHz clock.
+	 */
+	{ "jtag switching delay", PX30_GRF_SOC_CON5, 0x7270E00},
+};
+
+static const struct rockchip_grf_info px30_grf __initconst = {
+	.values = px30_defaults,
+	.num_values = ARRAY_SIZE(px30_defaults),
+};
+
 #define RK3036_GRF_SOC_CON0		0x140
 
 static const struct rockchip_grf_value rk3036_defaults[] __initconst = {
@@ -86,6 +102,17 @@ static const struct rockchip_grf_info rk3328_grf __initconst = {
 	.num_values = ARRAY_SIZE(rk3328_defaults),
 };
 
+#define RK3308_GRF_SOC_CON3		0x30c
+
+static const struct rockchip_grf_value rk3308_defaults[] __initconst = {
+	{ "uart dma mask", RK3308_GRF_SOC_CON3, HIWORD_UPDATE(0, 0x1f, 10) },
+};
+
+static const struct rockchip_grf_info rk3308_grf __initconst = {
+	.values = rk3308_defaults,
+	.num_values = ARRAY_SIZE(rk3308_defaults),
+};
+
 #define RK3368_GRF_SOC_CON15		0x43c
 
 static const struct rockchip_grf_value rk3368_defaults[] __initconst = {
@@ -108,8 +135,37 @@ static const struct rockchip_grf_info rk3399_grf __initconst = {
 	.num_values = ARRAY_SIZE(rk3399_defaults),
 };
 
+#define DELAY_ONE_SECOND		0x16E3600
+
+#define RV1126_GRF1_SDDETFLT_CON	0x10254
+#define RV1126_GRF1_UART2RX_LOW_CON	0x10258
+#define RV1126_GRF1_IOFUNC_CON1		0x10264
+#define RV1126_GRF1_IOFUNC_CON3		0x1026C
+#define RV1126_JTAG_GROUP0		0x0      /* mux to sdmmc*/
+#define RV1126_JTAG_GROUP1		0x1      /* mux to uart2 */
+#define FORCE_JTAG_ENABLE		0x1
+#define FORCE_JTAG_DISABLE		0x0
+
+static const struct rockchip_grf_value rv1126_defaults[] __initconst = {
+	{ "jtag group0 force", RV1126_GRF1_IOFUNC_CON3,
+		HIWORD_UPDATE(FORCE_JTAG_DISABLE, 1, 4) },
+	{ "jtag group1 force", RV1126_GRF1_IOFUNC_CON3,
+		HIWORD_UPDATE(FORCE_JTAG_DISABLE, 1, 5) },
+	{ "jtag group1 tms low delay", RV1126_GRF1_UART2RX_LOW_CON, DELAY_ONE_SECOND },
+	{ "switch to jtag groupx", RV1126_GRF1_IOFUNC_CON1, HIWORD_UPDATE(RV1126_JTAG_GROUP0, 1, 15) },
+	{ "jtag group0 switching delay", RV1126_GRF1_SDDETFLT_CON, DELAY_ONE_SECOND * 5 },
+};
+
+static const struct rockchip_grf_info rv1126_grf __initconst = {
+	.values = rv1126_defaults,
+	.num_values = ARRAY_SIZE(rv1126_defaults),
+};
+
 static const struct of_device_id rockchip_grf_dt_match[] __initconst = {
 	{
+		.compatible = "rockchip,px30-grf",
+		.data = (void *)&px30_grf,
+	}, {
 		.compatible = "rockchip,rk3036-grf",
 		.data = (void *)&rk3036_grf,
 	}, {
@@ -121,6 +177,9 @@ static const struct of_device_id rockchip_grf_dt_match[] __initconst = {
 	}, {
 		.compatible = "rockchip,rk3288-grf",
 		.data = (void *)&rk3288_grf,
+	}, {
+		.compatible = "rockchip,rk3308-grf",
+		.data = (void *)&rk3308_grf,
 	}, {
 		.compatible = "rockchip,rk3328-grf",
 		.data = (void *)&rk3328_grf,
@@ -130,6 +189,9 @@ static const struct of_device_id rockchip_grf_dt_match[] __initconst = {
 	}, {
 		.compatible = "rockchip,rk3399-grf",
 		.data = (void *)&rk3399_grf,
+	}, {
+		.compatible = "rockchip,rv1126-grf",
+		.data = (void *)&rv1126_grf,
 	},
 	{ /* sentinel */ },
 };
@@ -175,3 +237,6 @@ static int __init rockchip_grf_init(void)
 	return 0;
 }
 postcore_initcall(rockchip_grf_init);
+
+MODULE_DESCRIPTION("Rockchip GRF");
+MODULE_LICENSE("GPL");
diff --git a/drivers/soc/rockchip/io-domain.c b/drivers/soc/rockchip/io-domain.c
index b29e829e8..7bedfdab0 100644
--- a/drivers/soc/rockchip/io-domain.c
+++ b/drivers/soc/rockchip/io-domain.c
@@ -51,6 +51,10 @@
 #define RK3399_PMUGRF_CON0_VSEL		BIT(8)
 #define RK3399_PMUGRF_VSEL_SUPPLY_NUM	9
 
+#define RK3568_PMU_GRF_IO_VSEL0		(0x0140)
+#define RK3568_PMU_GRF_IO_VSEL1		(0x0144)
+#define RK3568_PMU_GRF_IO_VSEL2		(0x0148)
+
 struct rockchip_iodomain;
 
 /**
@@ -74,8 +78,51 @@ struct rockchip_iodomain {
 	struct regmap *grf;
 	const struct rockchip_iodomain_soc_data *soc_data;
 	struct rockchip_iodomain_supply supplies[MAX_SUPPLIES];
+	int (*write)(struct rockchip_iodomain_supply *supply, int uV);
 };
 
+static int rk3568_pmu_iodomain_write(struct rockchip_iodomain_supply *supply,
+				     int uV)
+{
+	struct rockchip_iodomain *iod = supply->iod;
+	u32 is_3v3 = uV > MAX_VOLTAGE_1_8;
+	u32 val0, val1;
+	int b;
+
+	switch (supply->idx) {
+	case 0: /* pmuio1 */
+		break;
+	case 1: /* pmuio2 */
+		b = supply->idx;
+		val0 = BIT(16 + b) | (is_3v3 ? 0 : BIT(b));
+		b = supply->idx + 4;
+		val1 = BIT(16 + b) | (is_3v3 ? BIT(b) : 0);
+
+		regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL2, val0);
+		regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL2, val1);
+		break;
+	case 3: /* vccio2 */
+		break;
+	case 2: /* vccio1 */
+	case 4: /* vccio3 */
+	case 5: /* vccio4 */
+	case 6: /* vccio5 */
+	case 7: /* vccio6 */
+	case 8: /* vccio7 */
+		b = supply->idx - 1;
+		val0 = BIT(16 + b) | (is_3v3 ? 0 : BIT(b));
+		val1 = BIT(16 + b) | (is_3v3 ? BIT(b) : 0);
+
+		regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL0, val0);
+		regmap_write(iod->grf, RK3568_PMU_GRF_IO_VSEL1, val1);
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	return 0;
+}
+
 static int rockchip_iodomain_write(struct rockchip_iodomain_supply *supply,
 				   int uV)
 {
@@ -139,7 +186,7 @@ static int rockchip_iodomain_notify(struct notifier_block *nb,
 			return NOTIFY_BAD;
 	}
 
-	ret = rockchip_iodomain_write(supply, uV);
+	ret = supply->iod->write(supply, uV);
 	if (ret && event == REGULATOR_EVENT_PRE_VOLTAGE_CHANGE)
 		return NOTIFY_BAD;
 
@@ -401,6 +448,21 @@ static const struct rockchip_iodomain_soc_data soc_data_rk3399_pmu = {
 	.init = rk3399_pmu_iodomain_init,
 };
 
+static const struct rockchip_iodomain_soc_data soc_data_rk3568_pmu = {
+	.grf_offset = 0x140,
+	.supply_names = {
+		"pmuio1",
+		"pmuio2",
+		"vccio1",
+		"vccio2",
+		"vccio3",
+		"vccio4",
+		"vccio5",
+		"vccio6",
+		"vccio7",
+	},
+};
+
 static const struct rockchip_iodomain_soc_data soc_data_rv1108 = {
 	.grf_offset = 0x404,
 	.supply_names = {
@@ -431,6 +493,22 @@ static const struct rockchip_iodomain_soc_data soc_data_rv1108_pmu = {
 	},
 };
 
+static const struct rockchip_iodomain_soc_data soc_data_rv1126_pmu = {
+	.grf_offset = 0x140,
+	.supply_names = {
+		NULL,
+		"vccio1",
+		"vccio2",
+		"vccio3",
+		"vccio4",
+		"vccio5",
+		"vccio6",
+		"vccio7",
+		"pmuio0",
+		"pmuio1",
+	},
+};
+
 static const struct of_device_id rockchip_iodomain_match[] = {
 	{
 		.compatible = "rockchip,px30-io-voltage-domain",
@@ -472,6 +550,10 @@ static const struct of_device_id rockchip_iodomain_match[] = {
 		.compatible = "rockchip,rk3399-pmu-io-voltage-domain",
 		.data = &soc_data_rk3399_pmu
 	},
+	{
+		.compatible = "rockchip,rk3568-pmu-io-voltage-domain",
+		.data = &soc_data_rk3568_pmu
+	},
 	{
 		.compatible = "rockchip,rv1108-io-voltage-domain",
 		.data = &soc_data_rv1108
@@ -480,6 +562,10 @@ static const struct of_device_id rockchip_iodomain_match[] = {
 		.compatible = "rockchip,rv1108-pmu-io-voltage-domain",
 		.data = &soc_data_rv1108_pmu
 	},
+	{
+		.compatible = "rockchip,rv1126-pmu-io-voltage-domain",
+		.data = &soc_data_rv1126_pmu
+	},
 	{ /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, rockchip_iodomain_match);
@@ -505,6 +591,11 @@ static int rockchip_iodomain_probe(struct platform_device *pdev)
 	match = of_match_node(rockchip_iodomain_match, np);
 	iod->soc_data = match->data;
 
+	if (match->data == &soc_data_rk3568_pmu)
+		iod->write = rk3568_pmu_iodomain_write;
+	else
+		iod->write = rockchip_iodomain_write;
+
 	parent = pdev->dev.parent;
 	if (parent && parent->of_node) {
 		iod->grf = syscon_node_to_regmap(parent->of_node);
@@ -565,7 +656,7 @@ static int rockchip_iodomain_probe(struct platform_device *pdev)
 		supply->reg = reg;
 		supply->nb.notifier_call = rockchip_iodomain_notify;
 
-		ret = rockchip_iodomain_write(supply, uV);
+		ret = iod->write(supply, uV);
 		if (ret) {
 			supply->reg = NULL;
 			goto unreg_notify;
diff --git a/drivers/soc/rockchip/pm_domains.c b/drivers/soc/rockchip/pm_domains.c
index 54eb6cfc5..f0484cf2e 100644
--- a/drivers/soc/rockchip/pm_domains.c
+++ b/drivers/soc/rockchip/pm_domains.c
@@ -5,6 +5,7 @@
  * Copyright (c) 2015 ROCKCHIP, Co. Ltd.
  */
 
+#include <linux/module.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
 #include <linux/err.h>
@@ -15,8 +16,15 @@
 #include <linux/of_platform.h>
 #include <linux/clk.h>
 #include <linux/regmap.h>
+#include <linux/slab.h>
 #include <linux/mfd/syscon.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <soc/rockchip/pm_domains.h>
+#include <soc/rockchip/rockchip_dmc.h>
 #include <dt-bindings/power/px30-power.h>
+#include <dt-bindings/power/rv1126-power.h>
+#include <dt-bindings/power/rk1808-power.h>
 #include <dt-bindings/power/rk3036-power.h>
 #include <dt-bindings/power/rk3066-power.h>
 #include <dt-bindings/power/rk3128-power.h>
@@ -27,8 +35,11 @@
 #include <dt-bindings/power/rk3366-power.h>
 #include <dt-bindings/power/rk3368-power.h>
 #include <dt-bindings/power/rk3399-power.h>
+#include <dt-bindings/power/rk3568-power.h>
+#include <dt-bindings/power/rk3588-power.h>
 
 struct rockchip_domain_info {
+	const char *name;
 	int pwr_mask;
 	int status_mask;
 	int req_mask;
@@ -37,6 +48,10 @@ struct rockchip_domain_info {
 	bool active_wakeup;
 	int pwr_w_mask;
 	int req_w_mask;
+	int repair_status_mask;
+	bool keepon_startup;
+	u32 pwr_offset;
+	u32 req_offset;
 };
 
 struct rockchip_pmu_info {
@@ -45,6 +60,7 @@ struct rockchip_pmu_info {
 	u32 req_offset;
 	u32 idle_offset;
 	u32 ack_offset;
+	u32 repair_status_offset;
 
 	u32 core_pwrcnt_offset;
 	u32 gpu_pwrcnt_offset;
@@ -72,6 +88,9 @@ struct rockchip_pm_domain {
 	u32 *qos_save_regs[MAX_QOS_REGS_NUM];
 	int num_clks;
 	struct clk_bulk_data *clks;
+	bool is_ignore_pwr;
+	bool is_qos_saved;
+	struct regulator *supply;
 };
 
 struct rockchip_pmu {
@@ -83,32 +102,89 @@ struct rockchip_pmu {
 	struct generic_pm_domain *domains[];
 };
 
+static struct rockchip_pmu *g_pmu;
+static bool pm_domain_always_on;
+
+module_param_named(always_on, pm_domain_always_on, bool, 0644);
+MODULE_PARM_DESC(always_on,
+		 "Always keep pm domains power on except for system suspend.");
+
+static void rockchip_pmu_lock(struct rockchip_pm_domain *pd)
+{
+	mutex_lock(&pd->pmu->mutex);
+	rockchip_dmcfreq_lock_nested();
+}
+
+static void rockchip_pmu_unlock(struct rockchip_pm_domain *pd)
+{
+	rockchip_dmcfreq_unlock();
+	mutex_unlock(&pd->pmu->mutex);
+}
+
 #define to_rockchip_pd(gpd) container_of(gpd, struct rockchip_pm_domain, genpd)
 
-#define DOMAIN(pwr, status, req, idle, ack, wakeup)	\
+#define DOMAIN(_name, pwr, status, req, idle, ack, wakeup, keepon)	\
 {							\
+	.name = _name,					\
 	.pwr_mask = (pwr),				\
 	.status_mask = (status),			\
 	.req_mask = (req),				\
 	.idle_mask = (idle),				\
 	.ack_mask = (ack),				\
 	.active_wakeup = (wakeup),			\
+	.keepon_startup = (keepon),			\
+}
+
+#define DOMAIN_M(_name, pwr, status, req, idle, ack, wakeup, keepon)	\
+{							\
+	.name = _name,					\
+	.pwr_w_mask = (pwr) << 16,			\
+	.pwr_mask = (pwr),				\
+	.status_mask = (status),			\
+	.req_w_mask = (req) << 16,			\
+	.req_mask = (req),				\
+	.idle_mask = (idle),				\
+	.ack_mask = (ack),				\
+	.active_wakeup = wakeup,			\
+	.keepon_startup = keepon,			\
+}
+
+#define DOMAIN_M_O(_name, pwr, status, p_offset, req, idle, ack, r_offset, wakeup, keepon)	\
+{							\
+	.name = _name,					\
+	.pwr_w_mask = (pwr) << 16,			\
+	.pwr_mask = (pwr),				\
+	.status_mask = (status),			\
+	.req_w_mask = (req) << 16,			\
+	.req_mask = (req),				\
+	.idle_mask = (idle),				\
+	.ack_mask = (ack),				\
+	.active_wakeup = wakeup,			\
+	.keepon_startup = keepon,			\
+	.pwr_offset = p_offset,				\
+	.req_offset = r_offset,				\
 }
 
-#define DOMAIN_M(pwr, status, req, idle, ack, wakeup)	\
+#define DOMAIN_M_O_R(_name, p_offset, pwr, status, r_status, r_offset, req, idle, ack, wakeup, keepon)	\
 {							\
+	.name = _name,					\
+	.pwr_offset = p_offset,				\
 	.pwr_w_mask = (pwr) << 16,			\
 	.pwr_mask = (pwr),				\
 	.status_mask = (status),			\
+	.repair_status_mask = (r_status),		\
+	.req_offset = r_offset,				\
 	.req_w_mask = (req) << 16,			\
 	.req_mask = (req),				\
 	.idle_mask = (idle),				\
 	.ack_mask = (ack),				\
 	.active_wakeup = wakeup,			\
+	.keepon_startup = keepon,			\
 }
 
-#define DOMAIN_RK3036(req, ack, idle, wakeup)		\
+#define DOMAIN_RK3036(_name, req, ack, idle, wakeup)	\
 {							\
+	.name = _name,					\
 	.req_mask = (req),				\
 	.req_w_mask = (req) << 16,			\
 	.ack_mask = (ack),				\
@@ -116,20 +192,53 @@ struct rockchip_pmu {
 	.active_wakeup = wakeup,			\
 }
 
-#define DOMAIN_PX30(pwr, status, req, wakeup)		\
-	DOMAIN_M(pwr, status, req, (req) << 16, req, wakeup)
+#define DOMAIN_PX30(name, pwr, status, req, wakeup)		\
+	DOMAIN_M(name, pwr, status, req, (req) << 16, req, wakeup, false)
+
+#define DOMAIN_PX30_PROTECT(name, pwr, status, req, wakeup)	\
+	DOMAIN_M(name, pwr, status, req, (req) << 16, req, wakeup, true)
+
+#define DOMAIN_RV1126(name, pwr, req, idle, wakeup)		\
+	DOMAIN_M(name, pwr, pwr, req, idle, idle, wakeup, false)
+
+#define DOMAIN_RV1126_PROTECT(name, pwr, req, idle, wakeup)	\
+	DOMAIN_M(name, pwr, pwr, req, idle, idle, wakeup, true)
+
+#define DOMAIN_RV1126_O(name, pwr, req, idle, r_offset, wakeup)	\
+	DOMAIN_M_O(name, pwr, pwr, 0, req, idle, idle, r_offset, wakeup, false)
+
+#define DOMAIN_RK3288(name, pwr, status, req, wakeup)		\
+	DOMAIN(name, pwr, status, req, req, (req) << 16, wakeup, false)
+
+#define DOMAIN_RK3288_PROTECT(name, pwr, status, req, wakeup)	\
+	DOMAIN(name, pwr, status, req, req, (req) << 16, wakeup, true)
 
-#define DOMAIN_RK3288(pwr, status, req, wakeup)		\
-	DOMAIN(pwr, status, req, req, (req) << 16, wakeup)
+#define DOMAIN_RK3328(name, pwr, status, req, wakeup)		\
+	DOMAIN_M(name, pwr, pwr, req, (req) << 10, req, wakeup, false)
 
-#define DOMAIN_RK3328(pwr, status, req, wakeup)		\
-	DOMAIN_M(pwr, pwr, req, (req) << 10, req, wakeup)
+#define DOMAIN_RK3368(name, pwr, status, req, wakeup)		\
+	DOMAIN(name, pwr, status, req, (req) << 16, req, wakeup, false)
 
-#define DOMAIN_RK3368(pwr, status, req, wakeup)		\
-	DOMAIN(pwr, status, req, (req) << 16, req, wakeup)
+#define DOMAIN_RK3368_PROTECT(name, pwr, status, req, wakeup)	\
+	DOMAIN(name, pwr, status, req, (req) << 16, req, wakeup, true)
 
-#define DOMAIN_RK3399(pwr, status, req, wakeup)		\
-	DOMAIN(pwr, status, req, req, req, wakeup)
+#define DOMAIN_RK3399(name, pwr, status, req, wakeup)		\
+	DOMAIN(name, pwr, status, req, req, req, wakeup, false)
+
+#define DOMAIN_RK3399_PROTECT(name, pwr, status, req, wakeup)	\
+	DOMAIN(name, pwr, status, req, req, req, wakeup, true)
+
+#define DOMAIN_RK3568(name, pwr, req, wakeup)			\
+	DOMAIN_M(name, pwr, pwr, req, req, req, wakeup, false)
+
+#define DOMAIN_RK3568_PROTECT(name, pwr, req, wakeup)		\
+	DOMAIN_M(name, pwr, pwr, req, req, req, wakeup, true)
+
+#define DOMAIN_RK3588(name, p_offset, pwr, status, r_status, r_offset, req, idle, wakeup)	\
+	DOMAIN_M_O_R(name, p_offset, pwr, status, r_status, r_offset, req, idle, idle, wakeup, false)
+
+#define DOMAIN_RK3588_P(name, p_offset, pwr, status, r_status, r_offset, req, idle, wakeup)	\
+	DOMAIN_M_O_R(name, p_offset, pwr, status, r_status, r_offset, req, idle, idle, wakeup, true)
 
 static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd)
 {
@@ -155,20 +264,25 @@ static int rockchip_pmu_set_idle_request(struct rockchip_pm_domain *pd,
 	const struct rockchip_domain_info *pd_info = pd->info;
 	struct generic_pm_domain *genpd = &pd->genpd;
 	struct rockchip_pmu *pmu = pd->pmu;
+	u32 pd_req_offset = 0;
 	unsigned int target_ack;
 	unsigned int val;
 	bool is_idle;
-	int ret;
+	int ret = 0;
+
+	if (pd_info->req_offset)
+		pd_req_offset = pd_info->req_offset;
 
 	if (pd_info->req_mask == 0)
 		return 0;
 	else if (pd_info->req_w_mask)
-		regmap_write(pmu->regmap, pmu->info->req_offset,
+		regmap_write(pmu->regmap, pmu->info->req_offset + pd_req_offset,
 			     idle ? (pd_info->req_mask | pd_info->req_w_mask) :
 			     pd_info->req_w_mask);
 	else
-		regmap_update_bits(pmu->regmap, pmu->info->req_offset,
-				   pd_info->req_mask, idle ? -1U : 0);
+		regmap_update_bits(pmu->regmap, pmu->info->req_offset +
+				   pd_req_offset, pd_info->req_mask,
+				   idle ? -1U : 0);
 
 	dsb(sy);
 
@@ -179,23 +293,49 @@ static int rockchip_pmu_set_idle_request(struct rockchip_pm_domain *pd,
 					0, 10000);
 	if (ret) {
 		dev_err(pmu->dev,
-			"failed to get ack on domain '%s', val=0x%x\n",
-			genpd->name, val);
-		return ret;
+			"failed to get ack on domain '%s', target_idle = %d, target_ack = %d, val=0x%x\n",
+			genpd->name, idle, target_ack, val);
+		goto error;
 	}
 
 	ret = readx_poll_timeout_atomic(rockchip_pmu_domain_is_idle, pd,
 					is_idle, is_idle == idle, 0, 10000);
 	if (ret) {
 		dev_err(pmu->dev,
-			"failed to set idle on domain '%s', val=%d\n",
-			genpd->name, is_idle);
-		return ret;
+			"failed to set idle on domain '%s',  target_idle = %d, val=%d\n",
+			genpd->name, idle, is_idle);
+		goto error;
 	}
 
-	return 0;
+	return ret;
+error:
+	panic("panic_on_set_idle set ...\n");
+	return ret;
 }
 
+int rockchip_pmu_idle_request(struct device *dev, bool idle)
+{
+	struct generic_pm_domain *genpd;
+	struct rockchip_pm_domain *pd;
+	int ret;
+
+	if (IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = pd_to_genpd(dev->pm_domain);
+	pd = to_rockchip_pd(genpd);
+
+	rockchip_pmu_lock(pd);
+	ret = rockchip_pmu_set_idle_request(pd, idle);
+	rockchip_pmu_unlock(pd);
+
+	return ret;
+}
+EXPORT_SYMBOL(rockchip_pmu_idle_request);
+
 static int rockchip_pmu_save_qos(struct rockchip_pm_domain *pd)
 {
 	int i;
@@ -245,11 +385,63 @@ static int rockchip_pmu_restore_qos(struct rockchip_pm_domain *pd)
 	return 0;
 }
 
+int rockchip_save_qos(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	struct rockchip_pm_domain *pd;
+	int ret;
+
+	if (IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = pd_to_genpd(dev->pm_domain);
+	pd = to_rockchip_pd(genpd);
+
+	rockchip_pmu_lock(pd);
+	ret = rockchip_pmu_save_qos(pd);
+	rockchip_pmu_unlock(pd);
+
+	return ret;
+}
+EXPORT_SYMBOL(rockchip_save_qos);
+
+int rockchip_restore_qos(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	struct rockchip_pm_domain *pd;
+	int ret;
+
+	if (IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = pd_to_genpd(dev->pm_domain);
+	pd = to_rockchip_pd(genpd);
+
+	rockchip_pmu_lock(pd);
+	ret = rockchip_pmu_restore_qos(pd);
+	rockchip_pmu_unlock(pd);
+
+	return ret;
+}
+EXPORT_SYMBOL(rockchip_restore_qos);
+
 static bool rockchip_pmu_domain_is_on(struct rockchip_pm_domain *pd)
 {
 	struct rockchip_pmu *pmu = pd->pmu;
 	unsigned int val;
 
+	if (pd->info->repair_status_mask) {
+		regmap_read(pmu->regmap, pmu->info->repair_status_offset, &val);
+		/* 1'b1: power on, 1'b0: power off */
+		return val & pd->info->repair_status_mask;
+	}
+
 	/* check idle status for idle-only domains */
 	if (pd->info->status_mask == 0)
 		return !rockchip_pmu_domain_is_idle(pd);
@@ -260,76 +452,131 @@ static bool rockchip_pmu_domain_is_on(struct rockchip_pm_domain *pd)
 	return !(val & pd->info->status_mask);
 }
 
-static void rockchip_do_pmu_set_power_domain(struct rockchip_pm_domain *pd,
-					     bool on)
+static int rockchip_do_pmu_set_power_domain(struct rockchip_pm_domain *pd,
+					    bool on)
 {
 	struct rockchip_pmu *pmu = pd->pmu;
 	struct generic_pm_domain *genpd = &pd->genpd;
+	u32 pd_pwr_offset = 0;
 	bool is_on;
+	int ret = 0;
+
+	if (pd->info->pwr_offset)
+		pd_pwr_offset = pd->info->pwr_offset;
 
 	if (pd->info->pwr_mask == 0)
-		return;
+		return 0;
 	else if (pd->info->pwr_w_mask)
-		regmap_write(pmu->regmap, pmu->info->pwr_offset,
+		regmap_write(pmu->regmap, pmu->info->pwr_offset + pd_pwr_offset,
 			     on ? pd->info->pwr_w_mask :
 			     (pd->info->pwr_mask | pd->info->pwr_w_mask));
 	else
-		regmap_update_bits(pmu->regmap, pmu->info->pwr_offset,
-				   pd->info->pwr_mask, on ? 0 : -1U);
+		regmap_update_bits(pmu->regmap, pmu->info->pwr_offset +
+				   pd_pwr_offset, pd->info->pwr_mask,
+				   on ? 0 : -1U);
 
 	dsb(sy);
 
-	if (readx_poll_timeout_atomic(rockchip_pmu_domain_is_on, pd, is_on,
-				      is_on == on, 0, 10000)) {
+	ret = readx_poll_timeout_atomic(rockchip_pmu_domain_is_on, pd, is_on,
+					is_on == on, 0, 10000);
+	if (ret) {
 		dev_err(pmu->dev,
-			"failed to set domain '%s', val=%d\n",
-			genpd->name, is_on);
-		return;
+			"failed to set domain '%s', target_on= %d, val=%d\n",
+			genpd->name, on, is_on);
+			goto error;
 	}
+	return ret;
+
+error:
+	panic("panic_on_set_domain set ...\n");
+	return ret;
 }
 
 static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on)
 {
 	struct rockchip_pmu *pmu = pd->pmu;
-	int ret;
+	int ret = 0;
+	struct generic_pm_domain *genpd = &pd->genpd;
+
+	if (pm_domain_always_on && !power_on)
+		return 0;
 
-	mutex_lock(&pmu->mutex);
+	rockchip_pmu_lock(pd);
 
 	if (rockchip_pmu_domain_is_on(pd) != power_on) {
+		if (IS_ERR_OR_NULL(pd->supply) &&
+		    PTR_ERR(pd->supply) != -ENODEV)
+			pd->supply = devm_regulator_get_optional(pd->pmu->dev,
+								 genpd->name);
+
+		if (power_on && !IS_ERR(pd->supply)) {
+			ret = regulator_enable(pd->supply);
+			if (ret < 0) {
+				dev_err(pd->pmu->dev, "failed to set vdd supply enable '%s',\n",
+					genpd->name);
+				rockchip_pmu_unlock(pd);
+				return ret;
+			}
+		}
+
 		ret = clk_bulk_enable(pd->num_clks, pd->clks);
 		if (ret < 0) {
 			dev_err(pmu->dev, "failed to enable clocks\n");
-			mutex_unlock(&pmu->mutex);
+			rockchip_pmu_unlock(pd);
 			return ret;
 		}
 
 		if (!power_on) {
 			rockchip_pmu_save_qos(pd);
+			pd->is_qos_saved = true;
 
 			/* if powering down, idle request to NIU first */
-			rockchip_pmu_set_idle_request(pd, true);
+			ret = rockchip_pmu_set_idle_request(pd, true);
+			if (ret) {
+				dev_err(pd->pmu->dev, "failed to set idle request '%s',\n",
+					genpd->name);
+				goto out;
+			}
 		}
 
-		rockchip_do_pmu_set_power_domain(pd, power_on);
+		ret = rockchip_do_pmu_set_power_domain(pd, power_on);
+		if (ret) {
+			dev_err(pd->pmu->dev, "failed to set power '%s' = %d,\n",
+				genpd->name, power_on);
+			goto out;
+		}
 
 		if (power_on) {
 			/* if powering up, leave idle mode */
-			rockchip_pmu_set_idle_request(pd, false);
+			ret = rockchip_pmu_set_idle_request(pd, false);
+			if (ret) {
+				dev_err(pd->pmu->dev, "failed to set deidle request '%s',\n",
+					genpd->name);
+				goto out;
+			}
 
-			rockchip_pmu_restore_qos(pd);
+			if (pd->is_qos_saved)
+				rockchip_pmu_restore_qos(pd);
 		}
 
+out:
 		clk_bulk_disable(pd->num_clks, pd->clks);
+
+		if (!power_on && !IS_ERR(pd->supply))
+			ret = regulator_disable(pd->supply);
 	}
 
-	mutex_unlock(&pmu->mutex);
-	return 0;
+	rockchip_pmu_unlock(pd);
+	return ret;
 }
 
 static int rockchip_pd_power_on(struct generic_pm_domain *domain)
 {
 	struct rockchip_pm_domain *pd = to_rockchip_pd(domain);
 
+	if (pd->is_ignore_pwr)
+		return 0;
+
 	return rockchip_pd_power(pd, true);
 }
 
@@ -337,8 +584,70 @@ static int rockchip_pd_power_off(struct generic_pm_domain *domain)
 {
 	struct rockchip_pm_domain *pd = to_rockchip_pd(domain);
 
+	if (pd->is_ignore_pwr)
+		return 0;
+
+	return rockchip_pd_power(pd, false);
+}
+
+int rockchip_pmu_pd_on(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	struct rockchip_pm_domain *pd;
+
+	if (IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = pd_to_genpd(dev->pm_domain);
+	pd = to_rockchip_pd(genpd);
+
+	return rockchip_pd_power(pd, true);
+}
+EXPORT_SYMBOL(rockchip_pmu_pd_on);
+
+int rockchip_pmu_pd_off(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	struct rockchip_pm_domain *pd;
+
+	if (IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = pd_to_genpd(dev->pm_domain);
+	pd = to_rockchip_pd(genpd);
+
 	return rockchip_pd_power(pd, false);
 }
+EXPORT_SYMBOL(rockchip_pmu_pd_off);
+
+bool rockchip_pmu_pd_is_on(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	struct rockchip_pm_domain *pd;
+	bool is_on;
+
+	if (IS_ERR_OR_NULL(dev))
+		return false;
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return false;
+
+	genpd = pd_to_genpd(dev->pm_domain);
+	pd = to_rockchip_pd(genpd);
+
+	rockchip_pmu_lock(pd);
+	is_on = rockchip_pmu_domain_is_on(pd);
+	rockchip_pmu_unlock(pd);
+
+	return is_on;
+}
+EXPORT_SYMBOL(rockchip_pmu_pd_is_on);
 
 static int rockchip_pd_attach_dev(struct generic_pm_domain *genpd,
 				  struct device *dev)
@@ -378,15 +687,58 @@ static void rockchip_pd_detach_dev(struct generic_pm_domain *genpd,
 	pm_clk_destroy(dev);
 }
 
+static void rockchip_pd_qos_init(struct rockchip_pm_domain *pd,
+				 bool **qos_is_need_init)
+{
+	int i, is_pd_on;
+
+	is_pd_on = rockchip_pmu_domain_is_on(pd);
+	if (!is_pd_on)
+		rockchip_pd_power(pd, true);
+
+	for (i = 0; i < pd->num_qos; i++) {
+		if (qos_is_need_init[0][i])
+			regmap_write(pd->qos_regmap[i],
+				     QOS_PRIORITY,
+				     pd->qos_save_regs[0][i]);
+
+		if (qos_is_need_init[1][i])
+			regmap_write(pd->qos_regmap[i],
+				     QOS_MODE,
+				     pd->qos_save_regs[1][i]);
+
+		if (qos_is_need_init[2][i])
+			regmap_write(pd->qos_regmap[i],
+				     QOS_BANDWIDTH,
+				     pd->qos_save_regs[2][i]);
+
+		if (qos_is_need_init[3][i])
+			regmap_write(pd->qos_regmap[i],
+				     QOS_SATURATION,
+				     pd->qos_save_regs[3][i]);
+
+		if (qos_is_need_init[4][i])
+			regmap_write(pd->qos_regmap[i],
+				     QOS_EXTCONTROL,
+				     pd->qos_save_regs[4][i]);
+	}
+
+	if (!is_pd_on)
+		rockchip_pd_power(pd, false);
+}
+
 static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
 				      struct device_node *node)
 {
 	const struct rockchip_domain_info *pd_info;
 	struct rockchip_pm_domain *pd;
 	struct device_node *qos_node;
+	int num_qos = 0, num_qos_reg = 0;
 	int i, j;
-	u32 id;
+	u32 id, val;
 	int error;
+	bool *qos_is_need_init[MAX_QOS_REGS_NUM] = { NULL };
+	bool is_qos_need_init = false;
 
 	error = of_property_read_u32(node, "reg", &id);
 	if (error) {
@@ -401,6 +753,8 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
 			node, id);
 		return -EINVAL;
 	}
+	if (pmu->genpd_data.domains[id])
+		return 0;
 
 	pd_info = &pmu->info->domain_info[id];
 	if (!pd_info) {
@@ -415,6 +769,8 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
 
 	pd->info = pd_info;
 	pd->pmu = pmu;
+	if (!pd_info->pwr_mask)
+		pd->is_ignore_pwr = true;
 
 	pd->num_clks = of_clk_get_parent_count(node);
 	if (pd->num_clks > 0) {
@@ -443,8 +799,14 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
 	if (error)
 		goto err_put_clocks;
 
-	pd->num_qos = of_count_phandle_with_args(node, "pm_qos",
-						 NULL);
+	num_qos = of_count_phandle_with_args(node, "pm_qos", NULL);
+
+	for (j = 0; j < num_qos; j++) {
+		qos_node = of_parse_phandle(node, "pm_qos", j);
+		if (qos_node && of_device_is_available(qos_node))
+			pd->num_qos++;
+		of_node_put(qos_node);
+	}
 
 	if (pd->num_qos > 0) {
 		pd->qos_regmap = devm_kcalloc(pmu->dev, pd->num_qos,
@@ -455,55 +817,127 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
 			goto err_unprepare_clocks;
 		}
 
-		for (j = 0; j < MAX_QOS_REGS_NUM; j++) {
-			pd->qos_save_regs[j] = devm_kcalloc(pmu->dev,
-							    pd->num_qos,
-							    sizeof(u32),
-							    GFP_KERNEL);
-			if (!pd->qos_save_regs[j]) {
-				error = -ENOMEM;
-				goto err_unprepare_clocks;
-			}
+		pd->qos_save_regs[0] = (u32 *)devm_kmalloc(pmu->dev,
+							   sizeof(u32) *
+							   MAX_QOS_REGS_NUM *
+							   pd->num_qos,
+							   GFP_KERNEL);
+		if (!pd->qos_save_regs[0]) {
+			error = -ENOMEM;
+			goto err_unprepare_clocks;
+		}
+		qos_is_need_init[0] = kzalloc(sizeof(bool) *
+					      MAX_QOS_REGS_NUM *
+					      pd->num_qos,
+					      GFP_KERNEL);
+		if (!qos_is_need_init[0]) {
+			error = -ENOMEM;
+			goto err_unprepare_clocks;
+		}
+		for (i = 1; i < MAX_QOS_REGS_NUM; i++) {
+			pd->qos_save_regs[i] = pd->qos_save_regs[i - 1] +
+					       num_qos;
+			qos_is_need_init[i] = qos_is_need_init[i - 1] + num_qos;
 		}
 
-		for (j = 0; j < pd->num_qos; j++) {
+		for (j = 0; j < num_qos; j++) {
 			qos_node = of_parse_phandle(node, "pm_qos", j);
 			if (!qos_node) {
 				error = -ENODEV;
 				goto err_unprepare_clocks;
 			}
-			pd->qos_regmap[j] = syscon_node_to_regmap(qos_node);
-			if (IS_ERR(pd->qos_regmap[j])) {
-				error = -ENODEV;
-				of_node_put(qos_node);
-				goto err_unprepare_clocks;
+			if (of_device_is_available(qos_node)) {
+				pd->qos_regmap[num_qos_reg] =
+					syscon_node_to_regmap(qos_node);
+				if (IS_ERR(pd->qos_regmap[num_qos_reg])) {
+					error = -ENODEV;
+					of_node_put(qos_node);
+					goto err_unprepare_clocks;
+				}
+				if (!of_property_read_u32(qos_node,
+							  "priority-init",
+							  &val)) {
+					pd->qos_save_regs[0][j] = val;
+					qos_is_need_init[0][j] = true;
+					is_qos_need_init = true;
+				}
+
+				if (!of_property_read_u32(qos_node,
+							  "mode-init",
+							  &val)) {
+					pd->qos_save_regs[1][j] = val;
+					qos_is_need_init[1][j] = true;
+					is_qos_need_init = true;
+				}
+
+				if (!of_property_read_u32(qos_node,
+							  "bandwidth-init",
+							  &val)) {
+					pd->qos_save_regs[2][j] = val;
+					qos_is_need_init[2][j] = true;
+					is_qos_need_init = true;
+				}
+
+				if (!of_property_read_u32(qos_node,
+							  "saturation-init",
+							  &val)) {
+					pd->qos_save_regs[3][j] = val;
+					qos_is_need_init[3][j] = true;
+					is_qos_need_init = true;
+				}
+
+				if (!of_property_read_u32(qos_node,
+							  "extcontrol-init",
+							  &val)) {
+					pd->qos_save_regs[4][j] = val;
+					qos_is_need_init[4][j] = true;
+					is_qos_need_init = true;
+				}
+
+				num_qos_reg++;
 			}
 			of_node_put(qos_node);
+			if (num_qos_reg > pd->num_qos)
+				goto err_unprepare_clocks;
 		}
 	}
 
-	error = rockchip_pd_power(pd, true);
-	if (error) {
-		dev_err(pmu->dev,
-			"failed to power on domain '%pOFn': %d\n",
-			node, error);
-		goto err_unprepare_clocks;
-	}
-
-	pd->genpd.name = node->name;
+	if (pd->info->name)
+		pd->genpd.name = pd->info->name;
+	else
+		pd->genpd.name = kbasename(node->full_name);
 	pd->genpd.power_off = rockchip_pd_power_off;
 	pd->genpd.power_on = rockchip_pd_power_on;
 	pd->genpd.attach_dev = rockchip_pd_attach_dev;
 	pd->genpd.detach_dev = rockchip_pd_detach_dev;
-	pd->genpd.flags = GENPD_FLAG_PM_CLK;
 	if (pd_info->active_wakeup)
 		pd->genpd.flags |= GENPD_FLAG_ACTIVE_WAKEUP;
-	pm_genpd_init(&pd->genpd, NULL, false);
+#ifndef MODULE
+	if (pd_info->keepon_startup) {
+		pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
+		if (!rockchip_pmu_domain_is_on(pd)) {
+			error = rockchip_pd_power(pd, true);
+			if (error) {
+				dev_err(pmu->dev,
+					"failed to power on domain '%s': %d\n",
+					node->name, error);
+				goto err_unprepare_clocks;
+			}
+		}
+	}
+#endif
+	if (is_qos_need_init)
+		rockchip_pd_qos_init(pd, &qos_is_need_init[0]);
+
+	kfree(qos_is_need_init[0]);
+
+	pm_genpd_init(&pd->genpd, NULL, !rockchip_pmu_domain_is_on(pd));
 
 	pmu->genpd_data.domains[id] = &pd->genpd;
 	return 0;
 
 err_unprepare_clocks:
+	kfree(qos_is_need_init[0]);
 	clk_bulk_unprepare(pd->num_clks, pd->clks);
 err_put_clocks:
 	clk_bulk_put(pd->num_clks, pd->clks);
@@ -527,9 +961,9 @@ static void rockchip_pm_remove_one_domain(struct rockchip_pm_domain *pd)
 	clk_bulk_put(pd->num_clks, pd->clks);
 
 	/* protect the zeroing of pm->num_clks */
-	mutex_lock(&pd->pmu->mutex);
+	rockchip_pmu_lock(pd);
 	pd->num_clks = 0;
-	mutex_unlock(&pd->pmu->mutex);
+	rockchip_pmu_unlock(pd);
 
 	/* devm will free our memory */
 }
@@ -566,6 +1000,7 @@ static int rockchip_pm_add_subdomain(struct rockchip_pmu *pmu,
 {
 	struct device_node *np;
 	struct generic_pm_domain *child_domain, *parent_domain;
+	struct rockchip_pm_domain *child_pd, *parent_pd;
 	int error;
 
 	for_each_child_of_node(parent, np) {
@@ -606,6 +1041,17 @@ static int rockchip_pm_add_subdomain(struct rockchip_pmu *pmu,
 				parent_domain->name, child_domain->name);
 		}
 
+		/*
+		 * If child_pd doesn't do idle request or power on/off,
+		 * parent_pd may fail to do power on/off, so if parent_pd
+		 * need to power on/off, child_pd can't ignore to do idle
+		 * request and power on/off.
+		 */
+		child_pd = to_rockchip_pd(child_domain);
+		parent_pd = to_rockchip_pd(parent_domain);
+		if (!parent_pd->is_ignore_pwr)
+			child_pd->is_ignore_pwr = false;
+
 		rockchip_pm_add_subdomain(pmu, np);
 	}
 
@@ -616,6 +1062,75 @@ static int rockchip_pm_add_subdomain(struct rockchip_pmu *pmu,
 	return error;
 }
 
+#ifndef MODULE
+static void rockchip_pd_keepon_do_release(struct generic_pm_domain *genpd,
+					  struct rockchip_pm_domain *pd)
+{
+	struct pm_domain_data *pm_data;
+	int enable_count;
+
+	pd->genpd.flags &= (~GENPD_FLAG_ALWAYS_ON);
+	list_for_each_entry(pm_data, &genpd->dev_list, list_node) {
+		if (!atomic_read(&pm_data->dev->power.usage_count)) {
+			enable_count = 0;
+			if (!pm_runtime_enabled(pm_data->dev)) {
+				pm_runtime_enable(pm_data->dev);
+				enable_count = 1;
+			}
+			pm_runtime_get_sync(pm_data->dev);
+			pm_runtime_put_sync(pm_data->dev);
+			if (enable_count)
+				pm_runtime_disable(pm_data->dev);
+		}
+	}
+}
+
+static int __init rockchip_pd_keepon_release(void)
+{
+	struct generic_pm_domain *genpd;
+	struct rockchip_pm_domain *pd;
+	int i;
+
+	if (!g_pmu)
+		return 0;
+
+	for (i = 0; i < g_pmu->genpd_data.num_domains; i++) {
+		genpd = g_pmu->genpd_data.domains[i];
+		if (genpd) {
+			pd = to_rockchip_pd(genpd);
+			if (pd->info->keepon_startup)
+				rockchip_pd_keepon_do_release(genpd, pd);
+		}
+	}
+	return 0;
+}
+late_initcall_sync(rockchip_pd_keepon_release);
+#endif
+
+static void __iomem *pd_base;
+
+void rockchip_dump_pmu(void)
+{
+	if (pd_base) {
+		pr_warn("PMU:\n");
+		print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET,
+			       32, 4, pd_base,
+			       0x100, false);
+	}
+}
+EXPORT_SYMBOL_GPL(rockchip_dump_pmu);
+
+static int rockchip_pmu_panic(struct notifier_block *this,
+			     unsigned long ev, void *ptr)
+{
+	rockchip_dump_pmu();
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block pmu_panic_block = {
+	.notifier_call = rockchip_pmu_panic,
+};
+
 static int rockchip_pm_domain_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -626,6 +1141,7 @@ static int rockchip_pm_domain_probe(struct platform_device *pdev)
 	const struct of_device_id *match;
 	const struct rockchip_pmu_info *pmu_info;
 	int error;
+	void __iomem *reg_base;
 
 	if (!np) {
 		dev_err(dev, "device tree node not found\n");
@@ -666,6 +1182,14 @@ static int rockchip_pm_domain_probe(struct platform_device *pdev)
 		return PTR_ERR(pmu->regmap);
 	}
 
+	reg_base = of_iomap(parent->of_node, 0);
+	if (!reg_base) {
+		dev_err(dev, "%s: could not map pmu region\n", __func__);
+		return -ENOMEM;
+	}
+
+	pd_base = reg_base;
+
 	/*
 	 * Configure power up and down transition delays for CORE
 	 * and GPU domains.
@@ -708,6 +1232,10 @@ static int rockchip_pm_domain_probe(struct platform_device *pdev)
 		goto err_out;
 	}
 
+	atomic_notifier_chain_register(&panic_notifier_list,
+				       &pmu_panic_block);
+
+	g_pmu = pmu;
 	return 0;
 
 err_out:
@@ -716,129 +1244,194 @@ static int rockchip_pm_domain_probe(struct platform_device *pdev)
 }
 
 static const struct rockchip_domain_info px30_pm_domains[] = {
-	[PX30_PD_USB]		= DOMAIN_PX30(BIT(5),  BIT(5),  BIT(10), false),
-	[PX30_PD_SDCARD]	= DOMAIN_PX30(BIT(8),  BIT(8),  BIT(9),  false),
-	[PX30_PD_GMAC]		= DOMAIN_PX30(BIT(10), BIT(10), BIT(6),  false),
-	[PX30_PD_MMC_NAND]	= DOMAIN_PX30(BIT(11), BIT(11), BIT(5),  false),
-	[PX30_PD_VPU]		= DOMAIN_PX30(BIT(12), BIT(12), BIT(14), false),
-	[PX30_PD_VO]		= DOMAIN_PX30(BIT(13), BIT(13), BIT(7),  false),
-	[PX30_PD_VI]		= DOMAIN_PX30(BIT(14), BIT(14), BIT(8),  false),
-	[PX30_PD_GPU]		= DOMAIN_PX30(BIT(15), BIT(15), BIT(2),  false),
+	[PX30_PD_USB]		= DOMAIN_PX30("usb",        BIT(5),  BIT(5),  BIT(10), true),
+	[PX30_PD_SDCARD]	= DOMAIN_PX30("sdcard",     BIT(8),  BIT(8),  BIT(9),  false),
+	[PX30_PD_GMAC]		= DOMAIN_PX30("gmac",       BIT(10), BIT(10), BIT(6),  false),
+	[PX30_PD_MMC_NAND]	= DOMAIN_PX30("mmc_nand",   BIT(11), BIT(11), BIT(5),  false),
+	[PX30_PD_VPU]		= DOMAIN_PX30("vpu",        BIT(12), BIT(12), BIT(14), false),
+	[PX30_PD_VO]		= DOMAIN_PX30_PROTECT("vo", BIT(13), BIT(13), BIT(7),  false),
+	[PX30_PD_VI]		= DOMAIN_PX30_PROTECT("vi", BIT(14), BIT(14), BIT(8),  false),
+	[PX30_PD_GPU]		= DOMAIN_PX30("gpu",        BIT(15), BIT(15), BIT(2),  false),
+};
+
+static const struct rockchip_domain_info rv1126_pm_domains[] = {
+	[RV1126_PD_CRYPTO]	= DOMAIN_RV1126_O("crypto",   BIT(10), BIT(4),  BIT(20), 0x4, false),
+	[RV1126_PD_VEPU]	= DOMAIN_RV1126("vepu",       BIT(2),  BIT(9),  BIT(9),  false),
+	[RV1126_PD_VI]		= DOMAIN_RV1126("vi",         BIT(4),  BIT(6),  BIT(6),  false),
+	[RV1126_PD_VO]		= DOMAIN_RV1126_PROTECT("vo", BIT(5),  BIT(7),  BIT(7),  false),
+	[RV1126_PD_ISPP]	= DOMAIN_RV1126("ispp",       BIT(1),  BIT(8),  BIT(8),  false),
+	[RV1126_PD_VDPU]	= DOMAIN_RV1126("vdpu",       BIT(3),  BIT(10), BIT(10), false),
+	[RV1126_PD_NVM]		= DOMAIN_RV1126("nvm",        BIT(7),  BIT(11), BIT(11), false),
+	[RV1126_PD_SDIO]	= DOMAIN_RV1126("sdio",       BIT(8),  BIT(13), BIT(13), false),
+	[RV1126_PD_USB]		= DOMAIN_RV1126("usb",        BIT(9),  BIT(15), BIT(15), true),
+	[RV1126_PD_NPU]		= DOMAIN_RV1126_O("npu",      BIT(0),  BIT(2),  BIT(18), 0x4, false),
+};
+
+static const struct rockchip_domain_info rk1808_pm_domains[] = {
+	[RK1808_VD_NPU]		= DOMAIN_PX30("npu",         BIT(15), BIT(15), BIT(2), false),
+	[RK1808_PD_PCIE]	= DOMAIN_PX30("pcie",        BIT(9),  BIT(9),  BIT(4), true),
+	[RK1808_PD_VPU]		= DOMAIN_PX30("vpu",         BIT(13), BIT(13), BIT(7), false),
+	[RK1808_PD_VIO]		= DOMAIN_PX30_PROTECT("vio", BIT(14), BIT(14), BIT(8), false),
 };
 
 static const struct rockchip_domain_info rk3036_pm_domains[] = {
-	[RK3036_PD_MSCH]	= DOMAIN_RK3036(BIT(14), BIT(23), BIT(30), true),
-	[RK3036_PD_CORE]	= DOMAIN_RK3036(BIT(13), BIT(17), BIT(24), false),
-	[RK3036_PD_PERI]	= DOMAIN_RK3036(BIT(12), BIT(18), BIT(25), false),
-	[RK3036_PD_VIO]		= DOMAIN_RK3036(BIT(11), BIT(19), BIT(26), false),
-	[RK3036_PD_VPU]		= DOMAIN_RK3036(BIT(10), BIT(20), BIT(27), false),
-	[RK3036_PD_GPU]		= DOMAIN_RK3036(BIT(9),  BIT(21), BIT(28), false),
-	[RK3036_PD_SYS]		= DOMAIN_RK3036(BIT(8),  BIT(22), BIT(29), false),
+	[RK3036_PD_MSCH]	= DOMAIN_RK3036("msch", BIT(14), BIT(23), BIT(30), true),
+	[RK3036_PD_CORE]	= DOMAIN_RK3036("core", BIT(13), BIT(17), BIT(24), false),
+	[RK3036_PD_PERI]	= DOMAIN_RK3036("peri", BIT(12), BIT(18), BIT(25), false),
+	[RK3036_PD_VIO]		= DOMAIN_RK3036("vio",  BIT(11), BIT(19), BIT(26), false),
+	[RK3036_PD_VPU]		= DOMAIN_RK3036("vpu",  BIT(10), BIT(20), BIT(27), false),
+	[RK3036_PD_GPU]		= DOMAIN_RK3036("gpu",  BIT(9),  BIT(21), BIT(28), false),
+	[RK3036_PD_SYS]		= DOMAIN_RK3036("sys",  BIT(8),  BIT(22), BIT(29), false),
 };
 
 static const struct rockchip_domain_info rk3066_pm_domains[] = {
-	[RK3066_PD_GPU]		= DOMAIN(BIT(9), BIT(9), BIT(3), BIT(24), BIT(29), false),
-	[RK3066_PD_VIDEO]	= DOMAIN(BIT(8), BIT(8), BIT(4), BIT(23), BIT(28), false),
-	[RK3066_PD_VIO]		= DOMAIN(BIT(7), BIT(7), BIT(5), BIT(22), BIT(27), false),
-	[RK3066_PD_PERI]	= DOMAIN(BIT(6), BIT(6), BIT(2), BIT(25), BIT(30), false),
-	[RK3066_PD_CPU]		= DOMAIN(0,      BIT(5), BIT(1), BIT(26), BIT(31), false),
+	[RK3066_PD_GPU]		= DOMAIN("gpu",   BIT(9), BIT(9), BIT(3), BIT(24), BIT(29), false, false),
+	[RK3066_PD_VIDEO]	= DOMAIN("video", BIT(8), BIT(8), BIT(4), BIT(23), BIT(28), false, false),
+	[RK3066_PD_VIO]		= DOMAIN("vio",   BIT(7), BIT(7), BIT(5), BIT(22), BIT(27), false, true),
+	[RK3066_PD_PERI]	= DOMAIN("peri",  BIT(6), BIT(6), BIT(2), BIT(25), BIT(30), false, false),
+	[RK3066_PD_CPU]		= DOMAIN("cpu",   0,      BIT(5), BIT(1), BIT(26), BIT(31), false, false),
 };
 
 static const struct rockchip_domain_info rk3128_pm_domains[] = {
-	[RK3128_PD_CORE]	= DOMAIN_RK3288(BIT(0), BIT(0), BIT(4), false),
-	[RK3128_PD_MSCH]	= DOMAIN_RK3288(0,      0,      BIT(6), true),
-	[RK3128_PD_VIO]		= DOMAIN_RK3288(BIT(3), BIT(3), BIT(2), false),
-	[RK3128_PD_VIDEO]	= DOMAIN_RK3288(BIT(2), BIT(2), BIT(1), false),
-	[RK3128_PD_GPU]		= DOMAIN_RK3288(BIT(1), BIT(1), BIT(3), false),
+	[RK3128_PD_CORE]        = DOMAIN_RK3288("core",        BIT(0), BIT(0), BIT(4), false),
+	[RK3128_PD_MSCH]        = DOMAIN_RK3288("msch",        0,      0,      BIT(6), true),
+	[RK3128_PD_VIO]         = DOMAIN_RK3288_PROTECT("vio", BIT(3), BIT(3), BIT(2), false),
+	[RK3128_PD_VIDEO]       = DOMAIN_RK3288("video",       BIT(2), BIT(2), BIT(1), false),
+	[RK3128_PD_GPU]         = DOMAIN_RK3288("gpu",         BIT(1), BIT(1), BIT(3), false),
 };
 
 static const struct rockchip_domain_info rk3188_pm_domains[] = {
-	[RK3188_PD_GPU]		= DOMAIN(BIT(9), BIT(9), BIT(3), BIT(24), BIT(29), false),
-	[RK3188_PD_VIDEO]	= DOMAIN(BIT(8), BIT(8), BIT(4), BIT(23), BIT(28), false),
-	[RK3188_PD_VIO]		= DOMAIN(BIT(7), BIT(7), BIT(5), BIT(22), BIT(27), false),
-	[RK3188_PD_PERI]	= DOMAIN(BIT(6), BIT(6), BIT(2), BIT(25), BIT(30), false),
-	[RK3188_PD_CPU]		= DOMAIN(BIT(5), BIT(5), BIT(1), BIT(26), BIT(31), false),
+	[RK3188_PD_GPU]         = DOMAIN("gpu",   BIT(9), BIT(9), BIT(3), BIT(24), BIT(29), false, false),
+	[RK3188_PD_VIDEO]	= DOMAIN("video", BIT(8), BIT(8), BIT(4), BIT(23), BIT(28), false, false),
+	[RK3188_PD_VIO]		= DOMAIN("vio",   BIT(7), BIT(7), BIT(5), BIT(22), BIT(27), false, true),
+	[RK3188_PD_PERI]	= DOMAIN("peri",  BIT(6), BIT(6), BIT(2), BIT(25), BIT(30), false, false),
+	[RK3188_PD_CPU]		= DOMAIN("cpu",   BIT(5), BIT(5), BIT(1), BIT(26), BIT(31), false, false),
 };
 
 static const struct rockchip_domain_info rk3228_pm_domains[] = {
-	[RK3228_PD_CORE]	= DOMAIN_RK3036(BIT(0),  BIT(0),  BIT(16), true),
-	[RK3228_PD_MSCH]	= DOMAIN_RK3036(BIT(1),  BIT(1),  BIT(17), true),
-	[RK3228_PD_BUS]		= DOMAIN_RK3036(BIT(2),  BIT(2),  BIT(18), true),
-	[RK3228_PD_SYS]		= DOMAIN_RK3036(BIT(3),  BIT(3),  BIT(19), true),
-	[RK3228_PD_VIO]		= DOMAIN_RK3036(BIT(4),  BIT(4),  BIT(20), false),
-	[RK3228_PD_VOP]		= DOMAIN_RK3036(BIT(5),  BIT(5),  BIT(21), false),
-	[RK3228_PD_VPU]		= DOMAIN_RK3036(BIT(6),  BIT(6),  BIT(22), false),
-	[RK3228_PD_RKVDEC]	= DOMAIN_RK3036(BIT(7),  BIT(7),  BIT(23), false),
-	[RK3228_PD_GPU]		= DOMAIN_RK3036(BIT(8),  BIT(8),  BIT(24), false),
-	[RK3228_PD_PERI]	= DOMAIN_RK3036(BIT(9),  BIT(9),  BIT(25), true),
-	[RK3228_PD_GMAC]	= DOMAIN_RK3036(BIT(10), BIT(10), BIT(26), false),
+	[RK3228_PD_CORE]	= DOMAIN_RK3036("core", BIT(0),  BIT(0),  BIT(16), true),
+	[RK3228_PD_MSCH]	= DOMAIN_RK3036("msch", BIT(1),  BIT(1),  BIT(17), true),
+	[RK3228_PD_BUS]		= DOMAIN_RK3036("bus",  BIT(2),  BIT(2),  BIT(18), true),
+	[RK3228_PD_SYS]		= DOMAIN_RK3036("sys",  BIT(3),  BIT(3),  BIT(19), true),
+	[RK3228_PD_VIO]		= DOMAIN_RK3036("vio",  BIT(4),  BIT(4),  BIT(20), false),
+	[RK3228_PD_VOP]		= DOMAIN_RK3036("vop",  BIT(5),  BIT(5),  BIT(21), false),
+	[RK3228_PD_VPU]		= DOMAIN_RK3036("vpu",  BIT(6),  BIT(6),  BIT(22), false),
+	[RK3228_PD_RKVDEC]	= DOMAIN_RK3036("vdec", BIT(7),  BIT(7),  BIT(23), false),
+	[RK3228_PD_GPU]		= DOMAIN_RK3036("gpu",  BIT(8),  BIT(8),  BIT(24), false),
+	[RK3228_PD_PERI]	= DOMAIN_RK3036("peri", BIT(9),  BIT(9),  BIT(25), true),
+	[RK3228_PD_GMAC]	= DOMAIN_RK3036("gmac", BIT(10), BIT(10), BIT(26), false),
 };
 
 static const struct rockchip_domain_info rk3288_pm_domains[] = {
-	[RK3288_PD_VIO]		= DOMAIN_RK3288(BIT(7),  BIT(7),  BIT(4), false),
-	[RK3288_PD_HEVC]	= DOMAIN_RK3288(BIT(14), BIT(10), BIT(9), false),
-	[RK3288_PD_VIDEO]	= DOMAIN_RK3288(BIT(8),  BIT(8),  BIT(3), false),
-	[RK3288_PD_GPU]		= DOMAIN_RK3288(BIT(9),  BIT(9),  BIT(2), false),
+	[RK3288_PD_VIO]		= DOMAIN_RK3288_PROTECT("vio", BIT(7),  BIT(7),  BIT(4), false),
+	[RK3288_PD_HEVC]	= DOMAIN_RK3288("hevc",        BIT(14), BIT(10), BIT(9), false),
+	[RK3288_PD_VIDEO]	= DOMAIN_RK3288("video",       BIT(8),  BIT(8),  BIT(3), false),
+	[RK3288_PD_GPU]		= DOMAIN_RK3288("gpu",         BIT(9),  BIT(9),  BIT(2), false),
 };
 
 static const struct rockchip_domain_info rk3328_pm_domains[] = {
-	[RK3328_PD_CORE]	= DOMAIN_RK3328(0, BIT(0), BIT(0), false),
-	[RK3328_PD_GPU]		= DOMAIN_RK3328(0, BIT(1), BIT(1), false),
-	[RK3328_PD_BUS]		= DOMAIN_RK3328(0, BIT(2), BIT(2), true),
-	[RK3328_PD_MSCH]	= DOMAIN_RK3328(0, BIT(3), BIT(3), true),
-	[RK3328_PD_PERI]	= DOMAIN_RK3328(0, BIT(4), BIT(4), true),
-	[RK3328_PD_VIDEO]	= DOMAIN_RK3328(0, BIT(5), BIT(5), false),
-	[RK3328_PD_HEVC]	= DOMAIN_RK3328(0, BIT(6), BIT(6), false),
-	[RK3328_PD_VIO]		= DOMAIN_RK3328(0, BIT(8), BIT(8), false),
-	[RK3328_PD_VPU]		= DOMAIN_RK3328(0, BIT(9), BIT(9), false),
+	[RK3328_PD_CORE]	= DOMAIN_RK3328("core",  0, BIT(0), BIT(0), false),
+	[RK3328_PD_GPU]		= DOMAIN_RK3328("gpu",   0, BIT(1), BIT(1), false),
+	[RK3328_PD_BUS]		= DOMAIN_RK3328("bus",   0, BIT(2), BIT(2), true),
+	[RK3328_PD_MSCH]	= DOMAIN_RK3328("msch",  0, BIT(3), BIT(3), true),
+	[RK3328_PD_PERI]	= DOMAIN_RK3328("peri",  0, BIT(4), BIT(4), true),
+	[RK3328_PD_VIDEO]	= DOMAIN_RK3328("video", 0, BIT(5), BIT(5), false),
+	[RK3328_PD_HEVC]	= DOMAIN_RK3328("hevc",  0, BIT(6), BIT(6), false),
+	[RK3328_PD_VIO]		= DOMAIN_RK3328("vio",   0, BIT(8), BIT(8), false),
+	[RK3328_PD_VPU]		= DOMAIN_RK3328("vpu",   0, BIT(9), BIT(9), false),
 };
 
 static const struct rockchip_domain_info rk3366_pm_domains[] = {
-	[RK3366_PD_PERI]	= DOMAIN_RK3368(BIT(10), BIT(10), BIT(6), true),
-	[RK3366_PD_VIO]		= DOMAIN_RK3368(BIT(14), BIT(14), BIT(8), false),
-	[RK3366_PD_VIDEO]	= DOMAIN_RK3368(BIT(13), BIT(13), BIT(7), false),
-	[RK3366_PD_RKVDEC]	= DOMAIN_RK3368(BIT(11), BIT(11), BIT(7), false),
-	[RK3366_PD_WIFIBT]	= DOMAIN_RK3368(BIT(8),  BIT(8),  BIT(9), false),
-	[RK3366_PD_VPU]		= DOMAIN_RK3368(BIT(12), BIT(12), BIT(7), false),
-	[RK3366_PD_GPU]		= DOMAIN_RK3368(BIT(15), BIT(15), BIT(2), false),
+	[RK3366_PD_PERI]	= DOMAIN_RK3368("peri",        BIT(10), BIT(10), BIT(6), true),
+	[RK3366_PD_VIO]		= DOMAIN_RK3368_PROTECT("vio", BIT(14), BIT(14), BIT(8), false),
+	[RK3366_PD_VIDEO]	= DOMAIN_RK3368("video",       BIT(13), BIT(13), BIT(7), false),
+	[RK3366_PD_RKVDEC]	= DOMAIN_RK3368("rkvdec",      BIT(11), BIT(11), BIT(7), false),
+	[RK3366_PD_WIFIBT]	= DOMAIN_RK3368("wifibt",      BIT(8),  BIT(8),  BIT(9), false),
+	[RK3366_PD_VPU]		= DOMAIN_RK3368("vpu",         BIT(12), BIT(12), BIT(7), false),
+	[RK3366_PD_GPU]		= DOMAIN_RK3368("gpu",         BIT(15), BIT(15), BIT(2), false),
 };
 
 static const struct rockchip_domain_info rk3368_pm_domains[] = {
-	[RK3368_PD_PERI]	= DOMAIN_RK3368(BIT(13), BIT(12), BIT(6), true),
-	[RK3368_PD_VIO]		= DOMAIN_RK3368(BIT(15), BIT(14), BIT(8), false),
-	[RK3368_PD_VIDEO]	= DOMAIN_RK3368(BIT(14), BIT(13), BIT(7), false),
-	[RK3368_PD_GPU_0]	= DOMAIN_RK3368(BIT(16), BIT(15), BIT(2), false),
-	[RK3368_PD_GPU_1]	= DOMAIN_RK3368(BIT(17), BIT(16), BIT(2), false),
+	[RK3368_PD_PERI]	= DOMAIN_RK3368("peri",        BIT(13), BIT(12), BIT(6), true),
+	[RK3368_PD_VIO]		= DOMAIN_RK3368_PROTECT("vio", BIT(15), BIT(14), BIT(8), false),
+	[RK3368_PD_VIDEO]	= DOMAIN_RK3368("video",       BIT(14), BIT(13), BIT(7), false),
+	[RK3368_PD_GPU_0]	= DOMAIN_RK3368("gpu_0",       BIT(16), BIT(15), BIT(2), false),
+	[RK3368_PD_GPU_1]	= DOMAIN_RK3368("gpu_1",       BIT(17), BIT(16), BIT(2), false),
 };
 
 static const struct rockchip_domain_info rk3399_pm_domains[] = {
-	[RK3399_PD_TCPD0]	= DOMAIN_RK3399(BIT(8),  BIT(8),  0,	   false),
-	[RK3399_PD_TCPD1]	= DOMAIN_RK3399(BIT(9),  BIT(9),  0,	   false),
-	[RK3399_PD_CCI]		= DOMAIN_RK3399(BIT(10), BIT(10), 0,	   true),
-	[RK3399_PD_CCI0]	= DOMAIN_RK3399(0,	 0,	  BIT(15), true),
-	[RK3399_PD_CCI1]	= DOMAIN_RK3399(0,	 0,	  BIT(16), true),
-	[RK3399_PD_PERILP]	= DOMAIN_RK3399(BIT(11), BIT(11), BIT(1),  true),
-	[RK3399_PD_PERIHP]	= DOMAIN_RK3399(BIT(12), BIT(12), BIT(2),  true),
-	[RK3399_PD_CENTER]	= DOMAIN_RK3399(BIT(13), BIT(13), BIT(14), true),
-	[RK3399_PD_VIO]		= DOMAIN_RK3399(BIT(14), BIT(14), BIT(17), false),
-	[RK3399_PD_GPU]		= DOMAIN_RK3399(BIT(15), BIT(15), BIT(0),  false),
-	[RK3399_PD_VCODEC]	= DOMAIN_RK3399(BIT(16), BIT(16), BIT(3),  false),
-	[RK3399_PD_VDU]		= DOMAIN_RK3399(BIT(17), BIT(17), BIT(4),  false),
-	[RK3399_PD_RGA]		= DOMAIN_RK3399(BIT(18), BIT(18), BIT(5),  false),
-	[RK3399_PD_IEP]		= DOMAIN_RK3399(BIT(19), BIT(19), BIT(6),  false),
-	[RK3399_PD_VO]		= DOMAIN_RK3399(BIT(20), BIT(20), 0,	   false),
-	[RK3399_PD_VOPB]	= DOMAIN_RK3399(0,	 0,	  BIT(7),  false),
-	[RK3399_PD_VOPL]	= DOMAIN_RK3399(0, 	 0,	  BIT(8),  false),
-	[RK3399_PD_ISP0]	= DOMAIN_RK3399(BIT(22), BIT(22), BIT(9),  false),
-	[RK3399_PD_ISP1]	= DOMAIN_RK3399(BIT(23), BIT(23), BIT(10), false),
-	[RK3399_PD_HDCP]	= DOMAIN_RK3399(BIT(24), BIT(24), BIT(11), false),
-	[RK3399_PD_GMAC]	= DOMAIN_RK3399(BIT(25), BIT(25), BIT(23), true),
-	[RK3399_PD_EMMC]	= DOMAIN_RK3399(BIT(26), BIT(26), BIT(24), true),
-	[RK3399_PD_USB3]	= DOMAIN_RK3399(BIT(27), BIT(27), BIT(12), true),
-	[RK3399_PD_EDP]		= DOMAIN_RK3399(BIT(28), BIT(28), BIT(22), false),
-	[RK3399_PD_GIC]		= DOMAIN_RK3399(BIT(29), BIT(29), BIT(27), true),
-	[RK3399_PD_SD]		= DOMAIN_RK3399(BIT(30), BIT(30), BIT(28), true),
-	[RK3399_PD_SDIOAUDIO]	= DOMAIN_RK3399(BIT(31), BIT(31), BIT(29), true),
+	[RK3399_PD_TCPD0]	= DOMAIN_RK3399("tcpd0",        BIT(8),  BIT(8),  0,       false),
+	[RK3399_PD_TCPD1]	= DOMAIN_RK3399("tcpd1",        BIT(9),  BIT(9),  0,       false),
+	[RK3399_PD_CCI]		= DOMAIN_RK3399("cci",          BIT(10), BIT(10), 0,       true),
+	[RK3399_PD_CCI0]	= DOMAIN_RK3399("cci0",         0,       0,       BIT(15), true),
+	[RK3399_PD_CCI1]	= DOMAIN_RK3399("cci1",         0,       0,       BIT(16), true),
+	[RK3399_PD_PERILP]	= DOMAIN_RK3399("perilp",       BIT(11), BIT(11), BIT(1),  true),
+	[RK3399_PD_PERIHP]	= DOMAIN_RK3399("perihp",       BIT(12), BIT(12), BIT(2),  true),
+	[RK3399_PD_CENTER]	= DOMAIN_RK3399("center",       BIT(13), BIT(13), BIT(14), true),
+	[RK3399_PD_VIO]		= DOMAIN_RK3399_PROTECT("vio",  BIT(14), BIT(14), BIT(17), false),
+	[RK3399_PD_GPU]		= DOMAIN_RK3399("gpu",          BIT(15), BIT(15), BIT(0),  false),
+	[RK3399_PD_VCODEC]	= DOMAIN_RK3399("vcodec",       BIT(16), BIT(16), BIT(3),  false),
+	[RK3399_PD_VDU]		= DOMAIN_RK3399("vdu",          BIT(17), BIT(17), BIT(4),  false),
+	[RK3399_PD_RGA]		= DOMAIN_RK3399("rga",          BIT(18), BIT(18), BIT(5),  false),
+	[RK3399_PD_IEP]		= DOMAIN_RK3399("iep",          BIT(19), BIT(19), BIT(6),  false),
+	[RK3399_PD_VO]		= DOMAIN_RK3399_PROTECT("vo",   BIT(20), BIT(20), 0,       false),
+	[RK3399_PD_VOPB]	= DOMAIN_RK3399_PROTECT("vopb", 0,       0,       BIT(7),  false),
+	[RK3399_PD_VOPL]	= DOMAIN_RK3399_PROTECT("vopl", 0,       0,       BIT(8),  false),
+	[RK3399_PD_ISP0]	= DOMAIN_RK3399("isp0",         BIT(22), BIT(22), BIT(9),  false),
+	[RK3399_PD_ISP1]	= DOMAIN_RK3399("isp1",         BIT(23), BIT(23), BIT(10), false),
+	[RK3399_PD_HDCP]	= DOMAIN_RK3399_PROTECT("hdcp", BIT(24), BIT(24), BIT(11), false),
+	[RK3399_PD_GMAC]	= DOMAIN_RK3399("gmac",         BIT(25), BIT(25), BIT(23), true),
+	[RK3399_PD_EMMC]	= DOMAIN_RK3399("emmc",         BIT(26), BIT(26), BIT(24), true),
+	[RK3399_PD_USB3]	= DOMAIN_RK3399("usb3",         BIT(27), BIT(27), BIT(12), true),
+	[RK3399_PD_EDP]		= DOMAIN_RK3399_PROTECT("edp",  BIT(28), BIT(28), BIT(22), false),
+	[RK3399_PD_GIC]		= DOMAIN_RK3399("gic",          BIT(29), BIT(29), BIT(27), true),
+	[RK3399_PD_SD]		= DOMAIN_RK3399("sd",           BIT(30), BIT(30), BIT(28), true),
+	[RK3399_PD_SDIOAUDIO]	= DOMAIN_RK3399("sdioaudio",    BIT(31), BIT(31), BIT(29), true),
+};
+
+static const struct rockchip_domain_info rk3568_pm_domains[] = {
+	[RK3568_PD_NPU]		= DOMAIN_RK3568("npu",        BIT(1), BIT(2),  false),
+	[RK3568_PD_GPU]		= DOMAIN_RK3568("gpu",        BIT(0), BIT(1),  false),
+	[RK3568_PD_VI]		= DOMAIN_RK3568("vi",         BIT(6), BIT(3),  false),
+	[RK3568_PD_VO]		= DOMAIN_RK3568_PROTECT("vo", BIT(7), BIT(4),  false),
+	[RK3568_PD_RGA]		= DOMAIN_RK3568("rga",        BIT(5), BIT(5),  false),
+	[RK3568_PD_VPU]		= DOMAIN_RK3568("vpu",        BIT(2), BIT(6),  false),
+	[RK3568_PD_RKVDEC]	= DOMAIN_RK3568("rkvdec",     BIT(4), BIT(8),  false),
+	[RK3568_PD_RKVENC]	= DOMAIN_RK3568("rkvenc",     BIT(3), BIT(7),  false),
+	[RK3568_PD_PIPE]	= DOMAIN_RK3568("pipe",       BIT(8), BIT(11), false),
+};
+
+static const struct rockchip_domain_info rk3588_pm_domains[] = {
+					     /* name       p_offset pwr  status   r_status r_offset req  idle     wakeup */
+	[RK3588_PD_GPU]		= DOMAIN_RK3588("gpu",     0x0, BIT(0),  0,       BIT(1),  0x0, BIT(0),  BIT(0),  false),
+	[RK3588_PD_NPU]		= DOMAIN_RK3588("npu",     0x0, BIT(1),  BIT(1),  0,       0x0, 0,       0,       false),
+	[RK3588_PD_VCODEC]	= DOMAIN_RK3588("vcodec",  0x0, BIT(2),  BIT(2),  0,       0x0, 0,       0,       false),
+	[RK3588_PD_NPUTOP]	= DOMAIN_RK3588("nputop",  0x0, BIT(3),  0,       BIT(2),  0x0, BIT(1),  BIT(1),  false),
+	[RK3588_PD_NPU1]	= DOMAIN_RK3588("npu1",    0x0, BIT(4),  0,       BIT(3),  0x0, BIT(2),  BIT(2),  false),
+	[RK3588_PD_NPU2]	= DOMAIN_RK3588("npu2",    0x0, BIT(5),  0,       BIT(4),  0x0, BIT(3),  BIT(3),  false),
+	[RK3588_PD_VENC0]	= DOMAIN_RK3588("venc0",   0x0, BIT(6),  0,       BIT(5),  0x0, BIT(4),  BIT(4),  false),
+	[RK3588_PD_VENC1]	= DOMAIN_RK3588("venc1",   0x0, BIT(7),  0,       BIT(6),  0x0, BIT(5),  BIT(5),  false),
+	[RK3588_PD_RKVDEC0]	= DOMAIN_RK3588("rkvdec0", 0x0, BIT(8),  0,       BIT(7),  0x0, BIT(6),  BIT(6),  false),
+	[RK3588_PD_RKVDEC1]	= DOMAIN_RK3588("rkvdec1", 0x0, BIT(9),  0,       BIT(8),  0x0, BIT(7),  BIT(7),  false),
+	[RK3588_PD_VDPU]	= DOMAIN_RK3588("vdpu",    0x0, BIT(10), 0,       BIT(9),  0x0, BIT(8),  BIT(8),  false),
+	[RK3588_PD_RGA30]	= DOMAIN_RK3588("rga30",   0x0, BIT(11), 0,       BIT(10), 0x0, 0,       0,       false),
+	[RK3588_PD_AV1]		= DOMAIN_RK3588("av1",     0x0, BIT(12), 0,       BIT(11), 0x0, BIT(9),  BIT(9),  false),
+	[RK3588_PD_VI]		= DOMAIN_RK3588("vi",      0x0, BIT(13), 0,       BIT(12), 0x0, BIT(10), BIT(10), false),
+	[RK3588_PD_FEC]		= DOMAIN_RK3588("fec",     0x0, BIT(14), 0,       BIT(13), 0x0, 0,       0,       false),
+	[RK3588_PD_ISP1]	= DOMAIN_RK3588("isp1",    0x0, BIT(15), 0,       BIT(14), 0x0, BIT(11), BIT(11), false),
+	[RK3588_PD_RGA31]	= DOMAIN_RK3588("rga31",   0x4, BIT(0),  0,       BIT(15), 0x0, BIT(12), BIT(12), false),
+	[RK3588_PD_VOP]		= DOMAIN_RK3588_P("vop",   0x4, BIT(1),  0,       BIT(16), 0x0, BIT(13) | BIT(14), BIT(13) | BIT(14), false),
+	[RK3588_PD_VO0]		= DOMAIN_RK3588_P("vo0",   0x4, BIT(2),  0,       BIT(17), 0x0, BIT(15), BIT(15), false),
+	[RK3588_PD_VO1]		= DOMAIN_RK3588_P("vo1",   0x4, BIT(3),  0,       BIT(18), 0x4, BIT(0),  BIT(16), false),
+	[RK3588_PD_AUDIO]	= DOMAIN_RK3588("audio",   0x4, BIT(4),  0,       BIT(19), 0x4, BIT(1),  BIT(17), false),
+	[RK3588_PD_PHP]		= DOMAIN_RK3588("php",     0x4, BIT(5),  0,       BIT(20), 0x4, BIT(5),  BIT(21), false),
+	[RK3588_PD_GMAC]	= DOMAIN_RK3588("gmac",    0x4, BIT(6),  0,       BIT(21), 0x0, 0,       0,       false),
+	[RK3588_PD_PCIE]	= DOMAIN_RK3588("pcie",    0x4, BIT(7),  0,       BIT(22), 0x0, 0,       0,       true),
+	[RK3588_PD_NVM]		= DOMAIN_RK3588("nvm",     0x4, BIT(8),  BIT(24), 0,       0x4, BIT(2),  BIT(18), false),
+	[RK3588_PD_NVM0]	= DOMAIN_RK3588("nvm0",    0x4, BIT(9),  0,       BIT(23), 0x0, 0,       0,       false),
+	[RK3588_PD_SDIO]	= DOMAIN_RK3588("sdio",    0x4, BIT(10), 0,       BIT(24), 0x4, BIT(3),  BIT(19), false),
+	[RK3588_PD_USB]		= DOMAIN_RK3588("usb",     0x4, BIT(11), 0,       BIT(25), 0x4, BIT(4),  BIT(20), true),
+	[RK3588_PD_SDMMC]	= DOMAIN_RK3588("sdmmc",   0x4, BIT(13), 0,       BIT(26), 0x0, 0,       0,       false),
 };
 
 static const struct rockchip_pmu_info px30_pmu = {
@@ -852,6 +1445,28 @@ static const struct rockchip_pmu_info px30_pmu = {
 	.domain_info = px30_pm_domains,
 };
 
+static const struct rockchip_pmu_info rv1126_pmu = {
+	.pwr_offset = 0x110,
+	.status_offset = 0x108,
+	.req_offset = 0xc0,
+	.idle_offset = 0xd8,
+	.ack_offset = 0xd0,
+
+	.num_domains = ARRAY_SIZE(rv1126_pm_domains),
+	.domain_info = rv1126_pm_domains,
+};
+
+static const struct rockchip_pmu_info rk1808_pmu = {
+	.pwr_offset = 0x18,
+	.status_offset = 0x20,
+	.req_offset = 0x64,
+	.idle_offset = 0x6c,
+	.ack_offset = 0x6c,
+
+	.num_domains = ARRAY_SIZE(rk1808_pm_domains),
+	.domain_info = rk1808_pm_domains,
+};
+
 static const struct rockchip_pmu_info rk3036_pmu = {
 	.req_offset = 0x148,
 	.idle_offset = 0x14c,
@@ -976,11 +1591,42 @@ static const struct rockchip_pmu_info rk3399_pmu = {
 	.domain_info = rk3399_pm_domains,
 };
 
+static const struct rockchip_pmu_info rk3568_pmu = {
+	.pwr_offset = 0xa0,
+	.status_offset = 0x98,
+	.req_offset = 0x50,
+	.idle_offset = 0x68,
+	.ack_offset = 0x60,
+
+	.num_domains = ARRAY_SIZE(rk3568_pm_domains),
+	.domain_info = rk3568_pm_domains,
+};
+
+static const struct rockchip_pmu_info rk3588_pmu = {
+	.pwr_offset = 0x14c,
+	.status_offset = 0x180,
+	.req_offset = 0x10c,
+	.idle_offset = 0x120,
+	.ack_offset = 0x118,
+	.repair_status_offset = 0x290,
+
+	.num_domains = ARRAY_SIZE(rk3588_pm_domains),
+	.domain_info = rk3588_pm_domains,
+};
+
 static const struct of_device_id rockchip_pm_domain_dt_match[] = {
 	{
 		.compatible = "rockchip,px30-power-controller",
 		.data = (void *)&px30_pmu,
 	},
+	{
+		.compatible = "rockchip,rv1126-power-controller",
+		.data = (void *)&rv1126_pmu,
+	},
+	{
+		.compatible = "rockchip,rk1808-power-controller",
+		.data = (void *)&rk1808_pmu,
+	},
 	{
 		.compatible = "rockchip,rk3036-power-controller",
 		.data = (void *)&rk3036_pmu,
@@ -1021,8 +1667,17 @@ static const struct of_device_id rockchip_pm_domain_dt_match[] = {
 		.compatible = "rockchip,rk3399-power-controller",
 		.data = (void *)&rk3399_pmu,
 	},
+	{
+		.compatible = "rockchip,rk3568-power-controller",
+		.data = (void *)&rk3568_pmu,
+	},
+	{
+		.compatible = "rockchip,rk3588-power-controller",
+		.data = (void *)&rk3588_pmu,
+	},
 	{ /* sentinel */ },
 };
+MODULE_DEVICE_TABLE(of, rockchip_pm_domain_dt_match);
 
 static struct platform_driver rockchip_pm_domain_driver = {
 	.probe = rockchip_pm_domain_probe,
@@ -1043,3 +1698,12 @@ static int __init rockchip_pm_domain_drv_register(void)
 	return platform_driver_register(&rockchip_pm_domain_driver);
 }
 postcore_initcall(rockchip_pm_domain_drv_register);
+
+static void __exit rockchip_pm_domain_drv_unregister(void)
+{
+	platform_driver_unregister(&rockchip_pm_domain_driver);
+}
+module_exit(rockchip_pm_domain_drv_unregister);
+
+MODULE_DESCRIPTION("ROCKCHIP PM Domain Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c
index a9f97023d..f744c64f1 100644
--- a/drivers/spi/spi-rockchip.c
+++ b/drivers/spi/spi-rockchip.c
@@ -10,6 +10,7 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/devinfo.h>
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
 #include <linux/pm_runtime.h>
@@ -107,6 +108,8 @@
 #define CR0_OPM_MASTER				0x0
 #define CR0_OPM_SLAVE				0x1
 
+#define CR0_SOI_OFFSET				23
+
 #define CR0_MTM_OFFSET				0x21
 
 /* Bit fields in SER, 2bit */
@@ -116,13 +119,14 @@
 #define BAUDR_SCKDV_MIN				2
 #define BAUDR_SCKDV_MAX				65534
 
-/* Bit fields in SR, 5bit */
-#define SR_MASK						0x1f
+/* Bit fields in SR, 6bit */
+#define SR_MASK						0x3f
 #define SR_BUSY						(1 << 0)
 #define SR_TF_FULL					(1 << 1)
 #define SR_TF_EMPTY					(1 << 2)
 #define SR_RF_EMPTY					(1 << 3)
 #define SR_RF_FULL					(1 << 4)
+#define SR_SLAVE_TX_BUSY				(1 << 5)
 
 /* Bit fields in ISR, IMR, ISR, RISR, 5bit */
 #define INT_MASK					0x1f
@@ -130,7 +134,8 @@
 #define INT_TF_OVERFLOW				(1 << 1)
 #define INT_RF_UNDERFLOW			(1 << 2)
 #define INT_RF_OVERFLOW				(1 << 3)
-#define INT_RF_FULL					(1 << 4)
+#define INT_RF_FULL				(1 << 4)
+#define INT_CS_INACTIVE				(1 << 6)
 
 /* Bit fields in ICR, 4bit */
 #define ICR_MASK					0x0f
@@ -149,6 +154,8 @@
 
 /* sclk_out: spi master internal logic in rk3x can support 50Mhz */
 #define MAX_SCLK_OUT				50000000U
+/* max sclk of driver strength 4mA */
+#define IO_DRIVER_4MA_MAX_SCLK_OUT	24000000U
 
 /*
  * SPI_CTRLR1 is 16-bits, so we should support lengths of 0xffff + 1. However,
@@ -156,7 +163,8 @@
  */
 #define ROCKCHIP_SPI_MAX_TRANLEN		0xffff
 
-#define ROCKCHIP_SPI_MAX_CS_NUM			2
+/* 2 for native cs, 2 for cs-gpio */
+#define ROCKCHIP_SPI_MAX_CS_NUM			4
 #define ROCKCHIP_SPI_VER2_TYPE1			0x05EC0002
 #define ROCKCHIP_SPI_VER2_TYPE2			0x00110002
 
@@ -187,7 +195,10 @@ struct rockchip_spi {
 
 	bool cs_asserted[ROCKCHIP_SPI_MAX_CS_NUM];
 
+	struct pinctrl_state *high_speed_state;
 	bool slave_abort;
+	bool cs_inactive; /* spi slave tansmition stop when cs inactive */
+	struct spi_transfer *xfer; /* Store xfer temporarily */
 };
 
 static inline void spi_enable_chip(struct rockchip_spi *rs, bool enable)
@@ -195,13 +206,19 @@ static inline void spi_enable_chip(struct rockchip_spi *rs, bool enable)
 	writel_relaxed((enable ? 1U : 0U), rs->regs + ROCKCHIP_SPI_SSIENR);
 }
 
-static inline void wait_for_idle(struct rockchip_spi *rs)
+static inline void wait_for_tx_idle(struct rockchip_spi *rs, bool slave_mode)
 {
 	unsigned long timeout = jiffies + msecs_to_jiffies(5);
 
 	do {
-		if (!(readl_relaxed(rs->regs + ROCKCHIP_SPI_SR) & SR_BUSY))
-			return;
+		if (slave_mode) {
+			if (!(readl_relaxed(rs->regs + ROCKCHIP_SPI_SR) & SR_SLAVE_TX_BUSY) &&
+			    !((readl_relaxed(rs->regs + ROCKCHIP_SPI_SR) & SR_BUSY)))
+				return;
+		} else {
+			if (!(readl_relaxed(rs->regs + ROCKCHIP_SPI_SR) & SR_BUSY))
+				return;
+		}
 	} while (!time_after(jiffies, timeout));
 
 	dev_warn(rs->dev, "spi controller is in busy state!\n");
@@ -226,7 +243,7 @@ static void rockchip_spi_set_cs(struct spi_device *spi, bool enable)
 {
 	struct spi_controller *ctlr = spi->controller;
 	struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
-	bool cs_asserted = !enable;
+	bool cs_asserted = spi->mode & SPI_CS_HIGH ? enable : !enable;
 
 	/* Return immediately for no-op */
 	if (cs_asserted == rs->cs_asserted[spi->chip_select])
@@ -236,11 +253,15 @@ static void rockchip_spi_set_cs(struct spi_device *spi, bool enable)
 		/* Keep things powered as long as CS is asserted */
 		pm_runtime_get_sync(rs->dev);
 
-		ROCKCHIP_SPI_SET_BITS(rs->regs + ROCKCHIP_SPI_SER,
-				      BIT(spi->chip_select));
+		if (spi->cs_gpiod)
+			ROCKCHIP_SPI_SET_BITS(rs->regs + ROCKCHIP_SPI_SER, 1);
+		else
+			ROCKCHIP_SPI_SET_BITS(rs->regs + ROCKCHIP_SPI_SER, BIT(spi->chip_select));
 	} else {
-		ROCKCHIP_SPI_CLR_BITS(rs->regs + ROCKCHIP_SPI_SER,
-				      BIT(spi->chip_select));
+		if (spi->cs_gpiod)
+			ROCKCHIP_SPI_CLR_BITS(rs->regs + ROCKCHIP_SPI_SER, 1);
+		else
+			ROCKCHIP_SPI_CLR_BITS(rs->regs + ROCKCHIP_SPI_SER, BIT(spi->chip_select));
 
 		/* Drop reference from when we first asserted CS */
 		pm_runtime_put(rs->dev);
@@ -259,8 +280,9 @@ static void rockchip_spi_handle_err(struct spi_controller *ctlr,
 	 */
 	spi_enable_chip(rs, false);
 
-	/* make sure all interrupts are masked */
+	/* make sure all interrupts are masked and status cleared */
 	writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR);
+	writel_relaxed(0xffffffff, rs->regs + ROCKCHIP_SPI_ICR);
 
 	if (atomic_read(&rs->state) & TXDMA)
 		dmaengine_terminate_async(ctlr->dma_tx);
@@ -327,6 +349,15 @@ static irqreturn_t rockchip_spi_isr(int irq, void *dev_id)
 	struct spi_controller *ctlr = dev_id;
 	struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
 
+	/* When int_cs_inactive comes, spi slave abort */
+	if (rs->cs_inactive && readl_relaxed(rs->regs + ROCKCHIP_SPI_IMR) & INT_CS_INACTIVE) {
+		ctlr->slave_abort(ctlr);
+		writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR);
+		writel_relaxed(0xffffffff, rs->regs + ROCKCHIP_SPI_ICR);
+
+		return IRQ_HANDLED;
+	}
+
 	if (rs->tx_left)
 		rockchip_spi_pio_writer(rs);
 
@@ -334,6 +365,7 @@ static irqreturn_t rockchip_spi_isr(int irq, void *dev_id)
 	if (!rs->rx_left) {
 		spi_enable_chip(rs, false);
 		writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR);
+		writel_relaxed(0xffffffff, rs->regs + ROCKCHIP_SPI_ICR);
 		spi_finalize_current_transfer(ctlr);
 	}
 
@@ -341,14 +373,18 @@ static irqreturn_t rockchip_spi_isr(int irq, void *dev_id)
 }
 
 static int rockchip_spi_prepare_irq(struct rockchip_spi *rs,
-		struct spi_transfer *xfer)
+				    struct spi_controller *ctlr,
+				    struct spi_transfer *xfer)
 {
 	rs->tx = xfer->tx_buf;
 	rs->rx = xfer->rx_buf;
 	rs->tx_left = rs->tx ? xfer->len / rs->n_bytes : 0;
 	rs->rx_left = xfer->len / rs->n_bytes;
 
-	writel_relaxed(INT_RF_FULL, rs->regs + ROCKCHIP_SPI_IMR);
+	if (rs->cs_inactive)
+		writel_relaxed(INT_RF_FULL | INT_CS_INACTIVE, rs->regs + ROCKCHIP_SPI_IMR);
+	else
+		writel_relaxed(INT_RF_FULL, rs->regs + ROCKCHIP_SPI_IMR);
 	spi_enable_chip(rs, true);
 
 	if (rs->tx_left)
@@ -367,6 +403,9 @@ static void rockchip_spi_dma_rxcb(void *data)
 	if (state & TXDMA && !rs->slave_abort)
 		return;
 
+	if (rs->cs_inactive)
+		writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR);
+
 	spi_enable_chip(rs, false);
 	spi_finalize_current_transfer(ctlr);
 }
@@ -381,7 +420,7 @@ static void rockchip_spi_dma_txcb(void *data)
 		return;
 
 	/* Wait until the FIFO data completely. */
-	wait_for_idle(rs);
+	wait_for_tx_idle(rs, ctlr->slave);
 
 	spi_enable_chip(rs, false);
 	spi_finalize_current_transfer(ctlr);
@@ -407,14 +446,16 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs,
 
 	atomic_set(&rs->state, 0);
 
+	rs->tx = xfer->tx_buf;
+	rs->rx = xfer->rx_buf;
+
 	rxdesc = NULL;
 	if (xfer->rx_buf) {
 		struct dma_slave_config rxconf = {
 			.direction = DMA_DEV_TO_MEM,
 			.src_addr = rs->dma_addr_rx,
 			.src_addr_width = rs->n_bytes,
-			.src_maxburst = rockchip_spi_calc_burst_size(xfer->len /
-								     rs->n_bytes),
+			.src_maxburst = rockchip_spi_calc_burst_size(xfer->len / rs->n_bytes),
 		};
 
 		dmaengine_slave_config(ctlr->dma_rx, &rxconf);
@@ -458,10 +499,13 @@ static int rockchip_spi_prepare_dma(struct rockchip_spi *rs,
 	/* rx must be started before tx due to spi instinct */
 	if (rxdesc) {
 		atomic_or(RXDMA, &rs->state);
-		dmaengine_submit(rxdesc);
+		ctlr->dma_rx->cookie = dmaengine_submit(rxdesc);
 		dma_async_issue_pending(ctlr->dma_rx);
 	}
 
+	if (rs->cs_inactive)
+		writel_relaxed(INT_CS_INACTIVE, rs->regs + ROCKCHIP_SPI_IMR);
+
 	spi_enable_chip(rs, true);
 
 	if (txdesc) {
@@ -493,6 +537,8 @@ static int rockchip_spi_config(struct rockchip_spi *rs,
 	cr0 |= (spi->mode & 0x3U) << CR0_SCPH_OFFSET;
 	if (spi->mode & SPI_LSB_FIRST)
 		cr0 |= CR0_FBM_LSB << CR0_FBM_OFFSET;
+	if (spi->mode & SPI_CS_HIGH)
+		cr0 |= BIT(spi->chip_select) << CR0_SOI_OFFSET;
 
 	if (xfer->rx_buf && xfer->tx_buf)
 		cr0 |= CR0_XFM_TR << CR0_XFM_OFFSET;
@@ -531,6 +577,19 @@ static int rockchip_spi_config(struct rockchip_spi *rs,
 			dmacr |= RF_DMA_EN;
 	}
 
+	/*
+	 * If speed is larger than IO_DRIVER_4MA_MAX_SCLK_OUT,
+	 * set higher driver strength.
+	 */
+	if (rs->high_speed_state) {
+		if (rs->freq > IO_DRIVER_4MA_MAX_SCLK_OUT)
+			pinctrl_select_state(rs->dev->pins->p,
+					     rs->high_speed_state);
+		else
+			pinctrl_select_state(rs->dev->pins->p,
+					     rs->dev->pins->default_state);
+	}
+
 	writel_relaxed(cr0, rs->regs + ROCKCHIP_SPI_CTRLR0);
 	writel_relaxed(cr1, rs->regs + ROCKCHIP_SPI_CTRLR1);
 
@@ -538,12 +597,12 @@ static int rockchip_spi_config(struct rockchip_spi *rs,
 	 * interrupt exactly when the fifo is full doesn't seem to work,
 	 * so we need the strict inequality here
 	 */
-	if (xfer->len < rs->fifo_len)
-		writel_relaxed(xfer->len - 1, rs->regs + ROCKCHIP_SPI_RXFTLR);
+	if ((xfer->len / rs->n_bytes) < rs->fifo_len)
+		writel_relaxed(xfer->len / rs->n_bytes - 1, rs->regs + ROCKCHIP_SPI_RXFTLR);
 	else
 		writel_relaxed(rs->fifo_len / 2 - 1, rs->regs + ROCKCHIP_SPI_RXFTLR);
 
-	writel_relaxed(rs->fifo_len / 2, rs->regs + ROCKCHIP_SPI_DMATDLR);
+	writel_relaxed(rs->fifo_len / 2 - 1, rs->regs + ROCKCHIP_SPI_DMATDLR);
 	writel_relaxed(rockchip_spi_calc_burst_size(xfer->len / rs->n_bytes) - 1,
 		       rs->regs + ROCKCHIP_SPI_DMARDLR);
 	writel_relaxed(dmacr, rs->regs + ROCKCHIP_SPI_DMACR);
@@ -566,7 +625,43 @@ static size_t rockchip_spi_max_transfer_size(struct spi_device *spi)
 static int rockchip_spi_slave_abort(struct spi_controller *ctlr)
 {
 	struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
+	u32 rx_fifo_left;
+	struct dma_tx_state state;
+	enum dma_status status;
+
+	/* Get current dma rx point */
+	if (atomic_read(&rs->state) & RXDMA) {
+		dmaengine_pause(ctlr->dma_rx);
+		status = dmaengine_tx_status(ctlr->dma_rx, ctlr->dma_rx->cookie, &state);
+		if (status == DMA_ERROR) {
+			rs->rx = rs->xfer->rx_buf;
+			rs->xfer->len = 0;
+			rx_fifo_left = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXFLR);
+			for (; rx_fifo_left; rx_fifo_left--)
+				readl_relaxed(rs->regs + ROCKCHIP_SPI_RXDR);
+			goto out;
+		} else {
+			rs->rx += rs->xfer->len - rs->n_bytes * state.residue;
+		}
+	}
 
+	/* Get the valid data left in rx fifo and set rs->xfer->len real rx size */
+	if (rs->rx) {
+		rx_fifo_left = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXFLR);
+		for (; rx_fifo_left; rx_fifo_left--) {
+			u32 rxw = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXDR);
+
+			if (rs->n_bytes == 1)
+				*(u8 *)rs->rx = (u8)rxw;
+			else
+				*(u16 *)rs->rx = (u16)rxw;
+			rs->rx += rs->n_bytes;
+		}
+
+		rs->xfer->len = (unsigned int)(rs->rx - rs->xfer->rx_buf);
+	}
+
+out:
 	if (atomic_read(&rs->state) & RXDMA)
 		dmaengine_terminate_sync(ctlr->dma_rx);
 	if (atomic_read(&rs->state) & TXDMA)
@@ -608,7 +703,7 @@ static int rockchip_spi_transfer_one(
 	}
 
 	rs->n_bytes = xfer->bits_per_word <= 8 ? 1 : 2;
-
+	rs->xfer = xfer;
 	use_dma = ctlr->can_dma ? ctlr->can_dma(ctlr, spi, xfer) : false;
 
 	ret = rockchip_spi_config(rs, spi, xfer, use_dma, ctlr->slave);
@@ -618,7 +713,7 @@ static int rockchip_spi_transfer_one(
 	if (use_dma)
 		return rockchip_spi_prepare_dma(rs, ctlr, xfer);
 
-	return rockchip_spi_prepare_irq(rs, xfer);
+	return rockchip_spi_prepare_irq(rs, ctlr, xfer);
 }
 
 static bool rockchip_spi_can_dma(struct spi_controller *ctlr,
@@ -635,6 +730,26 @@ static bool rockchip_spi_can_dma(struct spi_controller *ctlr,
 	return xfer->len / bytes_per_word >= rs->fifo_len;
 }
 
+static int rockchip_spi_setup(struct spi_device *spi)
+{
+	struct rockchip_spi *rs = spi_controller_get_devdata(spi->controller);
+	u32 cr0;
+
+	pm_runtime_get_sync(rs->dev);
+
+	cr0 = readl_relaxed(rs->regs + ROCKCHIP_SPI_CTRLR0);
+
+	cr0 |= ((spi->mode & 0x3) << CR0_SCPH_OFFSET);
+	if (spi->mode & SPI_CS_HIGH)
+		cr0 |= BIT(spi->chip_select) << CR0_SOI_OFFSET;
+
+	writel_relaxed(cr0, rs->regs + ROCKCHIP_SPI_CTRLR0);
+
+	pm_runtime_put(rs->dev);
+
+	return 0;
+}
+
 static int rockchip_spi_probe(struct platform_device *pdev)
 {
 	int ret;
@@ -644,6 +759,7 @@ static int rockchip_spi_probe(struct platform_device *pdev)
 	struct device_node *np = pdev->dev.of_node;
 	u32 rsd_nsecs, num_cs;
 	bool slave_mode;
+	struct pinctrl *pinctrl = NULL;
 
 	slave_mode = of_property_read_bool(np, "spi-slave");
 
@@ -760,6 +876,7 @@ static int rockchip_spi_probe(struct platform_device *pdev)
 	ctlr->min_speed_hz = rs->freq / BAUDR_SCKDV_MAX;
 	ctlr->max_speed_hz = min(rs->freq / BAUDR_SCKDV_MIN, MAX_SCLK_OUT);
 
+	ctlr->setup = rockchip_spi_setup;
 	ctlr->set_cs = rockchip_spi_set_cs;
 	ctlr->transfer_one = rockchip_spi_transfer_one;
 	ctlr->max_transfer_size = rockchip_spi_max_transfer_size;
@@ -792,6 +909,28 @@ static int rockchip_spi_probe(struct platform_device *pdev)
 		ctlr->can_dma = rockchip_spi_can_dma;
 	}
 
+	switch (readl_relaxed(rs->regs + ROCKCHIP_SPI_VERSION)) {
+	case ROCKCHIP_SPI_VER2_TYPE2:
+		ctlr->mode_bits |= SPI_CS_HIGH;
+		if (ctlr->can_dma && slave_mode)
+			rs->cs_inactive = true;
+		else
+			rs->cs_inactive = false;
+		break;
+	default:
+		rs->cs_inactive = false;
+		break;
+	}
+
+	pinctrl = devm_pinctrl_get(&pdev->dev);
+	if (!IS_ERR(pinctrl)) {
+		rs->high_speed_state = pinctrl_lookup_state(pinctrl, "high_speed");
+		if (IS_ERR_OR_NULL(rs->high_speed_state)) {
+			dev_warn(&pdev->dev, "no high_speed pinctrl state\n");
+			rs->high_speed_state = NULL;
+		}
+	}
+
 	ret = devm_spi_register_controller(&pdev->dev, ctlr);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Failed to register controller\n");
@@ -847,14 +986,14 @@ static int rockchip_spi_suspend(struct device *dev)
 {
 	int ret;
 	struct spi_controller *ctlr = dev_get_drvdata(dev);
+	struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
 
 	ret = spi_controller_suspend(ctlr);
 	if (ret < 0)
 		return ret;
 
-	ret = pm_runtime_force_suspend(dev);
-	if (ret < 0)
-		return ret;
+	clk_disable_unprepare(rs->spiclk);
+	clk_disable_unprepare(rs->apb_pclk);
 
 	pinctrl_pm_select_sleep_state(dev);
 
@@ -869,10 +1008,14 @@ static int rockchip_spi_resume(struct device *dev)
 
 	pinctrl_pm_select_default_state(dev);
 
-	ret = pm_runtime_force_resume(dev);
+	ret = clk_prepare_enable(rs->apb_pclk);
 	if (ret < 0)
 		return ret;
 
+	ret = clk_prepare_enable(rs->spiclk);
+	if (ret < 0)
+		clk_disable_unprepare(rs->apb_pclk);
+
 	ret = spi_controller_resume(ctlr);
 	if (ret < 0) {
 		clk_disable_unprepare(rs->spiclk);
@@ -914,7 +1057,7 @@ static int rockchip_spi_runtime_resume(struct device *dev)
 #endif /* CONFIG_PM */
 
 static const struct dev_pm_ops rockchip_spi_pm = {
-	SET_SYSTEM_SLEEP_PM_OPS(rockchip_spi_suspend, rockchip_spi_resume)
+	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rockchip_spi_suspend, rockchip_spi_resume)
 	SET_RUNTIME_PM_OPS(rockchip_spi_runtime_suspend,
 			   rockchip_spi_runtime_resume, NULL)
 };
@@ -930,7 +1073,9 @@ static const struct of_device_id rockchip_spi_dt_match[] = {
 	{ .compatible = "rockchip,rk3328-spi", },
 	{ .compatible = "rockchip,rk3368-spi", },
 	{ .compatible = "rockchip,rk3399-spi", },
+	{ .compatible = "rockchip,rv1106-spi", },
 	{ .compatible = "rockchip,rv1108-spi", },
+	{ .compatible = "rockchip,rv1126-spi", },
 	{ },
 };
 MODULE_DEVICE_TABLE(of, rockchip_spi_dt_match);
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 9c5ec9943..ea5293934 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -693,6 +693,7 @@ static const struct of_device_id spidev_dt_ids[] = {
 	{ .compatible = "lwn,bk4" },
 	{ .compatible = "dh,dhcom-board" },
 	{ .compatible = "menlo,m53cpld" },
+	{ .compatible = "rockchip,spidev" },
 	{},
 };
 MODULE_DEVICE_TABLE(of, spidev_dt_ids);
diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile
index 3b66cd0b0..ca5df9abd 100644
--- a/drivers/staging/android/Makefile
+++ b/drivers/staging/android/Makefile
@@ -4,3 +4,4 @@ ccflags-y += -I$(src)			# needed for trace events
 obj-y					+= ion/
 
 obj-$(CONFIG_ASHMEM)			+= ashmem.o
+obj-$(CONFIG_DEBUG_KINFO)	+= debug_kinfo.o
diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c
index aa9e0e31e..a328dec78 100644
--- a/drivers/thermal/rockchip_thermal.c
+++ b/drivers/thermal/rockchip_thermal.c
@@ -18,6 +18,7 @@
 #include <linux/thermal.h>
 #include <linux/mfd/syscon.h>
 #include <linux/pinctrl/consumer.h>
+#include <linux/nvmem-consumer.h>
 
 /*
  * If the temperature over a period of time High,
@@ -26,7 +27,7 @@
  */
 enum tshut_mode {
 	TSHUT_MODE_CRU = 0,
-	TSHUT_MODE_GPIO,
+	TSHUT_MODE_OTP,
 };
 
 /*
@@ -61,22 +62,26 @@ enum adc_sort_mode {
 #include "thermal_hwmon.h"
 
 /**
- * The max sensors is two in rockchip SoCs.
- * Two sensors: CPU and GPU sensor.
+ * The max sensors is seven in rockchip SoCs.
  */
-#define SOC_MAX_SENSORS	2
+#define SOC_MAX_SENSORS	7
 
 /**
  * struct chip_tsadc_table - hold information about chip-specific differences
  * @id: conversion table
  * @length: size of conversion table
  * @data_mask: mask to apply on data inputs
+ * @kNum: linear parameter k
+ * @bNum: linear parameter b
  * @mode: sort mode of this adc variant (incrementing or decrementing)
  */
 struct chip_tsadc_table {
 	const struct tsadc_table *id;
 	unsigned int length;
 	u32 data_mask;
+	/* Tsadc is linear, using linear parameters */
+	int kNum;
+	int bNum;
 	enum adc_sort_mode mode;
 };
 
@@ -94,6 +99,8 @@ struct chip_tsadc_table {
  * @set_alarm_temp: set the high temperature interrupt
  * @set_tshut_temp: set the hardware-controlled shutdown temperature
  * @set_tshut_mode: set the hardware-controlled shutdown mode
+ * @get_trim_code: get the trim code by otp value
+ * @trim_temp: get trim temp by trim code
  * @table: the chip-specific conversion table
  */
 struct rockchip_tsadc_chip {
@@ -119,7 +126,11 @@ struct rockchip_tsadc_chip {
 			      int chn, void __iomem *reg, int temp);
 	int (*set_tshut_temp)(const struct chip_tsadc_table *table,
 			      int chn, void __iomem *reg, int temp);
-	void (*set_tshut_mode)(int chn, void __iomem *reg, enum tshut_mode m);
+	void (*set_tshut_mode)(struct regmap *grf, int chn,
+			       void __iomem *reg, enum tshut_mode m);
+	int (*get_trim_code)(struct platform_device *pdev,
+			     int code, int trim_base);
+	int (*trim_temp)(struct platform_device *pdev);
 
 	/* Per-table methods */
 	struct chip_tsadc_table table;
@@ -143,13 +154,18 @@ struct rockchip_thermal_sensor {
  * @pdev: platform device of thermal
  * @reset: the reset controller of tsadc
  * @sensors: array of thermal sensors
- * @clk: the controller clock is divided by the exteral 24MHz
- * @pclk: the advanced peripherals bus clock
+ * @clk: the bulk clk of tsadc, include controller clock and peripherals bus clock
+ * @num_clks: the number of tsadc clks
  * @grf: the general register file will be used to do static set by software
  * @regs: the base address of tsadc controller
  * @tshut_temp: the hardware-controlled shutdown temperature value
+ * @trim: trimmed value
  * @tshut_mode: the hardware-controlled shutdown mode (0:CRU 1:GPIO)
  * @tshut_polarity: the hardware-controlled active polarity (0:LOW 1:HIGH)
+ * @pinctrl: the pinctrl of tsadc
+ * @gpio_state: pinctrl select gpio function
+ * @otp_state: pinctrl select otp out function
+ * @panic_nb: panic notifier block
  */
 struct rockchip_thermal_data {
 	const struct rockchip_tsadc_chip *chip;
@@ -158,15 +174,21 @@ struct rockchip_thermal_data {
 
 	struct rockchip_thermal_sensor sensors[SOC_MAX_SENSORS];
 
-	struct clk *clk;
-	struct clk *pclk;
+	struct clk_bulk_data *clks;
+	int num_clks;
 
 	struct regmap *grf;
 	void __iomem *regs;
 
 	int tshut_temp;
+	int trim;
 	enum tshut_mode tshut_mode;
 	enum tshut_polarity tshut_polarity;
+	struct pinctrl *pinctrl;
+	struct pinctrl_state *gpio_state;
+	struct pinctrl_state *otp_state;
+
+	struct notifier_block panic_nb;
 };
 
 /**
@@ -180,29 +202,49 @@ struct rockchip_thermal_data {
 #define TSADCV2_AUTO_CON			0x04
 #define TSADCV2_INT_EN				0x08
 #define TSADCV2_INT_PD				0x0c
+#define TSADCV3_AUTO_SRC_CON			0x0c
+#define TSADCV3_HT_INT_EN			0x14
+#define TSADCV3_HSHUT_GPIO_INT_EN		0x18
+#define TSADCV3_HSHUT_CRU_INT_EN		0x1c
+#define TSADCV3_INT_PD				0x24
+#define TSADCV3_HSHUT_PD			0x28
 #define TSADCV2_DATA(chn)			(0x20 + (chn) * 0x04)
 #define TSADCV2_COMP_INT(chn)		        (0x30 + (chn) * 0x04)
 #define TSADCV2_COMP_SHUT(chn)		        (0x40 + (chn) * 0x04)
+#define TSADCV3_DATA(chn)			(0x2c + (chn) * 0x04)
+#define TSADCV3_COMP_INT(chn)		        (0x6c + (chn) * 0x04)
+#define TSADCV3_COMP_SHUT(chn)		        (0x10c + (chn) * 0x04)
 #define TSADCV2_HIGHT_INT_DEBOUNCE		0x60
 #define TSADCV2_HIGHT_TSHUT_DEBOUNCE		0x64
+#define TSADCV3_HIGHT_INT_DEBOUNCE		0x14c
+#define TSADCV3_HIGHT_TSHUT_DEBOUNCE		0x150
 #define TSADCV2_AUTO_PERIOD			0x68
 #define TSADCV2_AUTO_PERIOD_HT			0x6c
+#define TSADCV3_AUTO_PERIOD			0x154
+#define TSADCV3_AUTO_PERIOD_HT			0x158
 
 #define TSADCV2_AUTO_EN				BIT(0)
+#define TSADCV2_AUTO_EN_MASK			BIT(16)
 #define TSADCV2_AUTO_SRC_EN(chn)		BIT(4 + (chn))
+#define TSADCV3_AUTO_SRC_EN(chn)		BIT(chn)
+#define TSADCV3_AUTO_SRC_EN_MASK(chn)		BIT(16 + chn)
 #define TSADCV2_AUTO_TSHUT_POLARITY_HIGH	BIT(8)
+#define TSADCV2_AUTO_TSHUT_POLARITY_MASK	BIT(24)
 
 #define TSADCV3_AUTO_Q_SEL_EN			BIT(1)
 
 #define TSADCV2_INT_SRC_EN(chn)			BIT(chn)
+#define TSADCV2_INT_SRC_EN_MASK(chn)		BIT(16 + (chn))
 #define TSADCV2_SHUT_2GPIO_SRC_EN(chn)		BIT(4 + (chn))
 #define TSADCV2_SHUT_2CRU_SRC_EN(chn)		BIT(8 + (chn))
 
 #define TSADCV2_INT_PD_CLEAR_MASK		~BIT(8)
 #define TSADCV3_INT_PD_CLEAR_MASK		~BIT(16)
+#define TSADCV4_INT_PD_CLEAR_MASK		0xffffffff
 
 #define TSADCV2_DATA_MASK			0xfff
 #define TSADCV3_DATA_MASK			0x3ff
+#define TSADCV4_DATA_MASK			0x1ff
 
 #define TSADCV2_HIGHT_INT_DEBOUNCE_COUNT	4
 #define TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT	4
@@ -210,8 +252,13 @@ struct rockchip_thermal_data {
 #define TSADCV2_AUTO_PERIOD_HT_TIME		50  /* 50ms */
 #define TSADCV3_AUTO_PERIOD_TIME		1875 /* 2.5ms */
 #define TSADCV3_AUTO_PERIOD_HT_TIME		1875 /* 2.5ms */
+#define TSADCV5_AUTO_PERIOD_TIME		1622 /* 2.5ms */
+#define TSADCV5_AUTO_PERIOD_HT_TIME		1622 /* 2.5ms */
+#define TSADCV6_AUTO_PERIOD_TIME		5000 /* 2.5ms */
+#define TSADCV6_AUTO_PERIOD_HT_TIME		5000 /* 2.5ms */
 
 #define TSADCV2_USER_INTER_PD_SOC		0x340 /* 13 clocks */
+#define TSADCV5_USER_INTER_PD_SOC		0xfc0 /* 97us, at least 90us */
 
 #define GRF_SARADC_TESTBIT			0x0e644
 #define GRF_TSADC_TESTBIT_L			0x0e648
@@ -219,13 +266,33 @@ struct rockchip_thermal_data {
 
 #define PX30_GRF_SOC_CON2			0x0408
 
+#define RK1808_BUS_GRF_SOC_CON0			0x0400
+
+#define RK3568_GRF_TSADC_CON			0x0600
+#define RK3568_GRF_TSADC_ANA_REG0		(0x10001 << 0)
+#define RK3568_GRF_TSADC_ANA_REG1		(0x10001 << 1)
+#define RK3568_GRF_TSADC_ANA_REG2		(0x10001 << 2)
+#define RK3568_GRF_TSADC_TSEN			(0x10001 << 8)
+
+#define RV1126_GRF0_TSADC_CON			0x0100
+
+#define RV1126_GRF0_TSADC_TRM			(0xff0077 << 0)
+#define RV1126_GRF0_TSADC_SHUT_2CRU		(0x30003 << 10)
+#define RV1126_GRF0_TSADC_SHUT_2GPIO		(0x70007 << 12)
+
 #define GRF_SARADC_TESTBIT_ON			(0x10001 << 2)
 #define GRF_TSADC_TESTBIT_H_ON			(0x10001 << 2)
+#define GRF_TSADC_BANDGAP_CHOPPER_EN		(0x10001 << 2)
 #define GRF_TSADC_VCM_EN_L			(0x10001 << 7)
 #define GRF_TSADC_VCM_EN_H			(0x10001 << 7)
 
 #define GRF_CON_TSADC_CH_INV			(0x10001 << 1)
 
+#define MIN_TEMP				(-40000)
+#define LOWEST_TEMP				(-273000)
+#define MAX_TEMP				(125000)
+#define MAX_ENV_TEMP				(85000)
+
 /**
  * struct tsadc_table - code to temperature conversion table
  * @code: the value of adc channel
@@ -241,6 +308,7 @@ struct tsadc_table {
 	int temp;
 };
 
+
 static const struct tsadc_table rv1108_table[] = {
 	{0, -40000},
 	{374, -40000},
@@ -280,6 +348,45 @@ static const struct tsadc_table rv1108_table[] = {
 	{TSADCV2_DATA_MASK, 125000},
 };
 
+static const struct tsadc_table rk1808_code_table[] = {
+	{0, -40000},
+	{3455, -40000},
+	{3463, -35000},
+	{3471, -30000},
+	{3479, -25000},
+	{3487, -20000},
+	{3495, -15000},
+	{3503, -10000},
+	{3511, -5000},
+	{3519, 0},
+	{3527, 5000},
+	{3535, 10000},
+	{3543, 15000},
+	{3551, 20000},
+	{3559, 25000},
+	{3567, 30000},
+	{3576, 35000},
+	{3584, 40000},
+	{3592, 45000},
+	{3600, 50000},
+	{3609, 55000},
+	{3617, 60000},
+	{3625, 65000},
+	{3633, 70000},
+	{3642, 75000},
+	{3650, 80000},
+	{3659, 85000},
+	{3667, 90000},
+	{3675, 95000},
+	{3684, 100000},
+	{3692, 105000},
+	{3701, 110000},
+	{3709, 115000},
+	{3718, 120000},
+	{3726, 125000},
+	{TSADCV2_DATA_MASK, 125000},
+};
+
 static const struct tsadc_table rk3228_code_table[] = {
 	{0, -40000},
 	{588, -40000},
@@ -474,6 +581,54 @@ static const struct tsadc_table rk3399_code_table[] = {
 	{TSADCV3_DATA_MASK, 125000},
 };
 
+static const struct tsadc_table rk3568_code_table[] = {
+	{0, -40000},
+	{1584, -40000},
+	{1620, -35000},
+	{1652, -30000},
+	{1688, -25000},
+	{1720, -20000},
+	{1756, -15000},
+	{1788, -10000},
+	{1824, -5000},
+	{1856, 0},
+	{1892, 5000},
+	{1924, 10000},
+	{1956, 15000},
+	{1992, 20000},
+	{2024, 25000},
+	{2060, 30000},
+	{2092, 35000},
+	{2128, 40000},
+	{2160, 45000},
+	{2196, 50000},
+	{2228, 55000},
+	{2264, 60000},
+	{2300, 65000},
+	{2332, 70000},
+	{2368, 75000},
+	{2400, 80000},
+	{2436, 85000},
+	{2468, 90000},
+	{2500, 95000},
+	{2536, 100000},
+	{2572, 105000},
+	{2604, 110000},
+	{2636, 115000},
+	{2672, 120000},
+	{2704, 125000},
+	{TSADCV2_DATA_MASK, 125000},
+};
+
+static const struct tsadc_table rk3588_code_table[] = {
+	{0, -40000},
+	{220, -40000},
+	{285, 25000},
+	{345, 85000},
+	{385, 125000},
+	{TSADCV4_DATA_MASK, 125000},
+};
+
 static u32 rk_tsadcv2_temp_to_code(const struct chip_tsadc_table *table,
 				   int temp)
 {
@@ -482,6 +637,9 @@ static u32 rk_tsadcv2_temp_to_code(const struct chip_tsadc_table *table,
 	unsigned int denom;
 	u32 error = table->data_mask;
 
+	if (table->kNum)
+		return (((temp / 1000) * table->kNum) / 1000 + table->bNum);
+
 	low = 0;
 	high = (table->length - 1) - 1; /* ignore the last check for table */
 	mid = (high + low) / 2;
@@ -535,6 +693,13 @@ static int rk_tsadcv2_code_to_temp(const struct chip_tsadc_table *table,
 	unsigned int num;
 	unsigned long denom;
 
+	if (table->kNum) {
+		*temp = (((int)code - table->bNum) * 10000 / table->kNum) * 100;
+		if (*temp < MIN_TEMP || *temp > MAX_TEMP)
+			return -EAGAIN;
+		return 0;
+	}
+
 	WARN_ON(table->length < 2);
 
 	switch (table->mode) {
@@ -701,6 +866,89 @@ static void rk_tsadcv4_initialize(struct regmap *grf, void __iomem *regs,
 	regmap_write(grf, PX30_GRF_SOC_CON2, GRF_CON_TSADC_CH_INV);
 }
 
+static void rk_tsadcv5_initialize(struct regmap *grf, void __iomem *regs,
+				  enum tshut_polarity tshut_polarity)
+{
+	if (tshut_polarity == TSHUT_HIGH_ACTIVE)
+		writel_relaxed(0U | TSADCV2_AUTO_TSHUT_POLARITY_HIGH,
+			       regs + TSADCV2_AUTO_CON);
+	else
+		writel_relaxed(0U & ~TSADCV2_AUTO_TSHUT_POLARITY_HIGH,
+			       regs + TSADCV2_AUTO_CON);
+
+	writel_relaxed(TSADCV5_USER_INTER_PD_SOC, regs + TSADCV2_USER_CON);
+
+	writel_relaxed(TSADCV5_AUTO_PERIOD_TIME, regs + TSADCV2_AUTO_PERIOD);
+	writel_relaxed(TSADCV2_HIGHT_INT_DEBOUNCE_COUNT,
+		       regs + TSADCV2_HIGHT_INT_DEBOUNCE);
+	writel_relaxed(TSADCV5_AUTO_PERIOD_HT_TIME,
+		       regs + TSADCV2_AUTO_PERIOD_HT);
+	writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT,
+		       regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE);
+
+	if (!IS_ERR(grf))
+		regmap_write(grf, RK1808_BUS_GRF_SOC_CON0,
+			     GRF_TSADC_BANDGAP_CHOPPER_EN);
+}
+
+static void rk_tsadcv6_initialize(struct regmap *grf, void __iomem *regs,
+				  enum tshut_polarity tshut_polarity)
+{
+	rk_tsadcv2_initialize(grf, regs, tshut_polarity);
+
+	if (!IS_ERR(grf))
+		regmap_write(grf, RV1126_GRF0_TSADC_CON,
+			     RV1126_GRF0_TSADC_TRM);
+}
+
+static void rk_tsadcv7_initialize(struct regmap *grf, void __iomem *regs,
+				  enum tshut_polarity tshut_polarity)
+{
+	writel_relaxed(TSADCV5_USER_INTER_PD_SOC, regs + TSADCV2_USER_CON);
+	writel_relaxed(TSADCV5_AUTO_PERIOD_TIME, regs + TSADCV2_AUTO_PERIOD);
+	writel_relaxed(TSADCV2_HIGHT_INT_DEBOUNCE_COUNT,
+		       regs + TSADCV2_HIGHT_INT_DEBOUNCE);
+	writel_relaxed(TSADCV5_AUTO_PERIOD_HT_TIME,
+		       regs + TSADCV2_AUTO_PERIOD_HT);
+	writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT,
+		       regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE);
+
+	if (tshut_polarity == TSHUT_HIGH_ACTIVE)
+		writel_relaxed(0U | TSADCV2_AUTO_TSHUT_POLARITY_HIGH,
+			       regs + TSADCV2_AUTO_CON);
+	else
+		writel_relaxed(0U & ~TSADCV2_AUTO_TSHUT_POLARITY_HIGH,
+			       regs + TSADCV2_AUTO_CON);
+
+	if (!IS_ERR(grf)) {
+		regmap_write(grf, RK3568_GRF_TSADC_CON, RK3568_GRF_TSADC_TSEN);
+		udelay(15);
+		regmap_write(grf, RK3568_GRF_TSADC_CON, RK3568_GRF_TSADC_ANA_REG0);
+		regmap_write(grf, RK3568_GRF_TSADC_CON, RK3568_GRF_TSADC_ANA_REG1);
+		regmap_write(grf, RK3568_GRF_TSADC_CON, RK3568_GRF_TSADC_ANA_REG2);
+		usleep_range(100, 200);
+	}
+}
+
+static void rk_tsadcv8_initialize(struct regmap *grf, void __iomem *regs,
+				  enum tshut_polarity tshut_polarity)
+{
+	writel_relaxed(TSADCV6_AUTO_PERIOD_TIME, regs + TSADCV3_AUTO_PERIOD);
+	writel_relaxed(TSADCV6_AUTO_PERIOD_HT_TIME,
+		       regs + TSADCV3_AUTO_PERIOD_HT);
+	writel_relaxed(TSADCV2_HIGHT_INT_DEBOUNCE_COUNT,
+		       regs + TSADCV3_HIGHT_INT_DEBOUNCE);
+	writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT,
+		       regs + TSADCV3_HIGHT_TSHUT_DEBOUNCE);
+	if (tshut_polarity == TSHUT_HIGH_ACTIVE)
+		writel_relaxed(TSADCV2_AUTO_TSHUT_POLARITY_HIGH |
+			       TSADCV2_AUTO_TSHUT_POLARITY_MASK,
+			       regs + TSADCV2_AUTO_CON);
+	else
+		writel_relaxed(TSADCV2_AUTO_TSHUT_POLARITY_MASK,
+			       regs + TSADCV2_AUTO_CON);
+}
+
 static void rk_tsadcv2_irq_ack(void __iomem *regs)
 {
 	u32 val;
@@ -717,6 +965,17 @@ static void rk_tsadcv3_irq_ack(void __iomem *regs)
 	writel_relaxed(val & TSADCV3_INT_PD_CLEAR_MASK, regs + TSADCV2_INT_PD);
 }
 
+static void rk_tsadcv4_irq_ack(void __iomem *regs)
+{
+	u32 val;
+
+	val = readl_relaxed(regs + TSADCV3_INT_PD);
+	writel_relaxed(val & TSADCV4_INT_PD_CLEAR_MASK, regs + TSADCV3_INT_PD);
+	val = readl_relaxed(regs + TSADCV3_HSHUT_PD);
+	writel_relaxed(val & TSADCV3_INT_PD_CLEAR_MASK,
+		       regs + TSADCV3_HSHUT_PD);
+}
+
 static void rk_tsadcv2_control(void __iomem *regs, bool enable)
 {
 	u32 val;
@@ -752,6 +1011,18 @@ static void rk_tsadcv3_control(void __iomem *regs, bool enable)
 	writel_relaxed(val, regs + TSADCV2_AUTO_CON);
 }
 
+static void rk_tsadcv4_control(void __iomem *regs, bool enable)
+{
+	u32 val;
+
+	if (enable)
+		val = TSADCV2_AUTO_EN | TSADCV2_AUTO_EN_MASK;
+	else
+		val = TSADCV2_AUTO_EN_MASK;
+
+	writel_relaxed(val, regs + TSADCV2_AUTO_CON);
+}
+
 static int rk_tsadcv2_get_temp(const struct chip_tsadc_table *table,
 			       int chn, void __iomem *regs, int *temp)
 {
@@ -762,6 +1033,16 @@ static int rk_tsadcv2_get_temp(const struct chip_tsadc_table *table,
 	return rk_tsadcv2_code_to_temp(table, val, temp);
 }
 
+static int rk_tsadcv4_get_temp(const struct chip_tsadc_table *table,
+			       int chn, void __iomem *regs, int *temp)
+{
+	u32 val;
+
+	val = readl_relaxed(regs + TSADCV3_DATA(chn));
+
+	return rk_tsadcv2_code_to_temp(table, val, temp);
+}
+
 static int rk_tsadcv2_alarm_temp(const struct chip_tsadc_table *table,
 				 int chn, void __iomem *regs, int temp)
 {
@@ -796,6 +1077,33 @@ static int rk_tsadcv2_alarm_temp(const struct chip_tsadc_table *table,
 	return 0;
 }
 
+static int rk_tsadcv3_alarm_temp(const struct chip_tsadc_table *table,
+				 int chn, void __iomem *regs, int temp)
+{
+	u32 alarm_value;
+
+	/*
+	 * In some cases, some sensors didn't need the trip points, the
+	 * set_trips will pass {-INT_MAX, INT_MAX} to trigger tsadc alarm
+	 * in the end, ignore this case and disable the high temperature
+	 * interrupt.
+	 */
+	if (temp == INT_MAX) {
+		writel_relaxed(TSADCV2_INT_SRC_EN_MASK(chn),
+			       regs + TSADCV3_HT_INT_EN);
+		return 0;
+	}
+	/* Make sure the value is valid */
+	alarm_value = rk_tsadcv2_temp_to_code(table, temp);
+	if (alarm_value == table->data_mask)
+		return -ERANGE;
+	writel_relaxed(alarm_value & table->data_mask,
+		       regs + TSADCV3_COMP_INT(chn));
+	writel_relaxed(TSADCV2_INT_SRC_EN(chn) | TSADCV2_INT_SRC_EN_MASK(chn),
+		       regs + TSADCV3_HT_INT_EN);
+	return 0;
+}
+
 static int rk_tsadcv2_tshut_temp(const struct chip_tsadc_table *table,
 				 int chn, void __iomem *regs, int temp)
 {
@@ -815,13 +1123,33 @@ static int rk_tsadcv2_tshut_temp(const struct chip_tsadc_table *table,
 	return 0;
 }
 
-static void rk_tsadcv2_tshut_mode(int chn, void __iomem *regs,
+static int rk_tsadcv3_tshut_temp(const struct chip_tsadc_table *table,
+				 int chn, void __iomem *regs, int temp)
+{
+	u32 tshut_value;
+
+	/* Make sure the value is valid */
+	tshut_value = rk_tsadcv2_temp_to_code(table, temp);
+	if (tshut_value == table->data_mask)
+		return -ERANGE;
+
+	writel_relaxed(tshut_value, regs + TSADCV3_COMP_SHUT(chn));
+
+	/* TSHUT will be valid */
+	writel_relaxed(TSADCV3_AUTO_SRC_EN(chn) | TSADCV3_AUTO_SRC_EN_MASK(chn),
+		       regs + TSADCV3_AUTO_SRC_CON);
+
+	return 0;
+}
+
+static void rk_tsadcv2_tshut_mode(struct regmap *grf, int chn,
+				  void __iomem *regs,
 				  enum tshut_mode mode)
 {
 	u32 val;
 
 	val = readl_relaxed(regs + TSADCV2_INT_EN);
-	if (mode == TSHUT_MODE_GPIO) {
+	if (mode == TSHUT_MODE_OTP) {
 		val &= ~TSADCV2_SHUT_2CRU_SRC_EN(chn);
 		val |= TSADCV2_SHUT_2GPIO_SRC_EN(chn);
 	} else {
@@ -832,6 +1160,68 @@ static void rk_tsadcv2_tshut_mode(int chn, void __iomem *regs,
 	writel_relaxed(val, regs + TSADCV2_INT_EN);
 }
 
+static void rk_tsadcv3_tshut_mode(struct regmap *grf, int chn,
+				  void __iomem *regs,
+				  enum tshut_mode mode)
+{
+	u32 val;
+
+	val = readl_relaxed(regs + TSADCV2_INT_EN);
+	if (mode == TSHUT_MODE_OTP) {
+		val &= ~TSADCV2_SHUT_2CRU_SRC_EN(chn);
+		val |= TSADCV2_SHUT_2GPIO_SRC_EN(chn);
+		if (!IS_ERR(grf))
+			regmap_write(grf, RV1126_GRF0_TSADC_CON,
+				     RV1126_GRF0_TSADC_SHUT_2GPIO);
+	} else {
+		val &= ~TSADCV2_SHUT_2GPIO_SRC_EN(chn);
+		val |= TSADCV2_SHUT_2CRU_SRC_EN(chn);
+		if (!IS_ERR(grf))
+			regmap_write(grf, RV1126_GRF0_TSADC_CON,
+				     RV1126_GRF0_TSADC_SHUT_2CRU);
+	}
+
+	writel_relaxed(val, regs + TSADCV2_INT_EN);
+}
+
+static void rk_tsadcv4_tshut_mode(struct regmap *grf, int chn,
+				  void __iomem *regs,
+				  enum tshut_mode mode)
+{
+	u32 val_gpio, val_cru;
+
+	if (mode == TSHUT_MODE_OTP) {
+		val_gpio = TSADCV2_INT_SRC_EN(chn) | TSADCV2_INT_SRC_EN_MASK(chn);
+		val_cru = TSADCV2_INT_SRC_EN_MASK(chn);
+	} else {
+		val_cru = TSADCV2_INT_SRC_EN(chn) | TSADCV2_INT_SRC_EN_MASK(chn);
+		val_gpio = TSADCV2_INT_SRC_EN_MASK(chn);
+	}
+	writel_relaxed(val_gpio, regs + TSADCV3_HSHUT_GPIO_INT_EN);
+	writel_relaxed(val_cru, regs + TSADCV3_HSHUT_CRU_INT_EN);
+}
+
+static int rk_tsadcv1_get_trim_code(struct platform_device *pdev,
+				    int code, int trim_base)
+{
+	struct rockchip_thermal_data *thermal = platform_get_drvdata(pdev);
+	const struct chip_tsadc_table *table = &thermal->chip->table;
+	u32 base_code;
+	int trim_code;
+
+	base_code = trim_base * table->kNum / 1000 + table->bNum;
+	trim_code = code - base_code - 10;
+
+	return trim_code;
+}
+
+static int rk_tsadcv1_trim_temp(struct platform_device *pdev)
+{
+	struct rockchip_thermal_data *thermal = platform_get_drvdata(pdev);
+
+	return thermal->trim * 500;
+}
+
 static const struct rockchip_tsadc_chip px30_tsadc_data = {
 	.chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */
 	.chn_id[SENSOR_GPU] = 1, /* gpu sensor is channel 1 */
@@ -860,7 +1250,7 @@ static const struct rockchip_tsadc_chip rv1108_tsadc_data = {
 	.chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */
 	.chn_num = 1, /* one channel for tsadc */
 
-	.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
+	.tshut_mode = TSHUT_MODE_OTP, /* default TSHUT via GPIO give PMIC */
 	.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
 	.tshut_temp = 95000,
 
@@ -880,11 +1270,61 @@ static const struct rockchip_tsadc_chip rv1108_tsadc_data = {
 	},
 };
 
+static const struct rockchip_tsadc_chip rv1126_tsadc_data = {
+	.chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */
+	.chn_num = 1, /* one channel for tsadc */
+
+	.tshut_mode = TSHUT_MODE_CRU, /* default TSHUT via CRU */
+	.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
+	.tshut_temp = 95000,
+
+	.initialize = rk_tsadcv6_initialize,
+	.irq_ack = rk_tsadcv3_irq_ack,
+	.control = rk_tsadcv2_control,
+	.get_temp = rk_tsadcv2_get_temp,
+	.set_alarm_temp = rk_tsadcv2_alarm_temp,
+	.set_tshut_temp = rk_tsadcv2_tshut_temp,
+	.set_tshut_mode = rk_tsadcv3_tshut_mode,
+	.get_trim_code = rk_tsadcv1_get_trim_code,
+	.trim_temp = rk_tsadcv1_trim_temp,
+
+	.table = {
+		.kNum = 2263,
+		.bNum = 2704,
+		.data_mask = TSADCV2_DATA_MASK,
+		.mode = ADC_INCREMENT,
+	},
+};
+
+static const struct rockchip_tsadc_chip rk1808_tsadc_data = {
+	.chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */
+	.chn_num = 1, /* one channel for tsadc */
+
+	.tshut_mode = TSHUT_MODE_OTP, /* default TSHUT via GPIO give PMIC */
+	.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
+	.tshut_temp = 95000,
+
+	.initialize = rk_tsadcv5_initialize,
+	.irq_ack = rk_tsadcv3_irq_ack,
+	.control = rk_tsadcv3_control,
+	.get_temp = rk_tsadcv2_get_temp,
+	.set_alarm_temp = rk_tsadcv2_alarm_temp,
+	.set_tshut_temp = rk_tsadcv2_tshut_temp,
+	.set_tshut_mode = rk_tsadcv2_tshut_mode,
+
+	.table = {
+		.id = rk1808_code_table,
+		.length = ARRAY_SIZE(rk1808_code_table),
+		.data_mask = TSADCV2_DATA_MASK,
+		.mode = ADC_INCREMENT,
+	},
+};
+
 static const struct rockchip_tsadc_chip rk3228_tsadc_data = {
 	.chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */
 	.chn_num = 1, /* one channel for tsadc */
 
-	.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
+	.tshut_mode = TSHUT_MODE_OTP, /* default TSHUT via GPIO give PMIC */
 	.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
 	.tshut_temp = 95000,
 
@@ -909,7 +1349,7 @@ static const struct rockchip_tsadc_chip rk3288_tsadc_data = {
 	.chn_id[SENSOR_GPU] = 2, /* gpu sensor is channel 2 */
 	.chn_num = 2, /* two channels for tsadc */
 
-	.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
+	.tshut_mode = TSHUT_MODE_OTP, /* default TSHUT via GPIO give PMIC */
 	.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
 	.tshut_temp = 95000,
 
@@ -957,7 +1397,7 @@ static const struct rockchip_tsadc_chip rk3366_tsadc_data = {
 	.chn_id[SENSOR_GPU] = 1, /* gpu sensor is channel 1 */
 	.chn_num = 2, /* two channels for tsadc */
 
-	.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
+	.tshut_mode = TSHUT_MODE_OTP, /* default TSHUT via GPIO give PMIC */
 	.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
 	.tshut_temp = 95000,
 
@@ -982,7 +1422,7 @@ static const struct rockchip_tsadc_chip rk3368_tsadc_data = {
 	.chn_id[SENSOR_GPU] = 1, /* gpu sensor is channel 1 */
 	.chn_num = 2, /* two channels for tsadc */
 
-	.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
+	.tshut_mode = TSHUT_MODE_OTP, /* default TSHUT via GPIO give PMIC */
 	.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
 	.tshut_temp = 95000,
 
@@ -1007,7 +1447,7 @@ static const struct rockchip_tsadc_chip rk3399_tsadc_data = {
 	.chn_id[SENSOR_GPU] = 1, /* gpu sensor is channel 1 */
 	.chn_num = 2, /* two channels for tsadc */
 
-	.tshut_mode = TSHUT_MODE_GPIO, /* default TSHUT via GPIO give PMIC */
+	.tshut_mode = TSHUT_MODE_OTP, /* default TSHUT via GPIO give PMIC */
 	.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
 	.tshut_temp = 95000,
 
@@ -1027,6 +1467,53 @@ static const struct rockchip_tsadc_chip rk3399_tsadc_data = {
 	},
 };
 
+static const struct rockchip_tsadc_chip rk3568_tsadc_data = {
+	.chn_id[SENSOR_CPU] = 0, /* cpu sensor is channel 0 */
+	.chn_id[SENSOR_GPU] = 1, /* gpu sensor is channel 1 */
+	.chn_num = 2, /* two channels for tsadc */
+
+	.tshut_mode = TSHUT_MODE_OTP, /* default TSHUT via GPIO give PMIC */
+	.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
+	.tshut_temp = 95000,
+
+	.initialize = rk_tsadcv7_initialize,
+	.irq_ack = rk_tsadcv3_irq_ack,
+	.control = rk_tsadcv3_control,
+	.get_temp = rk_tsadcv2_get_temp,
+	.set_alarm_temp = rk_tsadcv2_alarm_temp,
+	.set_tshut_temp = rk_tsadcv2_tshut_temp,
+	.set_tshut_mode = rk_tsadcv2_tshut_mode,
+
+	.table = {
+		.id = rk3568_code_table,
+		.length = ARRAY_SIZE(rk3568_code_table),
+		.data_mask = TSADCV2_DATA_MASK,
+		.mode = ADC_INCREMENT,
+	},
+};
+
+static const struct rockchip_tsadc_chip rk3588_tsadc_data = {
+	/* top, big_core0, big_core1, little_core, center, gpu, npu */
+	.chn_id = {0, 1, 2, 3, 4, 5, 6},
+	.chn_num = 7, /* seven channels for tsadc */
+	.tshut_mode = TSHUT_MODE_OTP, /* default TSHUT via GPIO give PMIC */
+	.tshut_polarity = TSHUT_LOW_ACTIVE, /* default TSHUT LOW ACTIVE */
+	.tshut_temp = 95000,
+	.initialize = rk_tsadcv8_initialize,
+	.irq_ack = rk_tsadcv4_irq_ack,
+	.control = rk_tsadcv4_control,
+	.get_temp = rk_tsadcv4_get_temp,
+	.set_alarm_temp = rk_tsadcv3_alarm_temp,
+	.set_tshut_temp = rk_tsadcv3_tshut_temp,
+	.set_tshut_mode = rk_tsadcv4_tshut_mode,
+	.table = {
+		.id = rk3588_code_table,
+		.length = ARRAY_SIZE(rk3588_code_table),
+		.data_mask = TSADCV4_DATA_MASK,
+		.mode = ADC_INCREMENT,
+	},
+};
+
 static const struct of_device_id of_rockchip_thermal_match[] = {
 	{	.compatible = "rockchip,px30-tsadc",
 		.data = (void *)&px30_tsadc_data,
@@ -1035,6 +1522,14 @@ static const struct of_device_id of_rockchip_thermal_match[] = {
 		.compatible = "rockchip,rv1108-tsadc",
 		.data = (void *)&rv1108_tsadc_data,
 	},
+	{
+		.compatible = "rockchip,rv1126-tsadc",
+		.data = (void *)&rv1126_tsadc_data,
+	},
+	{
+		.compatible = "rockchip,rk1808-tsadc",
+		.data = (void *)&rk1808_tsadc_data,
+	},
 	{
 		.compatible = "rockchip,rk3228-tsadc",
 		.data = (void *)&rk3228_tsadc_data,
@@ -1059,6 +1554,14 @@ static const struct of_device_id of_rockchip_thermal_match[] = {
 		.compatible = "rockchip,rk3399-tsadc",
 		.data = (void *)&rk3399_tsadc_data,
 	},
+	{
+		.compatible = "rockchip,rk3568-tsadc",
+		.data = (void *)&rk3568_tsadc_data,
+	},
+	{
+		.compatible = "rockchip,rk3588-tsadc",
+		.data = (void *)&rk3588_tsadc_data,
+	},
 	{ /* end */ },
 };
 MODULE_DEVICE_TABLE(of, of_rockchip_thermal_match);
@@ -1099,6 +1602,9 @@ static int rockchip_thermal_set_trips(void *_sensor, int low, int high)
 	dev_dbg(&thermal->pdev->dev, "%s: sensor %d: low: %d, high %d\n",
 		__func__, sensor->id, low, high);
 
+	if (tsadc->trim_temp)
+		high += tsadc->trim_temp(thermal->pdev);
+
 	return tsadc->set_alarm_temp(&tsadc->table,
 				     sensor->id, thermal->regs, high);
 }
@@ -1112,6 +1618,8 @@ static int rockchip_thermal_get_temp(void *_sensor, int *out_temp)
 
 	retval = tsadc->get_temp(&tsadc->table,
 				 sensor->id, thermal->regs, out_temp);
+	if (tsadc->trim_temp)
+		*out_temp -= tsadc->trim_temp(thermal->pdev);
 	dev_dbg(&thermal->pdev->dev, "sensor %d - temp: %d, retval: %d\n",
 		sensor->id, *out_temp, retval);
 
@@ -1123,11 +1631,52 @@ static const struct thermal_zone_of_device_ops rockchip_of_thermal_ops = {
 	.set_trips = rockchip_thermal_set_trips,
 };
 
+static void thermal_pinctrl_select_otp(struct rockchip_thermal_data *thermal)
+{
+	if (!IS_ERR(thermal->pinctrl) && !IS_ERR_OR_NULL(thermal->otp_state))
+		pinctrl_select_state(thermal->pinctrl,
+				     thermal->otp_state);
+}
+
+static void thermal_pinctrl_select_gpio(struct rockchip_thermal_data *thermal)
+{
+	if (!IS_ERR(thermal->pinctrl) && !IS_ERR_OR_NULL(thermal->gpio_state))
+		pinctrl_select_state(thermal->pinctrl,
+				     thermal->gpio_state);
+}
+
+static int rockchip_get_efuse_value(struct device_node *np, char *porp_name,
+				    int *value)
+{
+	struct nvmem_cell *cell;
+	unsigned char *buf;
+	size_t len;
+
+	cell = of_nvmem_cell_get(np, porp_name);
+	if (IS_ERR(cell))
+		return PTR_ERR(cell);
+
+	buf = (unsigned char *)nvmem_cell_read(cell, &len);
+
+	nvmem_cell_put(cell);
+
+	if (IS_ERR(buf))
+		return PTR_ERR(buf);
+
+	*value = buf[0];
+
+	kfree(buf);
+
+	return 0;
+}
+
 static int rockchip_configure_from_dt(struct device *dev,
 				      struct device_node *np,
 				      struct rockchip_thermal_data *thermal)
 {
+	const struct rockchip_tsadc_chip *tsadc = thermal->chip;
 	u32 shut_temp, tshut_mode, tshut_polarity;
+	int trim_l = 0, trim_h = 0, trim_bsae = 0;
 
 	if (of_property_read_u32(np, "rockchip,hw-tshut-temp", &shut_temp)) {
 		dev_warn(dev,
@@ -1146,7 +1695,7 @@ static int rockchip_configure_from_dt(struct device *dev,
 	if (of_property_read_u32(np, "rockchip,hw-tshut-mode", &tshut_mode)) {
 		dev_warn(dev,
 			 "Missing tshut mode property, using default (%s)\n",
-			 thermal->chip->tshut_mode == TSHUT_MODE_GPIO ?
+			 thermal->chip->tshut_mode == TSHUT_MODE_OTP ?
 				"gpio" : "cru");
 		thermal->tshut_mode = thermal->chip->tshut_mode;
 	} else {
@@ -1183,6 +1732,29 @@ static int rockchip_configure_from_dt(struct device *dev,
 	if (IS_ERR(thermal->grf))
 		dev_warn(dev, "Missing rockchip,grf property\n");
 
+	if (tsadc->trim_temp && tsadc->get_trim_code) {
+		/* The tsadc won't to handle the error in here
+		 * since some SoCs didn't need this property.
+		 * rv1126 need trim tsadc.
+		 */
+		if (rockchip_get_efuse_value(np, "trim_l", &trim_l))
+			dev_warn(dev, "Missing trim_l property\n");
+		if (rockchip_get_efuse_value(np, "trim_h", &trim_h))
+			dev_warn(dev, "Missing trim_h property\n");
+		if (rockchip_get_efuse_value(np, "trim_base", &trim_bsae))
+			dev_warn(dev, "Missing trim_base property\n");
+
+		if (trim_l && trim_h && trim_bsae) {
+			thermal->trim = tsadc->get_trim_code(thermal->pdev,
+							     (trim_h << 8) |
+							     trim_l,
+							     trim_bsae);
+			dev_info(dev, "tsadc trimmed value = %d\n",
+				 thermal->trim);
+			thermal->tshut_temp += tsadc->trim_temp(thermal->pdev);
+		}
+	}
+
 	return 0;
 }
 
@@ -1195,7 +1767,8 @@ rockchip_thermal_register_sensor(struct platform_device *pdev,
 	const struct rockchip_tsadc_chip *tsadc = thermal->chip;
 	int error;
 
-	tsadc->set_tshut_mode(id, thermal->regs, thermal->tshut_mode);
+	tsadc->set_tshut_mode(thermal->grf, id, thermal->regs,
+			      thermal->tshut_mode);
 
 	error = tsadc->set_tshut_temp(&tsadc->table, id, thermal->regs,
 			      thermal->tshut_temp);
@@ -1228,6 +1801,43 @@ static void rockchip_thermal_reset_controller(struct reset_control *reset)
 	reset_control_deassert(reset);
 }
 
+static void rockchip_dump_temperature(struct rockchip_thermal_data *thermal)
+{
+	struct platform_device *pdev;
+	int i;
+
+	if (!thermal)
+		return;
+
+	pdev = thermal->pdev;
+
+	for (i = 0; i < thermal->chip->chn_num; i++) {
+		struct rockchip_thermal_sensor *sensor = &thermal->sensors[i];
+		struct thermal_zone_device *tz = sensor->tzd;
+
+		if (tz->temperature != THERMAL_TEMP_INVALID)
+			dev_warn(&pdev->dev, "channal %d: temperature(%d C)\n",
+				 i, tz->temperature / 1000);
+	}
+
+	if (thermal->regs) {
+		pr_warn("THERMAL REGS:\n");
+		print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET,
+			       32, 4, thermal->regs, 0x88, false);
+	}
+}
+
+static int rockchip_thermal_panic(struct notifier_block *this,
+				  unsigned long ev, void *ptr)
+{
+	struct rockchip_thermal_data *thermal;
+
+	thermal = container_of(this, struct rockchip_thermal_data, panic_nb);
+	rockchip_dump_temperature(thermal);
+
+	return NOTIFY_DONE;
+}
+
 static int rockchip_thermal_probe(struct platform_device *pdev)
 {
 	struct device_node *np = pdev->dev.of_node;
@@ -1262,40 +1872,26 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
 	if (IS_ERR(thermal->regs))
 		return PTR_ERR(thermal->regs);
 
-	thermal->reset = devm_reset_control_get(&pdev->dev, "tsadc-apb");
+	thermal->reset = devm_reset_control_array_get(&pdev->dev, false, false);
 	if (IS_ERR(thermal->reset)) {
-		error = PTR_ERR(thermal->reset);
-		dev_err(&pdev->dev, "failed to get tsadc reset: %d\n", error);
-		return error;
-	}
-
-	thermal->clk = devm_clk_get(&pdev->dev, "tsadc");
-	if (IS_ERR(thermal->clk)) {
-		error = PTR_ERR(thermal->clk);
-		dev_err(&pdev->dev, "failed to get tsadc clock: %d\n", error);
-		return error;
+		if (PTR_ERR(thermal->reset) != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "failed to get tsadc reset lines\n");
+		return PTR_ERR(thermal->reset);
 	}
 
-	thermal->pclk = devm_clk_get(&pdev->dev, "apb_pclk");
-	if (IS_ERR(thermal->pclk)) {
-		error = PTR_ERR(thermal->pclk);
-		dev_err(&pdev->dev, "failed to get apb_pclk clock: %d\n",
-			error);
-		return error;
-	}
+	thermal->num_clks = devm_clk_bulk_get_all(&pdev->dev, &thermal->clks);
+	if (thermal->num_clks < 1)
+		return -ENODEV;
 
-	error = clk_prepare_enable(thermal->clk);
+	error = clk_bulk_prepare_enable(thermal->num_clks, thermal->clks);
 	if (error) {
-		dev_err(&pdev->dev, "failed to enable converter clock: %d\n",
+		dev_err(&pdev->dev, "failed to prepare enable tsadc bulk clks: %d\n",
 			error);
 		return error;
 	}
+	platform_set_drvdata(pdev, thermal);
 
-	error = clk_prepare_enable(thermal->pclk);
-	if (error) {
-		dev_err(&pdev->dev, "failed to enable pclk: %d\n", error);
-		goto err_disable_clk;
-	}
+	thermal->chip->control(thermal->regs, false);
 
 	rockchip_thermal_reset_controller(thermal->reset);
 
@@ -1303,12 +1899,30 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
 	if (error) {
 		dev_err(&pdev->dev, "failed to parse device tree data: %d\n",
 			error);
-		goto err_disable_pclk;
+		goto err_disable_clocks;
 	}
 
 	thermal->chip->initialize(thermal->grf, thermal->regs,
 				  thermal->tshut_polarity);
 
+	if (thermal->tshut_mode == TSHUT_MODE_OTP) {
+		thermal->pinctrl = devm_pinctrl_get(&pdev->dev);
+		if (IS_ERR(thermal->pinctrl))
+			dev_err(&pdev->dev, "failed to find thermal pinctrl\n");
+
+		thermal->gpio_state = pinctrl_lookup_state(thermal->pinctrl,
+							   "gpio");
+		if (IS_ERR_OR_NULL(thermal->gpio_state))
+			dev_err(&pdev->dev, "failed to find thermal gpio state\n");
+
+		thermal->otp_state = pinctrl_lookup_state(thermal->pinctrl,
+							  "otpout");
+		if (IS_ERR_OR_NULL(thermal->otp_state))
+			dev_err(&pdev->dev, "failed to find thermal otpout state\n");
+
+		thermal_pinctrl_select_otp(thermal);
+	}
+
 	for (i = 0; i < thermal->chip->chn_num; i++) {
 		error = rockchip_thermal_register_sensor(pdev, thermal,
 						&thermal->sensors[i],
@@ -1317,7 +1931,7 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
 			dev_err(&pdev->dev,
 				"failed to register sensor[%d] : error = %d\n",
 				i, error);
-			goto err_disable_pclk;
+			goto err_disable_clocks;
 		}
 	}
 
@@ -1328,7 +1942,7 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
 	if (error) {
 		dev_err(&pdev->dev,
 			"failed to request tsadc irq: %d\n", error);
-		goto err_disable_pclk;
+		goto err_disable_clocks;
 	}
 
 	thermal->chip->control(thermal->regs, true);
@@ -1343,14 +1957,16 @@ static int rockchip_thermal_probe(struct platform_device *pdev)
 				 i, error);
 	}
 
-	platform_set_drvdata(pdev, thermal);
+	thermal->panic_nb.notifier_call = rockchip_thermal_panic;
+	atomic_notifier_chain_register(&panic_notifier_list,
+				       &thermal->panic_nb);
+
+	dev_info(&pdev->dev, "tsadc is probed successfully!\n");
 
 	return 0;
 
-err_disable_pclk:
-	clk_disable_unprepare(thermal->pclk);
-err_disable_clk:
-	clk_disable_unprepare(thermal->clk);
+err_disable_clocks:
+	clk_bulk_disable_unprepare(thermal->num_clks, thermal->clks);
 
 	return error;
 }
@@ -1369,12 +1985,28 @@ static int rockchip_thermal_remove(struct platform_device *pdev)
 
 	thermal->chip->control(thermal->regs, false);
 
-	clk_disable_unprepare(thermal->pclk);
-	clk_disable_unprepare(thermal->clk);
+	clk_bulk_disable_unprepare(thermal->num_clks, thermal->clks);
 
 	return 0;
 }
 
+static void rockchip_thermal_shutdown(struct platform_device *pdev)
+{
+	struct rockchip_thermal_data *thermal = platform_get_drvdata(pdev);
+	int i;
+
+	for (i = 0; i < thermal->chip->chn_num; i++) {
+		int id = thermal->sensors[i].id;
+
+		if (thermal->tshut_mode != TSHUT_MODE_CRU)
+			thermal->chip->set_tshut_mode(thermal->grf, id,
+						      thermal->regs,
+						      TSHUT_MODE_CRU);
+	}
+	if (thermal->tshut_mode == TSHUT_MODE_OTP)
+		thermal_pinctrl_select_gpio(thermal);
+}
+
 static int __maybe_unused rockchip_thermal_suspend(struct device *dev)
 {
 	struct rockchip_thermal_data *thermal = dev_get_drvdata(dev);
@@ -1385,10 +2017,10 @@ static int __maybe_unused rockchip_thermal_suspend(struct device *dev)
 
 	thermal->chip->control(thermal->regs, false);
 
-	clk_disable(thermal->pclk);
-	clk_disable(thermal->clk);
+	clk_bulk_disable(thermal->num_clks, thermal->clks);
 
-	pinctrl_pm_select_sleep_state(dev);
+	if (thermal->tshut_mode == TSHUT_MODE_OTP)
+		thermal_pinctrl_select_gpio(thermal);
 
 	return 0;
 }
@@ -1399,13 +2031,10 @@ static int __maybe_unused rockchip_thermal_resume(struct device *dev)
 	int i;
 	int error;
 
-	error = clk_enable(thermal->clk);
-	if (error)
-		return error;
-
-	error = clk_enable(thermal->pclk);
+	error = clk_bulk_enable(thermal->num_clks, thermal->clks);
 	if (error) {
-		clk_disable(thermal->clk);
+		dev_err(dev, "failed to enable tsadc bulk clks: %d\n",
+			error);
 		return error;
 	}
 
@@ -1417,7 +2046,7 @@ static int __maybe_unused rockchip_thermal_resume(struct device *dev)
 	for (i = 0; i < thermal->chip->chn_num; i++) {
 		int id = thermal->sensors[i].id;
 
-		thermal->chip->set_tshut_mode(id, thermal->regs,
+		thermal->chip->set_tshut_mode(thermal->grf, id, thermal->regs,
 					      thermal->tshut_mode);
 
 		error = thermal->chip->set_tshut_temp(&thermal->chip->table,
@@ -1433,7 +2062,8 @@ static int __maybe_unused rockchip_thermal_resume(struct device *dev)
 	for (i = 0; i < thermal->chip->chn_num; i++)
 		rockchip_thermal_toggle_sensor(&thermal->sensors[i], true);
 
-	pinctrl_pm_select_default_state(dev);
+	if (thermal->tshut_mode == TSHUT_MODE_OTP)
+		thermal_pinctrl_select_otp(thermal);
 
 	return 0;
 }
@@ -1449,6 +2079,7 @@ static struct platform_driver rockchip_thermal_driver = {
 	},
 	.probe = rockchip_thermal_probe,
 	.remove = rockchip_thermal_remove,
+	.shutdown = rockchip_thermal_shutdown,
 };
 
 module_platform_driver(rockchip_thermal_driver);
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index b6dc9003b..b7b690972 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -48,6 +48,9 @@ struct uart_8250_dma {
 	unsigned char		tx_running;
 	unsigned char		tx_err;
 	unsigned char		rx_running;
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+	size_t			rx_index;
+#endif
 };
 
 struct old_serial_port {
@@ -158,6 +161,9 @@ static inline bool serial8250_set_THRI(struct uart_8250_port *up)
 	if (up->ier & UART_IER_THRI)
 		return false;
 	up->ier |= UART_IER_THRI;
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+	up->ier |= UART_IER_PTIME;
+#endif
 	serial_out(up, UART_IER, up->ier);
 	return true;
 }
@@ -167,6 +173,9 @@ static inline bool serial8250_clear_THRI(struct uart_8250_port *up)
 	if (!(up->ier & UART_IER_THRI))
 		return false;
 	up->ier &= ~UART_IER_THRI;
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+	up->ier &= ~UART_IER_PTIME;
+#endif
 	serial_out(up, UART_IER, up->ier);
 	return true;
 }
@@ -327,6 +336,9 @@ static inline int is_omap1510_8250(struct uart_8250_port *pt)
 #ifdef CONFIG_SERIAL_8250_DMA
 extern int serial8250_tx_dma(struct uart_8250_port *);
 extern int serial8250_rx_dma(struct uart_8250_port *);
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+extern int serial8250_start_rx_dma(struct uart_8250_port *);
+#endif
 extern void serial8250_rx_dma_flush(struct uart_8250_port *);
 extern int serial8250_request_dma(struct uart_8250_port *);
 extern void serial8250_release_dma(struct uart_8250_port *);
@@ -335,10 +347,12 @@ static inline int serial8250_tx_dma(struct uart_8250_port *p)
 {
 	return -1;
 }
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
 static inline int serial8250_rx_dma(struct uart_8250_port *p)
 {
 	return -1;
 }
+#endif
 static inline void serial8250_rx_dma_flush(struct uart_8250_port *p) { }
 static inline int serial8250_request_dma(struct uart_8250_port *p)
 {
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 0a7e9491b..760886a2c 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -565,6 +565,7 @@ static void __init serial8250_isa_init_ports(void)
 static void __init
 serial8250_register_ports(struct uart_driver *drv, struct device *dev)
 {
+#ifndef CONFIG_ARCH_ROCKCHIP
 	int i;
 
 	for (i = 0; i < nr_uarts; i++) {
@@ -584,6 +585,7 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev)
 		serial8250_apply_quirks(up);
 		uart_add_one_port(drv, &up->port);
 	}
+#endif
 }
 
 #ifdef CONFIG_SERIAL_8250_CONSOLE
@@ -1031,6 +1033,9 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
 		uart->rs485_stop_tx	= up->rs485_stop_tx;
 		uart->dma		= up->dma;
 
+#ifdef CONFIG_ARCH_ROCKCHIP
+		uart->port.line		= up->port.line;
+#endif
 		/* Take tx_loadsz from fifosize if it wasn't set separately */
 		if (uart->port.fifosize && !uart->tx_loadsz)
 			uart->tx_loadsz = uart->port.fifosize;
@@ -1254,7 +1259,11 @@ static void __exit serial8250_exit(void)
 #endif
 }
 
+#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT
+rootfs_initcall(serial8250_init);
+#else
 module_init(serial8250_init);
+#endif
 module_exit(serial8250_exit);
 
 MODULE_LICENSE("GPL");
diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index b3c3f7e58..8344c6a9e 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -11,6 +11,12 @@
 
 #include "8250.h"
 
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+#define MAX_TX_BYTES		64
+#define MAX_FIFO_SIZE		64
+#define UART_RFL_16550A		0x21
+#endif
+
 static void __dma_tx_complete(void *param)
 {
 	struct uart_8250_port	*p = param;
@@ -40,6 +46,39 @@ static void __dma_tx_complete(void *param)
 	spin_unlock_irqrestore(&p->port.lock, flags);
 }
 
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+
+static void __dma_rx_complete(void *param)
+{
+	struct uart_8250_port	*p = param;
+	struct uart_8250_dma	*dma = p->dma;
+	struct tty_port		*tty_port = &p->port.state->port;
+	struct dma_tx_state	state;
+	unsigned int		count = 0, cur_index = 0;
+
+	dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
+	cur_index = dma->rx_size - state.residue;
+
+	if (cur_index == dma->rx_index)
+		return;
+	else if (cur_index > dma->rx_index)
+		count = cur_index - dma->rx_index;
+	else
+		count = dma->rx_size - dma->rx_index;
+
+	tty_insert_flip_string(tty_port, dma->rx_buf + dma->rx_index, count);
+
+	if (cur_index < dma->rx_index) {
+		tty_insert_flip_string(tty_port, dma->rx_buf, cur_index);
+		count += cur_index;
+	}
+
+	p->port.icount.rx += count;
+	dma->rx_index = cur_index;
+}
+
+#else
+
 static void __dma_rx_complete(void *param)
 {
 	struct uart_8250_port	*p = param;
@@ -59,6 +98,8 @@ static void __dma_rx_complete(void *param)
 	tty_flip_buffer_push(tty_port);
 }
 
+#endif
+
 int serial8250_tx_dma(struct uart_8250_port *p)
 {
 	struct uart_8250_dma		*dma = p->dma;
@@ -84,6 +125,12 @@ int serial8250_tx_dma(struct uart_8250_port *p)
 		return 0;
 	}
 
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+	if (dma->tx_size < MAX_TX_BYTES) {
+		ret = -EBUSY;
+		goto err;
+	}
+#endif
 	dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
 
 	desc = dmaengine_prep_slave_single(dma->txchan,
@@ -115,6 +162,64 @@ int serial8250_tx_dma(struct uart_8250_port *p)
 	return ret;
 }
 
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+
+int serial8250_rx_dma(struct uart_8250_port *p)
+{
+	unsigned int rfl, i = 0, fcr = 0, cur_index = 0;
+	unsigned char buf[MAX_FIFO_SIZE];
+	struct uart_port	*port = &p->port;
+	struct tty_port		*tty_port = &p->port.state->port;
+	struct dma_tx_state	state;
+	struct uart_8250_dma	*dma = p->dma;
+
+	fcr = UART_FCR_ENABLE_FIFO | UART_FCR_T_TRIG_10 | UART_FCR_R_TRIG_11;
+	serial_port_out(port, UART_FCR, fcr);
+
+	do {
+		dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
+		cur_index = dma->rx_size - state.residue;
+	} while (cur_index % dma->rxconf.src_maxburst);
+
+	rfl = serial_port_in(port, UART_RFL_16550A);
+	while (i < rfl)
+		buf[i++] = serial_port_in(port, UART_RX);
+
+	__dma_rx_complete(p);
+
+	tty_insert_flip_string(tty_port, buf, i);
+	p->port.icount.rx += i;
+	tty_flip_buffer_push(tty_port);
+
+	if (fcr)
+		serial_port_out(port, UART_FCR, p->fcr);
+	return 0;
+}
+
+int serial8250_start_rx_dma(struct uart_8250_port *p)
+{
+	struct uart_8250_dma		*dma = p->dma;
+	struct dma_async_tx_descriptor	*desc;
+
+	desc = dmaengine_prep_dma_cyclic(dma->rxchan, dma->rx_addr,
+					 dma->rx_size, dma->rx_size,
+					 DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT |
+					 DMA_CTRL_ACK);
+	if (!desc)
+		return -EBUSY;
+
+	dma->rx_running = 1;
+	desc->callback = NULL;
+	desc->callback_param = NULL;
+
+	dma->rx_cookie = dmaengine_submit(desc);
+	dma_async_issue_pending(dma->rxchan);
+	dma->rx_index = 0;
+	return 0;
+}
+
+#else
+
 int serial8250_rx_dma(struct uart_8250_port *p)
 {
 	struct uart_8250_dma		*dma = p->dma;
@@ -140,6 +245,8 @@ int serial8250_rx_dma(struct uart_8250_port *p)
 	return 0;
 }
 
+#endif
+
 void serial8250_rx_dma_flush(struct uart_8250_port *p)
 {
 	struct uart_8250_dma *dma = p->dma;
@@ -167,11 +274,19 @@ int serial8250_request_dma(struct uart_8250_port *p)
 	dma->rxconf.direction		= DMA_DEV_TO_MEM;
 	dma->rxconf.src_addr_width	= DMA_SLAVE_BUSWIDTH_1_BYTE;
 	dma->rxconf.src_addr		= rx_dma_addr + UART_RX;
-
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+	if ((p->port.fifosize / 4) < 16)
+		dma->rxconf.src_maxburst = p->port.fifosize / 4;
+	else
+		dma->rxconf.src_maxburst = 16;
+#endif
 	dma->txconf.direction		= DMA_MEM_TO_DEV;
 	dma->txconf.dst_addr_width	= DMA_SLAVE_BUSWIDTH_1_BYTE;
 	dma->txconf.dst_addr		= tx_dma_addr + UART_TX;
 
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+	dma->txconf.dst_maxburst	= 16;
+#endif
 	dma_cap_zero(mask);
 	dma_cap_set(DMA_SLAVE, mask);
 
@@ -194,50 +309,51 @@ int serial8250_request_dma(struct uart_8250_port *p)
 
 	dmaengine_slave_config(dma->rxchan, &dma->rxconf);
 
-	/* Get a channel for TX */
-	dma->txchan = dma_request_slave_channel_compat(mask,
-						       dma->fn, dma->tx_param,
-						       p->port.dev, "tx");
-	if (!dma->txchan) {
-		ret = -ENODEV;
-		goto release_rx;
-	}
-
-	/* 8250 tx dma requires dmaengine driver to support terminate */
-	ret = dma_get_slave_caps(dma->txchan, &caps);
-	if (ret)
-		goto err;
-	if (!caps.cmd_terminate) {
-		ret = -EINVAL;
-		goto err;
-	}
-
-	dmaengine_slave_config(dma->txchan, &dma->txconf);
-
 	/* RX buffer */
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+	if (!dma->rx_size)
+		dma->rx_size = PAGE_SIZE * 2;
+#else
 	if (!dma->rx_size)
 		dma->rx_size = PAGE_SIZE;
+#endif
 
 	dma->rx_buf = dma_alloc_coherent(dma->rxchan->device->dev, dma->rx_size,
 					&dma->rx_addr, GFP_KERNEL);
 	if (!dma->rx_buf) {
 		ret = -ENOMEM;
-		goto err;
+		goto release_rx;
 	}
 
-	/* TX buffer */
-	dma->tx_addr = dma_map_single(dma->txchan->device->dev,
-					p->port.state->xmit.buf,
-					UART_XMIT_SIZE,
-					DMA_TO_DEVICE);
-	if (dma_mapping_error(dma->txchan->device->dev, dma->tx_addr)) {
-		dma_free_coherent(dma->rxchan->device->dev, dma->rx_size,
-				  dma->rx_buf, dma->rx_addr);
-		ret = -ENOMEM;
-		goto err;
-	}
+	/* Get a channel for TX */
+	dma->txchan = dma_request_slave_channel_compat(mask,
+						       dma->fn, dma->tx_param,
+						       p->port.dev, "tx");
+	if (dma->txchan) {
+		dmaengine_slave_config(dma->txchan, &dma->txconf);
+
+		/* TX buffer */
+		dma->tx_addr = dma_map_single(dma->txchan->device->dev,
+						p->port.state->xmit.buf,
+						UART_XMIT_SIZE,
+						DMA_TO_DEVICE);
+		if (dma_mapping_error(dma->txchan->device->dev, dma->tx_addr)) {
+			dma_free_coherent(dma->rxchan->device->dev,
+					  dma->rx_size, dma->rx_buf,
+					  dma->rx_addr);
+			dma_release_channel(dma->txchan);
+			dma->txchan = NULL;
+		}
 
-	dev_dbg_ratelimited(p->port.dev, "got both dma channels\n");
+		dev_info_ratelimited(p->port.dev, "got rx and tx dma channels\n");
+	} else {
+		dev_info_ratelimited(p->port.dev, "got rx dma channels only\n");
+ 	}
+
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+	/* start dma for rx*/
+	serial8250_start_rx_dma(p);
+#endif
 
 	return 0;
 err:
@@ -262,13 +378,17 @@ void serial8250_release_dma(struct uart_8250_port *p)
 	dma_release_channel(dma->rxchan);
 	dma->rxchan = NULL;
 
-	/* Release TX resources */
-	dmaengine_terminate_sync(dma->txchan);
-	dma_unmap_single(dma->txchan->device->dev, dma->tx_addr,
-			 UART_XMIT_SIZE, DMA_TO_DEVICE);
-	dma_release_channel(dma->txchan);
-	dma->txchan = NULL;
-	dma->tx_running = 0;
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+	dma->rx_running = 0;
+#endif
+	if (dma->txchan) {
+		dmaengine_terminate_sync(dma->txchan);
+		dma_unmap_single(dma->txchan->device->dev, dma->tx_addr,
+				 UART_XMIT_SIZE, DMA_TO_DEVICE);
+		dma_release_channel(dma->txchan);
+		dma->txchan = NULL;
+		dma->tx_running = 0;
+	}
 
 	dev_dbg_ratelimited(p->port.dev, "dma channels released\n");
 }
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index ace221afe..5968b7714 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -29,10 +29,15 @@
 
 #include <asm/byteorder.h>
 
+#ifdef MODULE
+#include "8250_dwlib.c"
+#else
 #include "8250_dwlib.h"
+#endif
 
 /* Offsets for the DesignWare specific registers */
 #define DW_UART_USR	0x1f /* UART Status Register */
+#define DW_UART_RFL	0x21 /* UART Receive Fifo Level Register */
 
 /* DesignWare specific register fields */
 #define DW_UART_MCR_SIRE		BIT(6)
@@ -49,6 +54,11 @@ struct dw8250_data {
 	struct work_struct	clk_work;
 	struct reset_control	*rst;
 
+#ifdef CONFIG_ARCH_ROCKCHIP
+	int			irq;
+	int			irq_wake;
+	int			enable_wakeup;
+#endif
 	unsigned int		skip_autocfg:1;
 	unsigned int		uart_16550_compatible:1;
 };
@@ -238,10 +248,9 @@ static unsigned int dw8250_serial_in32be(struct uart_port *p, int offset)
 
 static int dw8250_handle_irq(struct uart_port *p)
 {
-	struct uart_8250_port *up = up_to_u8250p(p);
 	struct dw8250_data *d = to_dw8250_data(p->private_data);
 	unsigned int iir = p->serial_in(p, UART_IIR);
-	unsigned int status;
+	unsigned int status, usr, rfl;
 	unsigned long flags;
 
 	/*
@@ -250,15 +259,14 @@ static int dw8250_handle_irq(struct uart_port *p)
 	 * data available.  If we see such a case then we'll do a bogus
 	 * read.  If we don't do this then the "RX TIMEOUT" interrupt will
 	 * fire forever.
-	 *
-	 * This problem has only been observed so far when not in DMA mode
-	 * so we limit the workaround only to non-DMA mode.
 	 */
-	if (!up->dma && ((iir & 0x3f) == UART_IIR_RX_TIMEOUT)) {
+	if ((iir & 0x3f) == UART_IIR_RX_TIMEOUT) {
 		spin_lock_irqsave(&p->lock, flags);
+		usr = p->serial_in(p, d->usr_reg);
 		status = p->serial_in(p, UART_LSR);
 
-		if (!(status & (UART_LSR_DR | UART_LSR_BI)))
+		rfl = p->serial_in(p, DW_UART_RFL);
+		if (!(status & (UART_LSR_DR | UART_LSR_BI)) && !(usr & 0x1) && (rfl == 0))
 			(void) p->serial_in(p, UART_RX);
 
 		spin_unlock_irqrestore(&p->lock, flags);
@@ -332,12 +340,49 @@ dw8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old)
 static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios,
 			       struct ktermios *old)
 {
+#ifndef CONFIG_ARCH_ROCKCHIP
 	unsigned long newrate = tty_termios_baud_rate(termios) * 16;
+#endif
 	struct dw8250_data *d = to_dw8250_data(p->private_data);
 	long rate;
+#ifdef CONFIG_ARCH_ROCKCHIP
+	unsigned int baud = tty_termios_baud_rate(termios);
+	unsigned int rate_temp, diff;
+#endif
 	int ret;
 
 	clk_disable_unprepare(d->clk);
+#ifdef CONFIG_ARCH_ROCKCHIP
+	if (baud <= 115200)
+		rate = 24000000;
+	else if (baud == 230400)
+		rate = baud * 16 * 2;
+	else if (baud == 1152000)
+		rate = baud * 16 * 2;
+	else
+		rate = baud * 16;
+
+	ret = clk_set_rate(d->clk, rate);
+	rate_temp = clk_get_rate(d->clk);
+	diff = rate * 20 / 1000;
+	/*
+	 * If rate_temp is not equal to rate, is means fractional frequency
+	 * division is failed. Then use Integer frequency division, and
+	 * the baud rate error must be under -+2%
+	 */
+	if ((rate_temp < rate) && ((rate - rate_temp) > diff)) {
+		ret = clk_set_rate(d->clk, rate + diff);
+		rate_temp = clk_get_rate(d->clk);
+		if ((rate_temp < rate) && ((rate - rate_temp) > diff))
+			dev_info(p->dev, "set rate:%ld, but get rate:%d\n",
+				 rate, rate_temp);
+		else if ((rate < rate_temp) && ((rate_temp - rate) > diff))
+			dev_info(p->dev, "set rate:%ld, but get rate:%d\n",
+				 rate, rate_temp);
+	}
+	if (!ret)
+		p->uartclk = rate;
+#else
 	rate = clk_round_rate(d->clk, newrate);
 	if (rate > 0) {
 		/*
@@ -351,6 +396,7 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios,
 		if (ret)
 			swap(p->uartclk, rate);
 	}
+#endif
 	clk_prepare_enable(d->clk);
 
 	p->status &= ~UPSTAT_AUTOCTS;
@@ -483,6 +529,9 @@ static int dw8250_probe(struct platform_device *pdev)
 	data->data.dma.fn = dw8250_fallback_dma_filter;
 	data->usr_reg = DW_UART_USR;
 	p->private_data = &data->data;
+#ifdef CONFIG_ARCH_ROCKCHIP
+	data->irq	= irq;
+#endif
 
 	data->uart_16550_compatible = device_property_read_bool(dev,
 						"snps,uart-16550-compatible");
@@ -522,6 +571,13 @@ static int dw8250_probe(struct platform_device *pdev)
 		data->msr_mask_off |= UART_MSR_TERI;
 	}
 
+#ifdef CONFIG_ARCH_ROCKCHIP
+	if (device_property_read_bool(p->dev, "wakeup-source"))
+		data->enable_wakeup = 1;
+	else
+		data->enable_wakeup = 0;
+#endif
+
 	/* Always ask for fixed clock rate from a property. */
 	device_property_read_u32(dev, "clock-frequency", &p->uartclk);
 
@@ -603,6 +659,10 @@ static int dw8250_probe(struct platform_device *pdev)
 			queue_work(system_unbound_wq, &data->clk_work);
 	}
 
+#ifdef CONFIG_ARCH_ROCKCHIP
+	if (data->enable_wakeup)
+		device_init_wakeup(&pdev->dev, true);
+#endif
 	platform_set_drvdata(pdev, data);
 
 	pm_runtime_set_active(dev);
@@ -645,6 +705,10 @@ static int dw8250_remove(struct platform_device *pdev)
 
 	pm_runtime_disable(dev);
 	pm_runtime_put_noidle(dev);
+#ifdef CONFIG_ARCH_ROCKCHIP
+	if (data->enable_wakeup)
+		device_init_wakeup(&pdev->dev, false);
+#endif
 
 	return 0;
 }
@@ -655,6 +719,13 @@ static int dw8250_suspend(struct device *dev)
 	struct dw8250_data *data = dev_get_drvdata(dev);
 
 	serial8250_suspend_port(data->data.line);
+#ifdef CONFIG_ARCH_ROCKCHIP
+	if (device_may_wakeup(dev)) {
+		if (!enable_irq_wake(data->irq))
+			data->irq_wake = 1;
+		return 0;
+	}
+#endif
 
 	return 0;
 }
@@ -664,6 +735,15 @@ static int dw8250_resume(struct device *dev)
 	struct dw8250_data *data = dev_get_drvdata(dev);
 
 	serial8250_resume_port(data->data.line);
+#ifdef CONFIG_ARCH_ROCKCHIP
+	if (device_may_wakeup(dev)) {
+		if (data->irq_wake) {
+			disable_irq_wake(data->irq);
+			data->irq_wake = 0;
+		}
+		return 0;
+	}
+#endif
 
 	return 0;
 }
diff --git a/drivers/tty/serial/8250/8250_dwlib.c b/drivers/tty/serial/8250/8250_dwlib.c
index 6d6a78eea..fee4f2933 100644
--- a/drivers/tty/serial/8250/8250_dwlib.c
+++ b/drivers/tty/serial/8250/8250_dwlib.c
@@ -106,6 +106,15 @@ void dw8250_setup_port(struct uart_port *p)
 	}
 
 	reg = dw8250_readl_ext(p, DW_UART_CPR);
+
+#ifdef CONFIG_ARCH_ROCKCHIP
+	/*
+	 * The UART CPR may be 0 of some rockchip soc,
+	 * but it supports fifo and AFC, fifo entry is 32 default.
+	 */
+	if (reg == 0)
+		reg = 0x00023ff2;
+#endif
 	if (!reg)
 		return;
 
@@ -114,6 +123,9 @@ void dw8250_setup_port(struct uart_port *p)
 		p->type = PORT_16550A;
 		p->flags |= UPF_FIXED_TYPE;
 		p->fifosize = DW_UART_CPR_FIFO_SIZE(reg);
+#ifdef CONFIG_ARCH_ROCKCHIP
+		up->tx_loadsz = p->fifosize * 3 / 4;
+#endif
 		up->capabilities = UART_CAP_FIFO;
 	}
 
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 1f231fcda..1cb78f8ea 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -122,8 +122,7 @@ static const struct serial8250_config uart_config[] = {
 		.name		= "16C950/954",
 		.fifo_size	= 128,
 		.tx_loadsz	= 128,
-		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01,
-		.rxtrig_bytes	= {16, 32, 112, 120},
+		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
 		/* UART_CAP_EFR breaks billionon CF bluetooth card. */
 		.flags		= UART_CAP_FIFO | UART_CAP_SLEEP,
 	},
@@ -1537,8 +1536,13 @@ static inline void __start_tx(struct uart_port *port)
 {
 	struct uart_8250_port *up = up_to_u8250p(port);
 
+#ifdef CONFIG_ARCH_ROCKCHIP
+	if (up->dma && up->dma->txchan && !up->dma->tx_dma(up))
+		return;
+#else
 	if (up->dma && !up->dma->tx_dma(up))
 		return;
+#endif
 
 	if (serial8250_set_THRI(up)) {
 		if (up->bugs & UART_BUG_TXEN) {
@@ -1868,6 +1872,12 @@ EXPORT_SYMBOL_GPL(serial8250_modem_status);
 
 static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir)
 {
+#ifdef CONFIG_ARCH_ROCKCHIP
+	if ((iir & 0xf) != UART_IIR_RX_TIMEOUT)
+		return 0;
+	else
+		return up->dma->rx_dma(up);
+#else
 	switch (iir & 0x3f) {
 	case UART_IIR_RDI:
 		if (!up->dma->rx_running)
@@ -1879,6 +1889,7 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir)
 		return true;
 	}
 	return up->dma->rx_dma(up);
+#endif
 }
 
 /*
@@ -1889,7 +1900,9 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
 	unsigned char status;
 	unsigned long flags;
 	struct uart_8250_port *up = up_to_u8250p(port);
+#ifndef CONFIG_ARCH_ROCKCHIP
 	bool skip_rx = false;
+#endif
 
 	if (iir & UART_IIR_NO_INT)
 		return 0;
@@ -1898,6 +1911,17 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
 
 	status = serial_port_in(port, UART_LSR);
 
+#ifdef CONFIG_ARCH_ROCKCHIP
+	if (status & (UART_LSR_DR | UART_LSR_BI)) {
+		int dma_err = -1;
+
+		if (up->dma && up->dma->rxchan)
+			dma_err = handle_rx_dma(up, iir);
+
+		if (!up->dma || dma_err)
+			status = serial8250_rx_chars(up, status);
+	}
+#else
 	/*
 	 * If port is stopped and there are no error conditions in the
 	 * FIFO, then don't drain the FIFO, as this may lead to TTY buffer
@@ -1915,11 +1939,34 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
 		if (!up->dma || handle_rx_dma(up, iir))
 			status = serial8250_rx_chars(up, status);
 	}
+#endif
 	serial8250_modem_status(up);
-	if ((!up->dma || up->dma->tx_err) && (status & UART_LSR_THRE) &&
-		(up->ier & UART_IER_THRI))
+#ifdef CONFIG_ARCH_ROCKCHIP
+	if ((!up->dma || (up->dma && (!up->dma->txchan || up->dma->tx_err))) &&
+	    ((iir & 0xf) == UART_IIR_THRI))
 		serial8250_tx_chars(up);
-
+#else
+	if ((!up->dma || (up->dma && up->dma->tx_err)) &&
+	    (status & UART_LSR_THRE))
+		serial8250_tx_chars(up);
+#endif
+ 
+#ifdef CONFIG_ARCH_ROCKCHIP
+	if (status & UART_LSR_BRK_ERROR_BITS) {
+
+		if (status & UART_LSR_OE)
+			pr_err("%s: Overrun error!\n", port->name);
+		if (status & UART_LSR_PE)
+			pr_err("%s: Parity error!\n", port->name);
+		if (status & UART_LSR_FE)
+			pr_err("%s: Frame error!\n", port->name);
+		if (status & UART_LSR_BI)
+			pr_err("%s: Break interrupt!\n", port->name);
+
+		pr_err("%s: maybe rx pin is low or baudrate is not correct!\n",
+			port->name);
+	}
+#endif
 	uart_unlock_and_check_sysrq(port, flags);
 	return 1;
 }
@@ -2393,7 +2440,11 @@ int serial8250_do_startup(struct uart_port *port)
 		if (uart_console(port))
 			msg = "forbid DMA for kernel console";
 		else if (serial8250_request_dma(up))
+#ifdef CONFIG_ARCH_ROCKCHIP
+			msg = "failed to request DMA, use interrupt mode";
+#else
 			msg = "failed to request DMA";
+#endif
 		if (msg) {
 			dev_warn_ratelimited(port->dev, "%s\n", msg);
 			up->dma = NULL;
@@ -2587,6 +2638,10 @@ void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud,
 {
 	struct uart_8250_port *up = up_to_u8250p(port);
 
+#ifdef CONFIG_ARCH_ROCKCHIP
+	serial_port_out(port, UART_MCR, UART_MCR_LOOP);
+#endif
+
 	/* Workaround to enable 115200 baud on OMAP1510 internal ports */
 	if (is_omap1510_8250(up)) {
 		if (baud == 115200) {
@@ -2606,6 +2661,17 @@ void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud,
 		serial_port_out(port, UART_LCR, up->lcr | UART_LCR_DLAB);
 
 	serial_dl_write(up, quot);
+#ifdef CONFIG_ARCH_ROCKCHIP
+	if (quot != serial_dl_read(up))
+		dev_warn_ratelimited(port->dev, "ttyS%d set divisor fail, quot:%d != dll,dlh:%d\n",
+					serial_index(port), quot, serial_dl_read(up));
+#endif
+	if (port->type != PORT_16750)
+		serial_port_out(port, UART_LCR, up->lcr);	/* reset DLAB */
+
+#ifdef CONFIG_ARCH_ROCKCHIP
+	serial_port_out(port, UART_MCR, up->mcr);
+#endif
 }
 EXPORT_SYMBOL_GPL(serial8250_do_set_divisor);
 
@@ -2785,6 +2851,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
 	if ((termios->c_cflag & CREAD) == 0)
 		port->ignore_status_mask |= UART_LSR_DR;
 
+#ifndef CONFIG_ARCH_ROCKCHIP
 	/*
 	 * CTS flow control flag and modem status interrupts
 	 */
@@ -2798,6 +2865,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
 		up->ier |= UART_IER_RTOIE;
 
 	serial_port_out(port, UART_IER, up->ier);
+#endif
 
 	if (up->capabilities & UART_CAP_EFR) {
 		unsigned char efr = 0;
@@ -2816,16 +2884,25 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
 			serial_port_out(port, UART_EFR, efr);
 	}
 
+#ifdef CONFIG_ARCH_ROCKCHIP
+	/* Reset uart to make sure it is idle, then set baud rate */
+	serial_port_out(port, 0x88 >> 2, 0x7);
+#endif
+
 	serial8250_set_divisor(port, baud, quot, frac);
 
+#ifdef CONFIG_ARCH_ROCKCHIP
+	up->fcr = UART_FCR_ENABLE_FIFO | UART_FCR_T_TRIG_10 | UART_FCR_R_TRIG_10;
+#endif
 	/*
 	 * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
 	 * is written without DLAB set, this mode will be disabled.
 	 */
-	if (port->type == PORT_16750)
+	if (port->type == PORT_16750) {
 		serial_port_out(port, UART_FCR, up->fcr);
+		serial_port_out(port, UART_LCR, up->lcr);	/* reset DLAB */
+	}
 
-	serial_port_out(port, UART_LCR, up->lcr);	/* reset DLAB */
 	if (port->type != PORT_16750) {
 		/* emulated UARTs (Lucent Venus 167x) need two steps */
 		if (up->fcr & UART_FCR_ENABLE_FIFO)
@@ -2833,6 +2910,23 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
 		serial_port_out(port, UART_FCR, up->fcr);	/* set fcr */
 	}
 	serial8250_set_mctrl(port, port->mctrl);
+
+#ifndef CONFIG_ARCH_ROCKCHIP
+	/*
+	 * CTS flow control flag and modem status interrupts
+	 */
+	up->ier &= ~UART_IER_MSI;
+	if (!(up->bugs & UART_BUG_NOMSR) &&
+			UART_ENABLE_MS(&up->port, termios->c_cflag))
+		up->ier |= UART_IER_MSI;
+	if (up->capabilities & UART_CAP_UUE)
+		up->ier |= UART_IER_UUE;
+	if (up->capabilities & UART_CAP_RTOIE)
+		up->ier |= UART_IER_RTOIE;
+
+	serial_port_out(port, UART_IER, up->ier);
+#endif
+
 	spin_unlock_irqrestore(&port->lock, flags);
 	serial8250_rpm_put(up);
 
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 070b838c7..ab24ec3fb 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -340,9 +340,6 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf)
 			acm->iocount.overrun++;
 		spin_unlock_irqrestore(&acm->read_lock, flags);
 
-		if (newctrl & ACM_CTRL_BRK)
-			tty_flip_buffer_push(&acm->port);
-
 		if (difference)
 			wake_up_all(&acm->wioctl);
 
@@ -478,16 +475,11 @@ static int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags)
 
 static void acm_process_read_urb(struct acm *acm, struct urb *urb)
 {
-	unsigned long flags;
-
 	if (!urb->actual_length)
 		return;
 
-	spin_lock_irqsave(&acm->read_lock, flags);
 	tty_insert_flip_string(&acm->port, urb->transfer_buffer,
 			urb->actual_length);
-	spin_unlock_irqrestore(&acm->read_lock, flags);
-
 	tty_flip_buffer_push(&acm->port);
 }
 
@@ -734,8 +726,7 @@ static void acm_port_destruct(struct tty_port *port)
 {
 	struct acm *acm = container_of(port, struct acm, port);
 
-	if (acm->minor != ACM_MINOR_INVALID)
-		acm_release_minor(acm);
+	acm_release_minor(acm);
 	usb_put_intf(acm->control);
 	kfree(acm->country_codes);
 	kfree(acm);
@@ -1352,10 +1343,8 @@ static int acm_probe(struct usb_interface *intf,
 	usb_get_intf(acm->control); /* undone in destruct() */
 
 	minor = acm_alloc_minor(acm);
-	if (minor < 0) {
-		acm->minor = ACM_MINOR_INVALID;
+	if (minor < 0)
 		goto alloc_fail1;
-	}
 
 	acm->minor = minor;
 	acm->dev = usb_dev;
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 3aa7f0a3a..8aef5eb76 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -22,8 +22,6 @@
 #define ACM_TTY_MAJOR		166
 #define ACM_TTY_MINORS		256
 
-#define ACM_MINOR_INVALID	ACM_TTY_MINORS
-
 /*
  * Requests.
  */
diff --git a/drivers/usb/common/Kconfig b/drivers/usb/common/Kconfig
index b85662243..5e8a04e3d 100644
--- a/drivers/usb/common/Kconfig
+++ b/drivers/usb/common/Kconfig
@@ -6,7 +6,8 @@ config USB_COMMON
 
 config USB_LED_TRIG
 	bool "USB LED Triggers"
-	depends on LEDS_CLASS && USB_COMMON && LEDS_TRIGGERS
+	depends on LEDS_CLASS && LEDS_TRIGGERS
+	select USB_COMMON
 	help
 	  This option adds LED triggers for USB host and/or gadget activity.
 
diff --git a/drivers/usb/common/debug.c b/drivers/usb/common/debug.c
index f0c0e8db7..a76a086b9 100644
--- a/drivers/usb/common/debug.c
+++ b/drivers/usb/common/debug.c
@@ -207,28 +207,30 @@ static void usb_decode_set_isoch_delay(__u8 wValue, char *str, size_t size)
 	snprintf(str, size, "Set Isochronous Delay(Delay = %d ns)", wValue);
 }
 
-static void usb_decode_ctrl_generic(char *str, size_t size, __u8 bRequestType,
-				    __u8 bRequest, __u16 wValue, __u16 wIndex,
-				    __u16 wLength)
-{
-	u8 recip = bRequestType & USB_RECIP_MASK;
-	u8 type = bRequestType & USB_TYPE_MASK;
-
-	snprintf(str, size,
-		 "Type=%s Recipient=%s Dir=%s bRequest=%u wValue=%u wIndex=%u wLength=%u",
-		 (type == USB_TYPE_STANDARD)    ? "Standard" :
-		 (type == USB_TYPE_VENDOR)      ? "Vendor" :
-		 (type == USB_TYPE_CLASS)       ? "Class" : "Unknown",
-		 (recip == USB_RECIP_DEVICE)    ? "Device" :
-		 (recip == USB_RECIP_INTERFACE) ? "Interface" :
-		 (recip == USB_RECIP_ENDPOINT)  ? "Endpoint" : "Unknown",
-		 (bRequestType & USB_DIR_IN)    ? "IN" : "OUT",
-		 bRequest, wValue, wIndex, wLength);
-}
-
-static void usb_decode_ctrl_standard(char *str, size_t size, __u8 bRequestType,
-				     __u8 bRequest, __u16 wValue, __u16 wIndex,
-				     __u16 wLength)
+/**
+ * usb_decode_ctrl - Returns human readable representation of control request.
+ * @str: buffer to return a human-readable representation of control request.
+ *       This buffer should have about 200 bytes.
+ * @size: size of str buffer.
+ * @bRequestType: matches the USB bmRequestType field
+ * @bRequest: matches the USB bRequest field
+ * @wValue: matches the USB wValue field (CPU byte order)
+ * @wIndex: matches the USB wIndex field (CPU byte order)
+ * @wLength: matches the USB wLength field (CPU byte order)
+ *
+ * Function returns decoded, formatted and human-readable description of
+ * control request packet.
+ *
+ * The usage scenario for this is for tracepoints, so function as a return
+ * use the same value as in parameters. This approach allows to use this
+ * function in TP_printk
+ *
+ * Important: wValue, wIndex, wLength parameters before invoking this function
+ * should be processed by le16_to_cpu macro.
+ */
+const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType,
+			    __u8 bRequest, __u16 wValue, __u16 wIndex,
+			    __u16 wLength)
 {
 	switch (bRequest) {
 	case USB_REQ_GET_STATUS:
@@ -269,48 +271,14 @@ static void usb_decode_ctrl_standard(char *str, size_t size, __u8 bRequestType,
 		usb_decode_set_isoch_delay(wValue, str, size);
 		break;
 	default:
-		usb_decode_ctrl_generic(str, size, bRequestType, bRequest,
-					wValue, wIndex, wLength);
-		break;
-	}
-}
-
-/**
- * usb_decode_ctrl - Returns human readable representation of control request.
- * @str: buffer to return a human-readable representation of control request.
- *       This buffer should have about 200 bytes.
- * @size: size of str buffer.
- * @bRequestType: matches the USB bmRequestType field
- * @bRequest: matches the USB bRequest field
- * @wValue: matches the USB wValue field (CPU byte order)
- * @wIndex: matches the USB wIndex field (CPU byte order)
- * @wLength: matches the USB wLength field (CPU byte order)
- *
- * Function returns decoded, formatted and human-readable description of
- * control request packet.
- *
- * The usage scenario for this is for tracepoints, so function as a return
- * use the same value as in parameters. This approach allows to use this
- * function in TP_printk
- *
- * Important: wValue, wIndex, wLength parameters before invoking this function
- * should be processed by le16_to_cpu macro.
- */
-const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType,
-			    __u8 bRequest, __u16 wValue, __u16 wIndex,
-			    __u16 wLength)
-{
-	switch (bRequestType & USB_TYPE_MASK) {
-	case USB_TYPE_STANDARD:
-		usb_decode_ctrl_standard(str, size, bRequestType, bRequest,
-					 wValue, wIndex, wLength);
-		break;
-	case USB_TYPE_VENDOR:
-	case USB_TYPE_CLASS:
-	default:
-		usb_decode_ctrl_generic(str, size, bRequestType, bRequest,
-					wValue, wIndex, wLength);
-		break;
+		snprintf(str, size, "%02x %02x %02x %02x %02x %02x %02x %02x",
+			 bRequestType, bRequest,
+			 (u8)(cpu_to_le16(wValue) & 0xff),
+			 (u8)(cpu_to_le16(wValue) >> 8),
+			 (u8)(cpu_to_le16(wIndex) & 0xff),
+			 (u8)(cpu_to_le16(wIndex) >> 8),
+			 (u8)(cpu_to_le16(wLength) & 0xff),
+			 (u8)(cpu_to_le16(wLength) >> 8));
 	}
 
 	return str;
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 39f1eca60..562a730be 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -406,7 +406,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
 	 * the USB-2 spec requires such endpoints to have wMaxPacketSize = 0
 	 * (see the end of section 5.6.3), so don't warn about them.
 	 */
-	maxp = le16_to_cpu(endpoint->desc.wMaxPacketSize);
+	maxp = usb_endpoint_maxp(&endpoint->desc);
 	if (maxp == 0 && !(usb_endpoint_xfer_isoc(d) && asnum == 0)) {
 		dev_warn(ddev, "config %d interface %d altsetting %d endpoint 0x%X has invalid wMaxPacketSize 0\n",
 		    cfgno, inum, asnum, d->bEndpointAddress);
@@ -422,9 +422,9 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
 		maxpacket_maxes = full_speed_maxpacket_maxes;
 		break;
 	case USB_SPEED_HIGH:
-		/* Multiple-transactions bits are allowed only for HS periodic endpoints */
+		/* Bits 12..11 are allowed only for HS periodic endpoints */
 		if (usb_endpoint_xfer_int(d) || usb_endpoint_xfer_isoc(d)) {
-			i = maxp & USB_EP_MAXP_MULT_MASK;
+			i = maxp & (BIT(12) | BIT(11));
 			maxp &= ~i;
 		}
 		fallthrough;
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 4dfa44d6c..6253dce08 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -34,6 +34,7 @@
 
 #include "usb.h"
 
+#include <trace/hooks/usb.h>
 
 /*
  * Adds a new dynamic USBdevice ID to this driver,
@@ -1403,11 +1404,16 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
 	int			status = 0;
 	int			i = 0, n = 0;
 	struct usb_interface	*intf;
+	int			bypass = 0;
 
 	if (udev->state == USB_STATE_NOTATTACHED ||
 			udev->state == USB_STATE_SUSPENDED)
 		goto done;
 
+	trace_android_vh_usb_dev_suspend(udev, msg, &bypass);
+	if (bypass)
+		goto done;
+
 	/* Suspend all the interfaces and then udev itself */
 	if (udev->actconfig) {
 		n = udev->actconfig->desc.bNumInterfaces;
@@ -1504,11 +1510,17 @@ static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
 	int			status = 0;
 	int			i;
 	struct usb_interface	*intf;
+	int			bypass = 0;
 
 	if (udev->state == USB_STATE_NOTATTACHED) {
 		status = -ENODEV;
 		goto done;
 	}
+
+	trace_android_vh_usb_dev_resume(udev, msg, &bypass);
+	if (bypass)
+		goto done;
+
 	udev->can_submit = 1;
 
 	/* Resume the device */
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index ac347f9d5..2e85fcb37 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -754,7 +754,6 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
 {
 	struct urb	*urb;
 	int		length;
-	int		status;
 	unsigned long	flags;
 	char		buffer[6];	/* Any root hubs with > 31 ports? */
 
@@ -772,17 +771,11 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
 		if (urb) {
 			clear_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
 			hcd->status_urb = NULL;
-			if (urb->transfer_buffer_length >= length) {
-				status = 0;
-			} else {
-				status = -EOVERFLOW;
-				length = urb->transfer_buffer_length;
-			}
 			urb->actual_length = length;
 			memcpy(urb->transfer_buffer, buffer, length);
 
 			usb_hcd_unlink_urb_from_ep(hcd, urb);
-			usb_hcd_giveback_urb(hcd, urb, status);
+			usb_hcd_giveback_urb(hcd, urb, 0);
 		} else {
 			length = 0;
 			set_bit(HCD_FLAG_POLL_PENDING, &hcd->flags);
@@ -1562,13 +1555,6 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
 		urb->hcpriv = NULL;
 		INIT_LIST_HEAD(&urb->urb_list);
 		atomic_dec(&urb->use_count);
-		/*
-		 * Order the write of urb->use_count above before the read
-		 * of urb->reject below.  Pairs with the memory barriers in
-		 * usb_kill_urb() and usb_poison_urb().
-		 */
-		smp_mb__after_atomic();
-
 		atomic_dec(&urb->dev->urbnum);
 		if (atomic_read(&urb->reject))
 			wake_up(&usb_kill_urb_queue);
@@ -1673,13 +1659,6 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
 
 	usb_anchor_resume_wakeups(anchor);
 	atomic_dec(&urb->use_count);
-	/*
-	 * Order the write of urb->use_count above before the read
-	 * of urb->reject below.  Pairs with the memory barriers in
-	 * usb_kill_urb() and usb_poison_urb().
-	 */
-	smp_mb__after_atomic();
-
 	if (unlikely(atomic_read(&urb->reject)))
 		wake_up(&usb_kill_urb_queue);
 	usb_put_urb(urb);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index f2a3c0b5b..dfd24c966 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1109,10 +1109,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
 		} else {
 			hub_power_on(hub, true);
 		}
-	/* Give some time on remote wakeup to let links to transit to U0 */
-	} else if (hub_is_superspeed(hub->hdev))
-		msleep(20);
-
+	}
  init2:
 
 	/*
@@ -1227,7 +1224,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
 			 */
 			if (portchange || (hub_is_superspeed(hub->hdev) &&
 						port_resumed))
-				set_bit(port1, hub->event_bits);
+				set_bit(port1, hub->change_bits);
 
 		} else if (udev->persist_enabled) {
 #ifdef CONFIG_PM
@@ -1829,7 +1826,8 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
 	 * bus_resume methods.
 	 */
 	if (hdev->parent) {		/* normal device */
-		usb_enable_autosuspend(hdev);
+		if (!(hdev->parent->quirks & USB_QUIRK_AUTO_SUSPEND))
+			usb_enable_autosuspend(hdev);
 	} else {			/* root hub */
 		const struct hc_driver *drv = bus_to_hcd(hdev->bus)->driver;
 
@@ -4631,6 +4629,8 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
 	if (oldspeed == USB_SPEED_LOW)
 		delay = HUB_LONG_RESET_TIME;
 
+	mutex_lock(hcd->address0_mutex);
+
 	/* Reset the device; full speed may morph to high speed */
 	/* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
 	retval = hub_port_reset(hub, port1, udev, delay, false);
@@ -4941,6 +4941,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
 		hub_port_disable(hub, port1, 0);
 		update_devnum(udev, devnum);	/* for disconnect processing */
 	}
+	mutex_unlock(hcd->address0_mutex);
 	return retval;
 }
 
@@ -5115,7 +5116,6 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
 	struct usb_port *port_dev = hub->ports[port1 - 1];
 	struct usb_device *udev = port_dev->child;
 	static int unreliable_port = -1;
-	bool retry_locked;
 
 	/* Disconnect any existing devices under this port */
 	if (udev) {
@@ -5171,11 +5171,8 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
 		unit_load = 100;
 
 	status = 0;
-
 	for (i = 0; i < PORT_INIT_TRIES; i++) {
-		usb_lock_port(port_dev);
-		mutex_lock(hcd->address0_mutex);
-		retry_locked = true;
+
 		/* reallocate for each attempt, since references
 		 * to the previous one can escape in various ways
 		 */
@@ -5183,8 +5180,6 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
 		if (!udev) {
 			dev_err(&port_dev->dev,
 					"couldn't allocate usb_device\n");
-			mutex_unlock(hcd->address0_mutex);
-			usb_unlock_port(port_dev);
 			goto done;
 		}
 
@@ -5206,14 +5201,12 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
 		}
 
 		/* reset (non-USB 3.0 devices) and get descriptor */
+		usb_lock_port(port_dev);
 		status = hub_port_init(hub, udev, port1, i);
+		usb_unlock_port(port_dev);
 		if (status < 0)
 			goto loop;
 
-		mutex_unlock(hcd->address0_mutex);
-		usb_unlock_port(port_dev);
-		retry_locked = false;
-
 		if (udev->quirks & USB_QUIRK_DELAY_INIT)
 			msleep(2000);
 
@@ -5306,10 +5299,6 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
 		usb_ep0_reinit(udev);
 		release_devnum(udev);
 		hub_free_dev(udev);
-		if (retry_locked) {
-			mutex_unlock(hcd->address0_mutex);
-			usb_unlock_port(port_dev);
-		}
 		usb_put_dev(udev);
 		if ((status == -ENOTCONN) || (status == -ENOTSUPP))
 			break;
@@ -5334,7 +5323,8 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
 done:
 	hub_port_disable(hub, port1, 1);
 	if (hcd->driver->relinquish_port && !hub->hdev->parent) {
-		if (status != -ENOTCONN && status != -ENODEV)
+		if ((status != -ENOTCONN && status != -ENODEV) ||
+		    (status == -ENOTCONN && of_machine_is_compatible("rockchip,rk3288")))
 			hcd->driver->relinquish_port(hcd, port1);
 	}
 }
@@ -5851,8 +5841,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
 	bos = udev->bos;
 	udev->bos = NULL;
 
-	mutex_lock(hcd->address0_mutex);
-
 	for (i = 0; i < PORT_INIT_TRIES; ++i) {
 
 		/* ep0 maxpacket size may change; let the HCD know about it.
@@ -5862,7 +5850,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
 		if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV)
 			break;
 	}
-	mutex_unlock(hcd->address0_mutex);
 
 	if (ret < 0)
 		goto re_enumerate;
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 6d24d138c..b8164cf15 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -322,6 +322,10 @@ static const struct usb_device_id usb_quirk_list[] = {
 	/* Alcor Micro Corp. Hub */
 	{ USB_DEVICE(0x058f, 0x9254), .driver_info = USB_QUIRK_RESET_RESUME },
 
+	/* HD Camera Manufacturer */
+	{ USB_DEVICE(0x05a3, 0x9230), .driver_info = USB_QUIRK_AUTO_SUSPEND },
+	{ USB_DEVICE(0x05a3, 0x9320), .driver_info = USB_QUIRK_AUTO_SUSPEND },
+
 	/* appletouch */
 	{ USB_DEVICE(0x05ac, 0x021a), .driver_info = USB_QUIRK_RESET_RESUME },
 
@@ -426,6 +430,10 @@ static const struct usb_device_id usb_quirk_list[] = {
 	/* Generic RTL8153 based ethernet adapters */
 	{ USB_DEVICE(0x0bda, 0x8153), .driver_info = USB_QUIRK_NO_LPM },
 
+	/* Sonix FaceBlack device */
+	{ USB_DEVICE(0x0c45, 0x64ab), .driver_info = USB_QUIRK_AUTO_SUSPEND },
+	{ USB_DEVICE(0x0c45, 0x64ac), .driver_info = USB_QUIRK_AUTO_SUSPEND },
+
 	/* SONiX USB DEVICE Touchpad */
 	{ USB_DEVICE(0x0c45, 0x7056), .driver_info =
 			USB_QUIRK_IGNORE_REMOTE_WAKEUP },
@@ -454,12 +462,6 @@ static const struct usb_device_id usb_quirk_list[] = {
 	{ USB_DEVICE(0x17ef, 0x1018), .driver_info = USB_QUIRK_RESET_RESUME },
 	{ USB_DEVICE(0x17ef, 0x1019), .driver_info = USB_QUIRK_RESET_RESUME },
 
-	/* Lenovo USB-C to Ethernet Adapter RTL8153-04 */
-	{ USB_DEVICE(0x17ef, 0x720c), .driver_info = USB_QUIRK_NO_LPM },
-
-	/* Lenovo Powered USB-C Travel Hub (4X90S92381, RTL8153 GigE) */
-	{ USB_DEVICE(0x17ef, 0x721e), .driver_info = USB_QUIRK_NO_LPM },
-
 	/* Lenovo ThinkCenter A630Z TI024Gen3 usb-audio */
 	{ USB_DEVICE(0x17ef, 0xa012), .driver_info =
 			USB_QUIRK_DISCONNECT_SUSPEND },
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 03d16a082..12630e4d8 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -1061,6 +1061,7 @@ struct dwc2_hsotg {
 	unsigned int hcd_enabled:1;
 	unsigned int gadget_enabled:1;
 	unsigned int ll_hw_enabled:1;
+	unsigned int ll_phy_enabled:1;
 	unsigned int hibernated:1;
 	unsigned int reset_phy_on_wake:1;
 	unsigned int need_phy_for_wake:1;
@@ -1078,6 +1079,8 @@ struct dwc2_hsotg {
 	void *priv;
 	int     irq;
 	struct clk *clk;
+	struct clk_bulk_data *clks;
+	int num_clks;
 	struct reset_control *reset;
 	struct reset_control *reset_ecc;
 
@@ -1345,6 +1348,9 @@ extern const struct of_device_id dwc2_of_match_table[];
 int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg);
 int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg);
 
+int dwc2_lowlevel_phy_enable(struct dwc2_hsotg *hsotg);
+int dwc2_lowlevel_phy_disable(struct dwc2_hsotg *hsotg);
+
 /* Common polling functions */
 int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hs_otg, u32 reg, u32 bit,
 			    u32 timeout);
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index da0df69cc..99e04bf63 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -23,6 +23,7 @@
 #include <linux/io.h>
 #include <linux/slab.h>
 #include <linux/of_platform.h>
+#include <linux/platform_data/s3c-hsotg.h>
 
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
@@ -106,6 +107,23 @@ static inline bool using_desc_dma(struct dwc2_hsotg *hsotg)
 	return hsotg->params.g_dma_desc;
 }
 
+/**
+ * dwc2_hsotg_read_frameno - read current frame number
+ * @hsotg: The device instance
+ *
+ * Return the current frame number
+ */
+static u32 dwc2_hsotg_read_frameno(struct dwc2_hsotg *hsotg)
+{
+	u32 dsts;
+
+	dsts = dwc2_readl(hsotg, DSTS);
+	dsts &= DSTS_SOFFN_MASK;
+	dsts >>= DSTS_SOFFN_SHIFT;
+
+	return dsts;
+}
+
 /**
  * dwc2_gadget_incr_frame_num - Increments the targeted frame number.
  * @hs_ep: The endpoint
@@ -116,16 +134,13 @@ static inline bool using_desc_dma(struct dwc2_hsotg *hsotg)
 static inline void dwc2_gadget_incr_frame_num(struct dwc2_hsotg_ep *hs_ep)
 {
 	struct dwc2_hsotg *hsotg = hs_ep->parent;
-	u16 limit = DSTS_SOFFN_LIMIT;
-
-	if (hsotg->gadget.speed != USB_SPEED_HIGH)
-		limit >>= 3;
+	u32 current_frame = dwc2_hsotg_read_frameno(hsotg);
 
 	hs_ep->target_frame += hs_ep->interval;
-	if (hs_ep->target_frame > limit) {
+	if (hs_ep->target_frame > DSTS_SOFFN_LIMIT) {
 		hs_ep->frame_overrun = true;
-		hs_ep->target_frame &= limit;
-	} else {
+		hs_ep->target_frame &= DSTS_SOFFN_LIMIT;
+	} else if (current_frame <= hs_ep->target_frame) {
 		hs_ep->frame_overrun = false;
 	}
 }
@@ -142,16 +157,10 @@ static inline void dwc2_gadget_incr_frame_num(struct dwc2_hsotg_ep *hs_ep)
  */
 static inline void dwc2_gadget_dec_frame_num_by_one(struct dwc2_hsotg_ep *hs_ep)
 {
-	struct dwc2_hsotg *hsotg = hs_ep->parent;
-	u16 limit = DSTS_SOFFN_LIMIT;
-
-	if (hsotg->gadget.speed != USB_SPEED_HIGH)
-		limit >>= 3;
-
 	if (hs_ep->target_frame)
 		hs_ep->target_frame -= 1;
 	else
-		hs_ep->target_frame = limit;
+		hs_ep->target_frame = DSTS_SOFFN_LIMIT;
 }
 
 /**
@@ -697,23 +706,6 @@ static unsigned int get_ep_limit(struct dwc2_hsotg_ep *hs_ep)
 	return maxsize;
 }
 
-/**
- * dwc2_hsotg_read_frameno - read current frame number
- * @hsotg: The device instance
- *
- * Return the current frame number
- */
-static u32 dwc2_hsotg_read_frameno(struct dwc2_hsotg *hsotg)
-{
-	u32 dsts;
-
-	dsts = dwc2_readl(hsotg, DSTS);
-	dsts &= DSTS_SOFFN_MASK;
-	dsts >>= DSTS_SOFFN_SHIFT;
-
-	return dsts;
-}
-
 /**
  * dwc2_gadget_get_chain_limit - get the maximum data payload value of the
  * DMA descriptor chain prepared for specific endpoint
@@ -1030,12 +1022,6 @@ static void dwc2_gadget_start_isoc_ddma(struct dwc2_hsotg_ep *hs_ep)
 	dwc2_writel(hsotg, ctrl, depctl);
 }
 
-static bool dwc2_gadget_target_frame_elapsed(struct dwc2_hsotg_ep *hs_ep);
-static void dwc2_hsotg_complete_request(struct dwc2_hsotg *hsotg,
-					struct dwc2_hsotg_ep *hs_ep,
-				       struct dwc2_hsotg_req *hs_req,
-				       int result);
-
 /**
  * dwc2_hsotg_start_req - start a USB request from an endpoint's queue
  * @hsotg: The controller state.
@@ -1188,21 +1174,14 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg,
 		}
 	}
 
-	if (hs_ep->isochronous) {
-		if (!dwc2_gadget_target_frame_elapsed(hs_ep)) {
-			if (hs_ep->interval == 1) {
-				if (hs_ep->target_frame & 0x1)
-					ctrl |= DXEPCTL_SETODDFR;
-				else
-					ctrl |= DXEPCTL_SETEVENFR;
-			}
-			ctrl |= DXEPCTL_CNAK;
-		} else {
-			hs_req->req.frame_number = hs_ep->target_frame;
-			hs_req->req.actual = 0;
-			dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, -ENODATA);
-			return;
-		}
+	if (hs_ep->isochronous && hs_ep->interval == 1) {
+		hs_ep->target_frame = dwc2_hsotg_read_frameno(hsotg);
+		dwc2_gadget_incr_frame_num(hs_ep);
+
+		if (hs_ep->target_frame & 0x1)
+			ctrl |= DXEPCTL_SETODDFR;
+		else
+			ctrl |= DXEPCTL_SETEVENFR;
 	}
 
 	ctrl |= DXEPCTL_EPENA;	/* ensure ep enabled */
@@ -1350,16 +1329,12 @@ static bool dwc2_gadget_target_frame_elapsed(struct dwc2_hsotg_ep *hs_ep)
 	u32 target_frame = hs_ep->target_frame;
 	u32 current_frame = hsotg->frame_number;
 	bool frame_overrun = hs_ep->frame_overrun;
-	u16 limit = DSTS_SOFFN_LIMIT;
-
-	if (hsotg->gadget.speed != USB_SPEED_HIGH)
-		limit >>= 3;
 
 	if (!frame_overrun && current_frame >= target_frame)
 		return true;
 
 	if (frame_overrun && current_frame >= target_frame &&
-	    ((current_frame - target_frame) < limit / 2))
+	    ((current_frame - target_frame) < DSTS_SOFFN_LIMIT / 2))
 		return true;
 
 	return false;
@@ -1742,9 +1717,11 @@ static struct dwc2_hsotg_req *get_ep_head(struct dwc2_hsotg_ep *hs_ep)
  */
 static void dwc2_gadget_start_next_request(struct dwc2_hsotg_ep *hs_ep)
 {
+	u32 mask;
 	struct dwc2_hsotg *hsotg = hs_ep->parent;
 	int dir_in = hs_ep->dir_in;
 	struct dwc2_hsotg_req *hs_req;
+	u32 epmsk_reg = dir_in ? DIEPMSK : DOEPMSK;
 
 	if (!list_empty(&hs_ep->queue)) {
 		hs_req = get_ep_head(hs_ep);
@@ -1760,6 +1737,9 @@ static void dwc2_gadget_start_next_request(struct dwc2_hsotg_ep *hs_ep)
 	} else {
 		dev_dbg(hsotg->dev, "%s: No more ISOC-OUT requests\n",
 			__func__);
+		mask = dwc2_readl(hsotg, epmsk_reg);
+		mask |= DOEPMSK_OUTTKNEPDISMSK;
+		dwc2_writel(hsotg, mask, epmsk_reg);
 	}
 }
 
@@ -2329,6 +2309,19 @@ static void dwc2_hsotg_ep0_zlp(struct dwc2_hsotg *hsotg, bool dir_in)
 	dwc2_hsotg_program_zlp(hsotg, hsotg->eps_out[0]);
 }
 
+static void dwc2_hsotg_change_ep_iso_parity(struct dwc2_hsotg *hsotg,
+					    u32 epctl_reg)
+{
+	u32 ctrl;
+
+	ctrl = dwc2_readl(hsotg, epctl_reg);
+	if (ctrl & DXEPCTL_EOFRNUM)
+		ctrl |= DXEPCTL_SETEVENFR;
+	else
+		ctrl |= DXEPCTL_SETODDFR;
+	dwc2_writel(hsotg, ctrl, epctl_reg);
+}
+
 /*
  * dwc2_gadget_get_xfersize_ddma - get transferred bytes amount from desc
  * @hs_ep - The endpoint on which transfer went
@@ -2449,12 +2442,21 @@ static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum)
 			dwc2_hsotg_ep0_zlp(hsotg, true);
 	}
 
-	/* Set actual frame number for completed transfers */
-	if (!using_desc_dma(hsotg) && hs_ep->isochronous) {
-		req->frame_number = hs_ep->target_frame;
-		dwc2_gadget_incr_frame_num(hs_ep);
+	/*
+	 * Slave mode OUT transfers do not go through XferComplete so
+	 * adjust the ISOC parity here.
+	 */
+	if (!using_dma(hsotg)) {
+		if (hs_ep->isochronous && hs_ep->interval == 1)
+			dwc2_hsotg_change_ep_iso_parity(hsotg, DOEPCTL(epnum));
+		else if (hs_ep->isochronous && hs_ep->interval > 1)
+			dwc2_gadget_incr_frame_num(hs_ep);
 	}
 
+	/* Set actual frame number for completed transfers */
+	if (!using_desc_dma(hsotg) && hs_ep->isochronous)
+		req->frame_number = hsotg->frame_number;
+
 	dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, result);
 }
 
@@ -2767,12 +2769,6 @@ static void dwc2_hsotg_complete_in(struct dwc2_hsotg *hsotg,
 		return;
 	}
 
-	/* Set actual frame number for completed transfers */
-	if (!using_desc_dma(hsotg) && hs_ep->isochronous) {
-		hs_req->req.frame_number = hs_ep->target_frame;
-		dwc2_gadget_incr_frame_num(hs_ep);
-	}
-
 	dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
 }
 
@@ -2833,18 +2829,23 @@ static void dwc2_gadget_handle_ep_disabled(struct dwc2_hsotg_ep *hs_ep)
 
 		dwc2_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index);
 
+		if (hs_ep->isochronous) {
+			dwc2_hsotg_complete_in(hsotg, hs_ep);
+			return;
+		}
+
 		if ((epctl & DXEPCTL_STALL) && (epctl & DXEPCTL_EPTYPE_BULK)) {
 			int dctl = dwc2_readl(hsotg, DCTL);
 
 			dctl |= DCTL_CGNPINNAK;
 			dwc2_writel(hsotg, dctl, DCTL);
 		}
-	} else {
+		return;
+	}
 
-		if (dctl & DCTL_GOUTNAKSTS) {
-			dctl |= DCTL_CGOUTNAK;
-			dwc2_writel(hsotg, dctl, DCTL);
-		}
+	if (dctl & DCTL_GOUTNAKSTS) {
+		dctl |= DCTL_CGOUTNAK;
+		dwc2_writel(hsotg, dctl, DCTL);
 	}
 
 	if (!hs_ep->isochronous)
@@ -2858,16 +2859,15 @@ static void dwc2_gadget_handle_ep_disabled(struct dwc2_hsotg_ep *hs_ep)
 
 	do {
 		hs_req = get_ep_head(hs_ep);
-		if (hs_req) {
-			hs_req->req.frame_number = hs_ep->target_frame;
-			hs_req->req.actual = 0;
+		if (hs_req)
 			dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req,
 						    -ENODATA);
-		}
 		dwc2_gadget_incr_frame_num(hs_ep);
 		/* Update current frame number value. */
 		hsotg->frame_number = dwc2_hsotg_read_frameno(hsotg);
 	} while (dwc2_gadget_target_frame_elapsed(hs_ep));
+
+	dwc2_gadget_start_next_request(hs_ep);
 }
 
 /**
@@ -2884,8 +2884,8 @@ static void dwc2_gadget_handle_ep_disabled(struct dwc2_hsotg_ep *hs_ep)
 static void dwc2_gadget_handle_out_token_ep_disabled(struct dwc2_hsotg_ep *ep)
 {
 	struct dwc2_hsotg *hsotg = ep->parent;
-	struct dwc2_hsotg_req *hs_req;
 	int dir_in = ep->dir_in;
+	u32 doepmsk;
 
 	if (dir_in || !ep->isochronous)
 		return;
@@ -2899,42 +2899,28 @@ static void dwc2_gadget_handle_out_token_ep_disabled(struct dwc2_hsotg_ep *ep)
 		return;
 	}
 
-	if (ep->target_frame == TARGET_FRAME_INITIAL) {
+	if (ep->interval > 1 &&
+	    ep->target_frame == TARGET_FRAME_INITIAL) {
 		u32 ctrl;
 
 		ep->target_frame = hsotg->frame_number;
-		if (ep->interval > 1) {
-			ctrl = dwc2_readl(hsotg, DOEPCTL(ep->index));
-			if (ep->target_frame & 0x1)
-				ctrl |= DXEPCTL_SETODDFR;
-			else
-				ctrl |= DXEPCTL_SETEVENFR;
-
-			dwc2_writel(hsotg, ctrl, DOEPCTL(ep->index));
-		}
-	}
+		dwc2_gadget_incr_frame_num(ep);
 
-	while (dwc2_gadget_target_frame_elapsed(ep)) {
-		hs_req = get_ep_head(ep);
-		if (hs_req) {
-			hs_req->req.frame_number = ep->target_frame;
-			hs_req->req.actual = 0;
-			dwc2_hsotg_complete_request(hsotg, ep, hs_req, -ENODATA);
-		}
+		ctrl = dwc2_readl(hsotg, DOEPCTL(ep->index));
+		if (ep->target_frame & 0x1)
+			ctrl |= DXEPCTL_SETODDFR;
+		else
+			ctrl |= DXEPCTL_SETEVENFR;
 
-		dwc2_gadget_incr_frame_num(ep);
-		/* Update current frame number value. */
-		hsotg->frame_number = dwc2_hsotg_read_frameno(hsotg);
+		dwc2_writel(hsotg, ctrl, DOEPCTL(ep->index));
 	}
 
-	if (!ep->req)
-		dwc2_gadget_start_next_request(ep);
-
+	dwc2_gadget_start_next_request(ep);
+	doepmsk = dwc2_readl(hsotg, DOEPMSK);
+	doepmsk &= ~DOEPMSK_OUTTKNEPDISMSK;
+	dwc2_writel(hsotg, doepmsk, DOEPMSK);
 }
 
-static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
-				   struct dwc2_hsotg_ep *hs_ep);
-
 /**
  * dwc2_gadget_handle_nak - handle NAK interrupt
  * @hs_ep: The endpoint on which interrupt is asserted.
@@ -2952,9 +2938,7 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg,
 static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep)
 {
 	struct dwc2_hsotg *hsotg = hs_ep->parent;
-	struct dwc2_hsotg_req *hs_req;
 	int dir_in = hs_ep->dir_in;
-	u32 ctrl;
 
 	if (!dir_in || !hs_ep->isochronous)
 		return;
@@ -2996,32 +2980,13 @@ static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep)
 
 			dwc2_writel(hsotg, ctrl, DIEPCTL(hs_ep->index));
 		}
-	}
 
-	if (using_desc_dma(hsotg))
-		return;
-
-	ctrl = dwc2_readl(hsotg, DIEPCTL(hs_ep->index));
-	if (ctrl & DXEPCTL_EPENA)
-		dwc2_hsotg_ep_stop_xfr(hsotg, hs_ep);
-	else
-		dwc2_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index);
-
-	while (dwc2_gadget_target_frame_elapsed(hs_ep)) {
-		hs_req = get_ep_head(hs_ep);
-		if (hs_req) {
-			hs_req->req.frame_number = hs_ep->target_frame;
-			hs_req->req.actual = 0;
-			dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, -ENODATA);
-		}
-
-		dwc2_gadget_incr_frame_num(hs_ep);
-		/* Update current frame number value. */
-		hsotg->frame_number = dwc2_hsotg_read_frameno(hsotg);
+		dwc2_hsotg_complete_request(hsotg, hs_ep,
+					    get_ep_head(hs_ep), 0);
 	}
 
-	if (!hs_ep->req)
-		dwc2_gadget_start_next_request(hs_ep);
+	if (!using_desc_dma(hsotg))
+		dwc2_gadget_incr_frame_num(hs_ep);
 }
 
 /**
@@ -3077,15 +3042,21 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
 
 		/* In DDMA handle isochronous requests separately */
 		if (using_desc_dma(hsotg) && hs_ep->isochronous) {
-			dwc2_gadget_complete_isoc_request_ddma(hs_ep);
+			/* XferCompl set along with BNA */
+			if (!(ints & DXEPINT_BNAINTR))
+				dwc2_gadget_complete_isoc_request_ddma(hs_ep);
 		} else if (dir_in) {
 			/*
 			 * We get OutDone from the FIFO, so we only
 			 * need to look at completing IN requests here
 			 * if operating slave mode
 			 */
-			if (!hs_ep->isochronous || !(ints & DXEPINT_NAKINTRPT))
-				dwc2_hsotg_complete_in(hsotg, hs_ep);
+			if (hs_ep->isochronous && hs_ep->interval > 1)
+				dwc2_gadget_incr_frame_num(hs_ep);
+
+			dwc2_hsotg_complete_in(hsotg, hs_ep);
+			if (ints & DXEPINT_NAKINTRPT)
+				ints &= ~DXEPINT_NAKINTRPT;
 
 			if (idx == 0 && !hs_ep->req)
 				dwc2_hsotg_enqueue_setup(hsotg);
@@ -3094,8 +3065,10 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
 			 * We're using DMA, we need to fire an OutDone here
 			 * as we ignore the RXFIFO.
 			 */
-			if (!hs_ep->isochronous || !(ints & DXEPINT_OUTTKNEPDIS))
-				dwc2_hsotg_handle_outdone(hsotg, idx);
+			if (hs_ep->isochronous && hs_ep->interval > 1)
+				dwc2_gadget_incr_frame_num(hs_ep);
+
+			dwc2_hsotg_handle_outdone(hsotg, idx);
 		}
 	}
 
@@ -4115,7 +4088,6 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep,
 			mask |= DIEPMSK_NAKMSK;
 			dwc2_writel(hsotg, mask, DIEPMSK);
 		} else {
-			epctrl |= DXEPCTL_SNAK;
 			mask = dwc2_readl(hsotg, DOEPMSK);
 			mask |= DOEPMSK_OUTTKNEPDISMSK;
 			dwc2_writel(hsotg, mask, DOEPMSK);
@@ -4529,6 +4501,14 @@ static int dwc2_hsotg_udc_start(struct usb_gadget *gadget,
 			goto err;
 	}
 
+	if (hsotg->dr_mode == USB_DR_MODE_OTG && dwc2_is_device_mode(hsotg)) {
+		if (!hsotg->ll_phy_enabled) {
+			ret = dwc2_lowlevel_phy_enable(hsotg);
+			if (ret)
+				goto err;
+		}
+	}
+
 	if (!IS_ERR_OR_NULL(hsotg->uphy))
 		otg_set_peripheral(hsotg->uphy->otg, &hsotg->gadget);
 
@@ -4588,6 +4568,11 @@ static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget)
 	if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
 		dwc2_lowlevel_hw_disable(hsotg);
 
+	if (hsotg->dr_mode == USB_DR_MODE_OTG && dwc2_is_device_mode(hsotg)) {
+		if (hsotg->ll_phy_enabled)
+			dwc2_lowlevel_phy_disable(hsotg);
+	}
+
 	return 0;
 }
 
@@ -5032,7 +5017,7 @@ int dwc2_hsotg_suspend(struct dwc2_hsotg *hsotg)
 		hsotg->gadget.speed = USB_SPEED_UNKNOWN;
 		spin_unlock_irqrestore(&hsotg->lock, flags);
 
-		for (ep = 1; ep < hsotg->num_of_eps; ep++) {
+		for (ep = 0; ep < hsotg->num_of_eps; ep++) {
 			if (hsotg->eps_in[ep])
 				dwc2_hsotg_ep_disable_lock(&hsotg->eps_in[ep]->ep);
 			if (hsotg->eps_out[ep])
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 9279d3d36..e6fadfd68 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -812,11 +812,13 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
 	 * uframe/frame (in the worst case), the core generates a channel
 	 * halted and disables the channel automatically.
 	 */
-	if ((hsotg->params.g_dma && !hsotg->params.g_dma_desc) ||
+	if ((hsotg->params.host_dma && !hsotg->params.dma_desc_enable) ||
 	    hsotg->hw_params.arch == GHWCFG2_EXT_DMA_ARCH) {
 		if (!chan->do_split &&
 		    (chan->ep_type == USB_ENDPOINT_XFER_ISOC ||
-		     chan->ep_type == USB_ENDPOINT_XFER_INT)) {
+		     chan->ep_type == USB_ENDPOINT_XFER_INT) &&
+		    (halt_status == DWC2_HC_XFER_URB_DEQUEUE)) {
+			chan->halt_status = halt_status;
 			dev_err(hsotg->dev, "%s() Channel can't be halted\n",
 				__func__);
 			return;
@@ -1827,11 +1829,13 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg, bool force)
 	 * Without the extra check here we will end calling disconnect
 	 * and won't get any future interrupts to handle the connect.
 	 */
-	if (!force) {
-		hprt0 = dwc2_readl(hsotg, HPRT0);
-		if (!(hprt0 & HPRT0_CONNDET) && (hprt0 & HPRT0_CONNSTS))
-			dwc2_hcd_connect(hsotg);
-	}
+	hprt0 = dwc2_readl(hsotg, HPRT0);
+
+	if (!force && !(hprt0 & HPRT0_CONNDET) &&
+	    (hprt0 & HPRT0_CONNSTS))
+		dwc2_hcd_connect(hsotg);
+	else if (hsotg->lx_state != DWC2_L0)
+		usb_hcd_resume_root_hub(hsotg->priv);
 }
 
 /**
@@ -2441,10 +2445,13 @@ static void dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg,
 	}
 }
 
-static int dwc2_alloc_split_dma_aligned_buf(struct dwc2_hsotg *hsotg,
-					    struct dwc2_qh *qh,
-					    struct dwc2_host_chan *chan)
+static int dwc2_alloc_qh_dma_aligned_buf(struct dwc2_hsotg *hsotg,
+					 struct dwc2_qh *qh,
+					 struct dwc2_qtd *qtd,
+					 struct dwc2_host_chan *chan)
 {
+	u32 offset;
+
 	if (!hsotg->unaligned_cache ||
 	    chan->max_packet > DWC2_KMEM_UNALIGNED_BUF_SIZE)
 		return -ENOMEM;
@@ -2456,6 +2463,18 @@ static int dwc2_alloc_split_dma_aligned_buf(struct dwc2_hsotg *hsotg,
 			return -ENOMEM;
 	}
 
+	if (!chan->ep_is_in) {
+		if (qh->do_split) {
+			offset = chan->xfer_dma - qtd->urb->dma;
+			memcpy(qh->dw_align_buf, (u8 *)qtd->urb->buf + offset,
+			       (chan->xfer_len > 188 ? 188 : chan->xfer_len));
+		} else {
+			offset = chan->xfer_dma - qtd->urb->dma;
+			memcpy(qh->dw_align_buf, (u8 *)qtd->urb->buf + offset,
+			       chan->xfer_len);
+		}
+	}
+
 	qh->dw_align_buf_dma = dma_map_single(hsotg->dev, qh->dw_align_buf,
 					      DWC2_KMEM_UNALIGNED_BUF_SIZE,
 					      DMA_FROM_DEVICE);
@@ -2660,10 +2679,10 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 	dwc2_hc_init_xfer(hsotg, chan, qtd);
 
 	/* For non-dword aligned buffers */
-	if (hsotg->params.host_dma && qh->do_split &&
-	    chan->ep_is_in && (chan->xfer_dma & 0x3)) {
+	if (hsotg->params.host_dma && (chan->xfer_dma & 0x3) &&
+	    chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
 		dev_vdbg(hsotg->dev, "Non-aligned buffer\n");
-		if (dwc2_alloc_split_dma_aligned_buf(hsotg, qh, chan)) {
+		if (dwc2_alloc_qh_dma_aligned_buf(hsotg, qh, qtd, chan)) {
 			dev_err(hsotg->dev,
 				"Failed to allocate memory to handle non-aligned buffer\n");
 			/* Add channel back to free list */
@@ -2677,8 +2696,8 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
 		}
 	} else {
 		/*
-		 * We assume that DMA is always aligned in non-split
-		 * case or split out case. Warn if not.
+		 * We assume that DMA is always aligned in other case,
+		 * Warn if not.
 		 */
 		WARN_ON_ONCE(hsotg->params.host_dma &&
 			     (chan->xfer_dma & 0x3));
@@ -3173,6 +3192,9 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
 
 	dev_dbg(hsotg->dev, "%s()\n", __func__);
 
+	if (!hsotg->ll_phy_enabled && dwc2_is_host_mode(hsotg))
+		dwc2_lowlevel_phy_enable(hsotg);
+
 	gotgctl = dwc2_readl(hsotg, GOTGCTL);
 	dev_dbg(hsotg->dev, "gotgctl=%0x\n", gotgctl);
 	dev_dbg(hsotg->dev, "gotgctl.b.conidsts=%d\n",
@@ -5074,10 +5096,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
 	hcd->has_tt = 1;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!res) {
-		retval = -EINVAL;
-		goto error2;
-	}
 	hcd->rsrc_start = res->start;
 	hcd->rsrc_len = resource_size(res);
 
@@ -5228,6 +5246,13 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg)
 	if (!IS_ERR_OR_NULL(hsotg->uphy))
 		otg_set_host(hsotg->uphy->otg, &hcd->self);
 
+	/*
+	 * do not manage the PHY state in the HCD core, instead let the driver
+	 * handle this (for example if the PHY can only be turned on after a
+	 * specific event)
+	 */
+	hcd->skip_phy_initialization = 1;
+
 	/*
 	 * Finish generic HCD initialization and start the HCD. This function
 	 * allocates the DMA buffer pool, registers the USB bus, requests the
diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c
index d5f4ec1b7..4989ec761 100644
--- a/drivers/usb/dwc2/hcd_intr.c
+++ b/drivers/usb/dwc2/hcd_intr.c
@@ -1180,7 +1180,10 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg,
 
 	if (urb->actual_length + xfer_length > urb->length) {
 		dev_warn(hsotg->dev, "%s(): trimming xfer length\n", __func__);
-		xfer_length = urb->length - urb->actual_length;
+		if (urb->length & 0x3)
+			xfer_length = 0;
+		else
+			xfer_length = urb->length - urb->actual_length;
 	}
 
 	urb->actual_length += xfer_length;
@@ -2065,8 +2068,6 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
 			 hcint, hcintmsk, hcint & hcintmsk);
 	}
 
-	dwc2_writel(hsotg, hcint, HCINT(chnum));
-
 	/*
 	 * If we got an interrupt after someone called
 	 * dwc2_hcd_endpoint_disable() we don't want to crash below
@@ -2079,6 +2080,8 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
 	chan->hcint = hcint;
 	hcint &= hcintmsk;
 
+	dwc2_writel(hsotg, hcint, HCINT(chnum));
+
 	/*
 	 * If the channel was halted due to a dequeue, the qtd list might
 	 * be empty or at least the first entry will not be the active qtd.
diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c
index 94af71e98..9d2b92a37 100644
--- a/drivers/usb/dwc2/hcd_queue.c
+++ b/drivers/usb/dwc2/hcd_queue.c
@@ -59,7 +59,7 @@
 #define DWC2_UNRESERVE_DELAY (msecs_to_jiffies(5))
 
 /* If we get a NAK, wait this long before retrying */
-#define DWC2_RETRY_WAIT_DELAY (1 * NSEC_PER_MSEC)
+#define DWC2_RETRY_WAIT_DELAY 1*1E6L
 
 /**
  * dwc2_periodic_channel_available() - Checks that a channel is available for a
@@ -730,8 +730,14 @@ static int dwc2_uframe_schedule_split(struct dwc2_hsotg *hsotg,
 	 * Note that this will tend to front-load the high speed schedule.
 	 * We may eventually want to try to avoid this by either considering
 	 * both schedules together or doing some sort of round robin.
+	 *
+	 * For isoc split out, start schedule at the 2 * DWC2_SLICES_PER_UFRAME
+	 * to transfer SSPLIT-begin OUT transaction like EHCI controller.
 	 */
-	ls_search_slice = 0;
+	if (qh->ep_type == USB_ENDPOINT_XFER_ISOC && !qh->ep_is_in)
+		ls_search_slice = 2 * DWC2_SLICES_PER_UFRAME;
+	else
+		ls_search_slice = 0;
 
 	while (ls_search_slice < DWC2_LS_SCHEDULE_SLICES) {
 		int start_s_uframe;
diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c
index 267543c3d..195cca1fe 100644
--- a/drivers/usb/dwc2/params.c
+++ b/drivers/usb/dwc2/params.c
@@ -90,6 +90,8 @@ static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg)
 	p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
 		GAHBCFG_HBSTLEN_SHIFT;
 	p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
+	p->lpm = false;
+	p->g_dma_desc = false;
 }
 
 static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg)
@@ -177,7 +179,10 @@ static void dwc2_set_stm32mp15_fsotg_params(struct dwc2_hsotg *hsotg)
 	p->i2c_enable = false;
 	p->activate_stm_fs_transceiver = true;
 	p->activate_stm_id_vb_detection = true;
+	p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT;
 	p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
+	p->host_support_fs_ls_low_power = true;
+	p->host_ls_low_power_phy_clk = true;
 }
 
 static void dwc2_set_stm32mp15_hsotg_params(struct dwc2_hsotg *hsotg)
@@ -189,7 +194,12 @@ static void dwc2_set_stm32mp15_hsotg_params(struct dwc2_hsotg *hsotg)
 	p->host_rx_fifo_size = 440;
 	p->host_nperio_tx_fifo_size = 256;
 	p->host_perio_tx_fifo_size = 256;
+	p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 << GAHBCFG_HBSTLEN_SHIFT;
 	p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
+	p->lpm = false;
+	p->lpm_clock_gating = false;
+	p->besl = false;
+	p->hird_threshold_en = false;
 }
 
 const struct of_device_id dwc2_of_match_table[] = {
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 8851db646..f658e3cdc 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -128,27 +128,11 @@ static void __dwc2_disable_regulators(void *data)
 	regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
 }
 
-static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
+static int __dwc2_lowlevel_phy_enable(struct dwc2_hsotg *hsotg)
 {
 	struct platform_device *pdev = to_platform_device(hsotg->dev);
 	int ret;
 
-	ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies),
-				    hsotg->supplies);
-	if (ret)
-		return ret;
-
-	ret = devm_add_action_or_reset(&pdev->dev,
-				       __dwc2_disable_regulators, hsotg);
-	if (ret)
-		return ret;
-
-	if (hsotg->clk) {
-		ret = clk_prepare_enable(hsotg->clk);
-		if (ret)
-			return ret;
-	}
-
 	if (hsotg->uphy) {
 		ret = usb_phy_init(hsotg->uphy);
 	} else if (hsotg->plat && hsotg->plat->phy_init) {
@@ -163,22 +147,22 @@ static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
 }
 
 /**
- * dwc2_lowlevel_hw_enable - enable platform lowlevel hw resources
+ * dwc2_lowlevel_phy_enable - enable lowlevel PHY resources
  * @hsotg: The driver state
  *
  * A wrapper for platform code responsible for controlling
- * low-level USB platform resources (phy, clock, regulators)
+ * low-level PHY resources.
  */
-int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
+int dwc2_lowlevel_phy_enable(struct dwc2_hsotg *hsotg)
 {
-	int ret = __dwc2_lowlevel_hw_enable(hsotg);
+	int ret = __dwc2_lowlevel_phy_enable(hsotg);
 
 	if (ret == 0)
-		hsotg->ll_hw_enabled = true;
+		hsotg->ll_phy_enabled = true;
 	return ret;
 }
 
-static int __dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg)
+static int __dwc2_lowlevel_phy_disable(struct dwc2_hsotg *hsotg)
 {
 	struct platform_device *pdev = to_platform_device(hsotg->dev);
 	int ret = 0;
@@ -192,11 +176,78 @@ static int __dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg)
 		if (ret == 0)
 			ret = phy_exit(hsotg->phy);
 	}
+
+	return ret;
+}
+
+/**
+ * dwc2_lowlevel_phy_disable - disable lowlevel PHY resources
+ * @hsotg: The driver state
+ *
+ * A wrapper for platform code responsible for controlling
+ * low-level PHY platform resources.
+ */
+int dwc2_lowlevel_phy_disable(struct dwc2_hsotg *hsotg)
+{
+	int ret = __dwc2_lowlevel_phy_disable(hsotg);
+
+	if (ret == 0)
+		hsotg->ll_phy_enabled = false;
+	return ret;
+}
+
+static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
+{
+	struct platform_device *pdev = to_platform_device(hsotg->dev);
+	int ret;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies),
+				    hsotg->supplies);
+	if (ret)
+		return ret;
+
+	ret = devm_add_action_or_reset(&pdev->dev,
+				       __dwc2_disable_regulators, hsotg);
+	if (ret)
+		return ret;
+
+	ret = clk_bulk_prepare_enable(hsotg->num_clks, hsotg->clks);
 	if (ret)
 		return ret;
 
-	if (hsotg->clk)
-		clk_disable_unprepare(hsotg->clk);
+	if (!hsotg->ll_phy_enabled)
+		ret = dwc2_lowlevel_phy_enable(hsotg);
+
+	return ret;
+}
+
+/**
+ * dwc2_lowlevel_hw_enable - enable platform lowlevel hw resources
+ * @hsotg: The driver state
+ *
+ * A wrapper for platform code responsible for controlling
+ * low-level USB platform resources (phy, clock, regulators)
+ */
+int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
+{
+	int ret = __dwc2_lowlevel_hw_enable(hsotg);
+
+	if (ret == 0)
+		hsotg->ll_hw_enabled = true;
+	return ret;
+}
+
+static int __dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg)
+{
+	int ret = 0;
+
+	if (hsotg->ll_phy_enabled)
+		ret = dwc2_lowlevel_phy_disable(hsotg);
+
+	if (ret)
+		return ret;
+
+	clk_bulk_disable_unprepare(hsotg->num_clks, hsotg->clks);
 
 	return 0;
 }
@@ -281,10 +332,18 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg)
 	hsotg->plat = dev_get_platdata(hsotg->dev);
 
 	/* Clock */
-	hsotg->clk = devm_clk_get_optional(hsotg->dev, "otg");
-	if (IS_ERR(hsotg->clk)) {
-		dev_err(hsotg->dev, "cannot get otg clock\n");
-		return PTR_ERR(hsotg->clk);
+	if (hsotg->dev->of_node) {
+		ret = devm_clk_bulk_get_all(hsotg->dev, &hsotg->clks);
+		if (ret == -EPROBE_DEFER)
+			return ret;
+		/*
+		 * Clocks are optional, but new DT platforms should support all
+		 * clocks as required by the DT-binding.
+		 */
+		if (ret < 0)
+			hsotg->num_clks = 0;
+		else
+			hsotg->num_clks = ret;
 	}
 
 	/* Regulators */
@@ -328,6 +387,9 @@ static int dwc2_driver_remove(struct platform_device *dev)
 	if (hsotg->params.activate_stm_id_vb_detection)
 		regulator_disable(hsotg->usb33d);
 
+	pm_runtime_put_sync(hsotg->dev);
+	pm_runtime_disable(hsotg->dev);
+
 	if (hsotg->ll_hw_enabled)
 		dwc2_lowlevel_hw_disable(hsotg);
 
@@ -479,6 +541,11 @@ static int dwc2_driver_probe(struct platform_device *dev)
 
 	hsotg->needs_byte_swap = dwc2_check_core_endianness(hsotg);
 
+	pm_runtime_enable(hsotg->dev);
+	retval = pm_runtime_get_sync(hsotg->dev);
+	if (retval < 0)
+		goto error;
+
 	retval = dwc2_get_dr_mode(hsotg);
 	if (retval)
 		goto error;
@@ -542,9 +609,6 @@ static int dwc2_driver_probe(struct platform_device *dev)
 		ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN;
 		ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN;
 		dwc2_writel(hsotg, ggpio, GGPIO);
-
-		/* ID/VBUS detection startup time */
-		usleep_range(5000, 7000);
 	}
 
 	retval = dwc2_drd_init(hsotg);
@@ -597,6 +661,11 @@ static int dwc2_driver_probe(struct platform_device *dev)
 	if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)
 		dwc2_lowlevel_hw_disable(hsotg);
 
+	if (hsotg->dr_mode == USB_DR_MODE_OTG && dwc2_is_device_mode(hsotg)) {
+		if (hsotg->ll_phy_enabled)
+			dwc2_lowlevel_phy_disable(hsotg);
+	}
+
 #if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
 	IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
 	/* Postponed adding a new gadget to the udc class driver list */
@@ -625,6 +694,8 @@ static int dwc2_driver_probe(struct platform_device *dev)
 	if (hsotg->params.activate_stm_id_vb_detection)
 		regulator_disable(hsotg->usb33d);
 error:
+	pm_runtime_put_sync(hsotg->dev);
+	pm_runtime_disable(hsotg->dev);
 	if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL)
 		dwc2_lowlevel_hw_disable(hsotg);
 	return retval;
@@ -684,6 +755,7 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
 static int __maybe_unused dwc2_resume(struct device *dev)
 {
 	struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
+	unsigned long flags;
 	int ret = 0;
 
 	if (dwc2->phy_off_for_suspend && dwc2->ll_hw_enabled) {
@@ -723,6 +795,17 @@ static int __maybe_unused dwc2_resume(struct device *dev)
 
 	dwc2_drd_resume(dwc2);
 
+	/* Stop hcd if dr_mode is host and PD is power off when suspend */
+	if (dwc2->op_state == OTG_STATE_A_HOST && dwc2_is_device_mode(dwc2)) {
+		spin_lock_irqsave(&dwc2->lock, flags);
+		dwc2_hcd_disconnect(dwc2, true);
+		dwc2->op_state = OTG_STATE_B_PERIPHERAL;
+		dwc2->lx_state = DWC2_L3;
+		if (!dwc2->driver)
+			dwc2_hsotg_core_init_disconnected(dwc2, false);
+		spin_unlock_irqrestore(&dwc2->lock, flags);
+	}
+
 	if (dwc2_is_device_mode(dwc2))
 		ret = dwc2_hsotg_resume(dwc2);
 
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index d73f624ed..7078a4912 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -119,26 +119,35 @@ static void __dwc3_set_mode(struct work_struct *work)
 	struct dwc3 *dwc = work_to_dwc(work);
 	unsigned long flags;
 	int ret;
+	int retries = 1000;
 	u32 reg;
-	u32 desired_dr_role;
 
 	mutex_lock(&dwc->mutex);
-	spin_lock_irqsave(&dwc->lock, flags);
-	desired_dr_role = dwc->desired_dr_role;
-	spin_unlock_irqrestore(&dwc->lock, flags);
 
 	pm_runtime_get_sync(dwc->dev);
 
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+	if (dwc->desired_role_sw_mode == USB_DR_MODE_PERIPHERAL &&
+	    dwc->desired_role_sw_mode != dwc->current_role_sw_mode)
+		pm_runtime_get(dwc->dev);
+	else if ((dwc->desired_role_sw_mode == USB_DR_MODE_UNKNOWN ||
+		  dwc->desired_role_sw_mode == USB_DR_MODE_HOST) &&
+		  dwc->current_role_sw_mode == USB_DR_MODE_PERIPHERAL)
+		pm_runtime_put(dwc->dev);
+
+	dwc->current_role_sw_mode = dwc->desired_role_sw_mode;
+#endif
+
 	if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
 		dwc3_otg_update(dwc, 0);
 
-	if (!desired_dr_role)
+	if (!dwc->desired_dr_role)
 		goto out;
 
-	if (desired_dr_role == dwc->current_dr_role)
+	if (dwc->desired_dr_role == dwc->current_dr_role)
 		goto out;
 
-	if (desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev)
+	if (dwc->desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev)
 		goto out;
 
 	switch (dwc->current_dr_role) {
@@ -160,13 +169,8 @@ static void __dwc3_set_mode(struct work_struct *work)
 		break;
 	}
 
-	/*
-	 * When current_dr_role is not set, there's no role switching.
-	 * Only perform GCTL.CoreSoftReset when there's DRD role switching.
-	 */
-	if (dwc->current_dr_role && ((DWC3_IP_IS(DWC3) ||
-			DWC3_VER_IS_PRIOR(DWC31, 190A)) &&
-			desired_dr_role != DWC3_GCTL_PRTCAP_OTG)) {
+	/* For DRD host or device mode only */
+	if (dwc->desired_dr_role != DWC3_GCTL_PRTCAP_OTG) {
 		reg = dwc3_readl(dwc->regs, DWC3_GCTL);
 		reg |= DWC3_GCTL_CORESOFTRESET;
 		dwc3_writel(dwc->regs, DWC3_GCTL, reg);
@@ -186,11 +190,11 @@ static void __dwc3_set_mode(struct work_struct *work)
 
 	spin_lock_irqsave(&dwc->lock, flags);
 
-	dwc3_set_prtcap(dwc, desired_dr_role);
+	dwc3_set_prtcap(dwc, dwc->desired_dr_role);
 
 	spin_unlock_irqrestore(&dwc->lock, flags);
 
-	switch (desired_dr_role) {
+	switch (dwc->desired_dr_role) {
 	case DWC3_GCTL_PRTCAP_HOST:
 		ret = dwc3_host_init(dwc);
 		if (ret) {
@@ -208,7 +212,26 @@ static void __dwc3_set_mode(struct work_struct *work)
 		}
 		break;
 	case DWC3_GCTL_PRTCAP_DEVICE:
-		dwc3_core_soft_reset(dwc);
+		reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+		reg |= DWC3_DCTL_CSFTRST;
+		dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+		if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32))
+			retries = 10;
+
+		do {
+			reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+			if (!(reg & DWC3_DCTL_CSFTRST))
+				goto done;
+
+			if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32))
+				msleep(20);
+			else
+				udelay(1);
+		} while (--retries);
+done:
+		if (DWC3_VER_IS_WITHIN(DWC31, ANY, 180A))
+			msleep(50);
 
 		dwc3_event_buffers_setup(dwc);
 
@@ -267,10 +290,23 @@ u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type)
  * dwc3_core_soft_reset - Issues core soft reset and PHY reset
  * @dwc: pointer to our context structure
  */
-int dwc3_core_soft_reset(struct dwc3 *dwc)
+static int dwc3_core_soft_reset(struct dwc3 *dwc)
 {
 	u32		reg;
 	int		retries = 1000;
+	int		ret;
+
+	usb_phy_init(dwc->usb2_phy);
+	usb_phy_init(dwc->usb3_phy);
+	ret = phy_init(dwc->usb2_generic_phy);
+	if (ret < 0)
+		return ret;
+
+	ret = phy_init(dwc->usb3_generic_phy);
+	if (ret < 0) {
+		phy_exit(dwc->usb2_generic_phy);
+		return ret;
+	}
 
 	/*
 	 * We're resetting only the device side because, if we're in host mode,
@@ -282,8 +318,7 @@ int dwc3_core_soft_reset(struct dwc3 *dwc)
 
 	reg = dwc3_readl(dwc->regs, DWC3_DCTL);
 	reg |= DWC3_DCTL_CSFTRST;
-	reg &= ~DWC3_DCTL_RUN_STOP;
-	dwc3_gadget_dctl_write_safe(dwc, reg);
+	dwc3_writel(dwc->regs, DWC3_DCTL, reg);
 
 	/*
 	 * For DWC_usb31 controller 1.90a and later, the DCTL.CSFRST bit
@@ -305,6 +340,9 @@ int dwc3_core_soft_reset(struct dwc3 *dwc)
 			udelay(1);
 	} while (--retries);
 
+	phy_exit(dwc->usb3_generic_phy);
+	phy_exit(dwc->usb2_generic_phy);
+
 	return -ETIMEDOUT;
 
 done:
@@ -562,6 +600,9 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
 	parms->hwparams6 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6);
 	parms->hwparams7 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS7);
 	parms->hwparams8 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS8);
+
+	if (DWC3_IP_IS(DWC32))
+		parms->hwparams9 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS9);
 }
 
 static int dwc3_core_ulpi_init(struct dwc3 *dwc)
@@ -730,16 +771,15 @@ static void dwc3_core_exit(struct dwc3 *dwc)
 {
 	dwc3_event_buffers_cleanup(dwc);
 
-	usb_phy_set_suspend(dwc->usb2_phy, 1);
-	usb_phy_set_suspend(dwc->usb3_phy, 1);
-	phy_power_off(dwc->usb2_generic_phy);
-	phy_power_off(dwc->usb3_generic_phy);
-
 	usb_phy_shutdown(dwc->usb2_phy);
 	usb_phy_shutdown(dwc->usb3_phy);
 	phy_exit(dwc->usb2_generic_phy);
 	phy_exit(dwc->usb3_generic_phy);
 
+	usb_phy_set_suspend(dwc->usb2_phy, 1);
+	usb_phy_set_suspend(dwc->usb3_phy, 1);
+	phy_power_off(dwc->usb2_generic_phy);
+	phy_power_off(dwc->usb3_generic_phy);
 	clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
 	reset_control_assert(dwc->reset);
 }
@@ -960,13 +1000,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
 
 	if (!dwc->ulpi_ready) {
 		ret = dwc3_core_ulpi_init(dwc);
-		if (ret) {
-			if (ret == -ETIMEDOUT) {
-				dwc3_core_soft_reset(dwc);
-				ret = -EPROBE_DEFER;
-			}
+		if (ret)
 			goto err0;
-		}
 		dwc->ulpi_ready = true;
 	}
 
@@ -977,21 +1012,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
 		dwc->phys_ready = true;
 	}
 
-	usb_phy_init(dwc->usb2_phy);
-	usb_phy_init(dwc->usb3_phy);
-	ret = phy_init(dwc->usb2_generic_phy);
-	if (ret < 0)
-		goto err0a;
-
-	ret = phy_init(dwc->usb3_generic_phy);
-	if (ret < 0) {
-		phy_exit(dwc->usb2_generic_phy);
-		goto err0a;
-	}
-
 	ret = dwc3_core_soft_reset(dwc);
 	if (ret)
-		goto err1;
+		goto err0a;
 
 	if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD &&
 	    !DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) {
@@ -1057,12 +1080,25 @@ static int dwc3_core_init(struct dwc3 *dwc)
 		if (!DWC3_VER_IS_PRIOR(DWC3, 290A))
 			reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW;
 
+		/*
+		 * Decouple USB 2.0 L1 & L2 events which will allow for
+		 * gadget driver to only receive U3/L2 suspend & wakeup
+		 * events and prevent the more frequent L1 LPM transitions
+		 * from interrupting the driver.
+		 */
+		if (!DWC3_VER_IS_PRIOR(DWC3, 300A))
+			reg |= DWC3_GUCTL1_DEV_DECOUPLE_L1L2_EVT;
+
 		if (dwc->dis_tx_ipgap_linecheck_quirk)
 			reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS;
 
 		if (dwc->parkmode_disable_ss_quirk)
 			reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS;
 
+		if (dwc->maximum_speed == USB_SPEED_HIGH ||
+		    dwc->maximum_speed == USB_SPEED_FULL)
+			reg |= DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK;
+
 		dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
 	}
 
@@ -1162,11 +1198,8 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
 		ret = PTR_ERR(dwc->usb2_phy);
 		if (ret == -ENXIO || ret == -ENODEV) {
 			dwc->usb2_phy = NULL;
-		} else if (ret == -EPROBE_DEFER) {
-			return ret;
 		} else {
-			dev_err(dev, "no usb2 phy configured\n");
-			return ret;
+			return dev_err_probe(dev, ret, "no usb2 phy configured\n");
 		}
 	}
 
@@ -1174,11 +1207,8 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
 		ret = PTR_ERR(dwc->usb3_phy);
 		if (ret == -ENXIO || ret == -ENODEV) {
 			dwc->usb3_phy = NULL;
-		} else if (ret == -EPROBE_DEFER) {
-			return ret;
 		} else {
-			dev_err(dev, "no usb3 phy configured\n");
-			return ret;
+			return dev_err_probe(dev, ret, "no usb3 phy configured\n");
 		}
 	}
 
@@ -1187,11 +1217,8 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
 		ret = PTR_ERR(dwc->usb2_generic_phy);
 		if (ret == -ENOSYS || ret == -ENODEV) {
 			dwc->usb2_generic_phy = NULL;
-		} else if (ret == -EPROBE_DEFER) {
-			return ret;
 		} else {
-			dev_err(dev, "no usb2 phy configured\n");
-			return ret;
+			return dev_err_probe(dev, ret, "no usb2 phy configured\n");
 		}
 	}
 
@@ -1200,11 +1227,8 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
 		ret = PTR_ERR(dwc->usb3_generic_phy);
 		if (ret == -ENOSYS || ret == -ENODEV) {
 			dwc->usb3_generic_phy = NULL;
-		} else if (ret == -EPROBE_DEFER) {
-			return ret;
 		} else {
-			dev_err(dev, "no usb3 phy configured\n");
-			return ret;
+			return dev_err_probe(dev, ret, "no usb3 phy configured\n");
 		}
 	}
 
@@ -1226,11 +1250,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
 		phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE);
 
 		ret = dwc3_gadget_init(dwc);
-		if (ret) {
-			if (ret != -EPROBE_DEFER)
-				dev_err(dev, "failed to initialize gadget\n");
-			return ret;
-		}
+		if (ret)
+			return dev_err_probe(dev, ret, "failed to initialize gadget\n");
 		break;
 	case USB_DR_MODE_HOST:
 		dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
@@ -1241,20 +1262,14 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
 		phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
 
 		ret = dwc3_host_init(dwc);
-		if (ret) {
-			if (ret != -EPROBE_DEFER)
-				dev_err(dev, "failed to initialize host\n");
-			return ret;
-		}
+		if (ret)
+			return dev_err_probe(dev, ret, "failed to initialize host\n");
 		break;
 	case USB_DR_MODE_OTG:
 		INIT_WORK(&dwc->drd_work, __dwc3_set_mode);
 		ret = dwc3_drd_init(dwc);
-		if (ret) {
-			if (ret != -EPROBE_DEFER)
-				dev_err(dev, "failed to initialize dual-role\n");
-			return ret;
-		}
+		if (ret)
+			return dev_err_probe(dev, ret, "failed to initialize dual-role\n");
 		break;
 	default:
 		dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode);
@@ -1291,10 +1306,13 @@ static void dwc3_get_properties(struct dwc3 *dwc)
 	u8			lpm_nyet_threshold;
 	u8			tx_de_emphasis;
 	u8			hird_threshold;
-	u8			rx_thr_num_pkt_prd = 0;
-	u8			rx_max_burst_prd = 0;
-	u8			tx_thr_num_pkt_prd = 0;
-	u8			tx_max_burst_prd = 0;
+	u8			rx_thr_num_pkt_prd;
+	u8			rx_max_burst_prd;
+	u8			tx_thr_num_pkt_prd;
+	u8			tx_max_burst_prd;
+	u8			tx_fifo_resize_max_num;
+	const char		*usb_psy_name;
+	int			ret;
 
 	/* default to highest possible threshold */
 	lpm_nyet_threshold = 0xf;
@@ -1308,7 +1326,15 @@ static void dwc3_get_properties(struct dwc3 *dwc)
 	 */
 	hird_threshold = 12;
 
+	/*
+	 * default to a TXFIFO size large enough to fit 6 max packets.  This
+	 * allows for systems with larger bus latencies to have some headroom
+	 * for endpoints that have a large bMaxBurst value.
+	 */
+	tx_fifo_resize_max_num = 6;
+
 	dwc->maximum_speed = usb_get_maximum_speed(dev);
+	dwc->max_ssp_rate = usb_get_maximum_ssp_rate(dev);
 	dwc->dr_mode = usb_get_dr_mode(dev);
 	dwc->hsphy_mode = of_usb_get_phy_mode(dev->of_node);
 
@@ -1319,6 +1345,13 @@ static void dwc3_get_properties(struct dwc3 *dwc)
 	else
 		dwc->sysdev = dwc->dev;
 
+	ret = device_property_read_string(dev, "usb-psy-name", &usb_psy_name);
+	if (ret >= 0) {
+		dwc->usb_psy = power_supply_get_by_name(usb_psy_name);
+		if (!dwc->usb_psy)
+			dev_err(dev, "couldn't get usb power supply\n");
+	}
+
 	dwc->has_lpm_erratum = device_property_read_bool(dev,
 				"snps,has-lpm-erratum");
 	device_property_read_u8(dev, "snps,lpm-nyet-threshold",
@@ -1343,6 +1376,11 @@ static void dwc3_get_properties(struct dwc3 *dwc)
 				&tx_thr_num_pkt_prd);
 	device_property_read_u8(dev, "snps,tx-max-burst-prd",
 				&tx_max_burst_prd);
+	dwc->do_fifo_resize = device_property_read_bool(dev,
+							"tx-fifo-resize");
+	if (dwc->do_fifo_resize)
+		device_property_read_u8(dev, "tx-fifo-max-num",
+					&tx_fifo_resize_max_num);
 
 	dwc->disable_scramble_quirk = device_property_read_bool(dev,
 				"snps,disable_scramble_quirk");
@@ -1408,6 +1446,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
 	dwc->tx_max_burst_prd = tx_max_burst_prd;
 
 	dwc->imod_interval = 0;
+
+	dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num;
 }
 
 /* check whether the core supports IMOD */
@@ -1482,21 +1522,59 @@ static void dwc3_check_params(struct dwc3 *dwc)
 		}
 		break;
 	}
+
+	/*
+	 * Currently the controller does not have visibility into the HW
+	 * parameter to determine the maximum number of lanes the HW supports.
+	 * If the number of lanes is not specified in the device property, then
+	 * set the default to support dual-lane for DWC_usb32 and single-lane
+	 * for DWC_usb31 for super-speed-plus.
+	 */
+	if (dwc->maximum_speed == USB_SPEED_SUPER_PLUS) {
+		switch (dwc->max_ssp_rate) {
+		case USB_SSP_GEN_2x1:
+			if (hwparam_gen == DWC3_GHWPARAMS3_SSPHY_IFC_GEN1)
+				dev_warn(dev, "UDC only supports Gen 1\n");
+			break;
+		case USB_SSP_GEN_1x2:
+		case USB_SSP_GEN_2x2:
+			if (DWC3_IP_IS(DWC31))
+				dev_warn(dev, "UDC only supports single lane\n");
+			break;
+		case USB_SSP_GEN_UNKNOWN:
+		default:
+			switch (hwparam_gen) {
+			case DWC3_GHWPARAMS3_SSPHY_IFC_GEN2:
+				if (DWC3_IP_IS(DWC32))
+					dwc->max_ssp_rate = USB_SSP_GEN_2x2;
+				else
+					dwc->max_ssp_rate = USB_SSP_GEN_2x1;
+				break;
+			case DWC3_GHWPARAMS3_SSPHY_IFC_GEN1:
+				if (DWC3_IP_IS(DWC32))
+					dwc->max_ssp_rate = USB_SSP_GEN_1x2;
+				break;
+			}
+			break;
+		}
+	}
 }
 
 static int dwc3_probe(struct platform_device *pdev)
 {
 	struct device		*dev = &pdev->dev;
 	struct resource		*res, dwc_res;
+	struct dwc3_vendor	*vdwc;
 	struct dwc3		*dwc;
 
 	int			ret;
 
 	void __iomem		*regs;
 
-	dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
-	if (!dwc)
+	vdwc = devm_kzalloc(dev, sizeof(*vdwc), GFP_KERNEL);
+	if (!vdwc)
 		return -ENOMEM;
+	dwc = &vdwc->dwc;
 
 	dwc->dev = dev;
 
@@ -1528,7 +1606,7 @@ static int dwc3_probe(struct platform_device *pdev)
 
 	dwc3_get_properties(dwc);
 
-	dwc->reset = devm_reset_control_array_get(dev, true, true);
+	dwc->reset = devm_reset_control_array_get_optional_shared(dev);
 	if (IS_ERR(dwc->reset))
 		return PTR_ERR(dwc->reset);
 
@@ -1594,8 +1672,7 @@ static int dwc3_probe(struct platform_device *pdev)
 
 	ret = dwc3_core_init(dwc);
 	if (ret) {
-		if (ret != -EPROBE_DEFER)
-			dev_err(dev, "failed to initialize core: %d\n", ret);
+		dev_err_probe(dev, ret, "failed to initialize core\n");
 		goto err4;
 	}
 
@@ -1606,24 +1683,36 @@ static int dwc3_probe(struct platform_device *pdev)
 	if (ret)
 		goto err5;
 
-	pm_runtime_put(dev);
-
+#if 0
+	if (dwc->dr_mode == USB_DR_MODE_OTG &&
+	    of_device_is_compatible(dev->parent->of_node,
+				    "rockchip,rk3399-dwc3")) {
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+		pm_runtime_set_autosuspend_delay(dev, 100);
+#endif
+		pm_runtime_allow(dev);
+		pm_runtime_put_sync_suspend(dev);
+	} else {
+		pm_runtime_put(dev);
+	}
+#endif
+    pm_runtime_put(dev);
 	return 0;
 
 err5:
 	dwc3_debugfs_exit(dwc);
 	dwc3_event_buffers_cleanup(dwc);
 
-	usb_phy_set_suspend(dwc->usb2_phy, 1);
-	usb_phy_set_suspend(dwc->usb3_phy, 1);
-	phy_power_off(dwc->usb2_generic_phy);
-	phy_power_off(dwc->usb3_generic_phy);
-
 	usb_phy_shutdown(dwc->usb2_phy);
 	usb_phy_shutdown(dwc->usb3_phy);
 	phy_exit(dwc->usb2_generic_phy);
 	phy_exit(dwc->usb3_generic_phy);
 
+	usb_phy_set_suspend(dwc->usb2_phy, 1);
+	usb_phy_set_suspend(dwc->usb3_phy, 1);
+	phy_power_off(dwc->usb2_generic_phy);
+	phy_power_off(dwc->usb3_generic_phy);
+
 	dwc3_ulpi_exit(dwc);
 
 err4:
@@ -1644,6 +1733,9 @@ static int dwc3_probe(struct platform_device *pdev)
 assert_reset:
 	reset_control_assert(dwc->reset);
 
+	if (dwc->usb_psy)
+		power_supply_put(dwc->usb_psy);
+
 	return ret;
 }
 
@@ -1666,6 +1758,9 @@ static int dwc3_remove(struct platform_device *pdev)
 	dwc3_free_event_buffers(dwc);
 	dwc3_free_scratch_buffers(dwc);
 
+	if (dwc->usb_psy)
+		power_supply_put(dwc->usb_psy);
+
 	return 0;
 }
 
@@ -1850,7 +1945,7 @@ static int dwc3_runtime_suspend(struct device *dev)
 	if (ret)
 		return ret;
 
-	device_init_wakeup(dev, true);
+	device_init_wakeup(dev, false);
 
 	return 0;
 }
@@ -1860,7 +1955,7 @@ static int dwc3_runtime_resume(struct device *dev)
 	struct dwc3     *dwc = dev_get_drvdata(dev);
 	int		ret;
 
-	device_init_wakeup(dev, false);
+	device_init_wakeup(dev, true);
 
 	ret = dwc3_resume_common(dwc, PMSG_AUTO_RESUME);
 	if (ret)
@@ -1909,6 +2004,9 @@ static int dwc3_suspend(struct device *dev)
 	struct dwc3	*dwc = dev_get_drvdata(dev);
 	int		ret;
 
+	if (pm_runtime_suspended(dwc->dev))
+		return 0;
+
 	ret = dwc3_suspend_common(dwc, PMSG_SUSPEND);
 	if (ret)
 		return ret;
@@ -1923,6 +2021,9 @@ static int dwc3_resume(struct device *dev)
 	struct dwc3	*dwc = dev_get_drvdata(dev);
 	int		ret;
 
+	if (pm_runtime_suspended(dwc->dev))
+		return 0;
+
 	pinctrl_pm_select_default_state(dev);
 
 	ret = dwc3_resume_common(dwc, PMSG_RESUME);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index cbebe541f..ab09e6668 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -22,6 +22,7 @@
 #include <linux/debugfs.h>
 #include <linux/wait.h>
 #include <linux/workqueue.h>
+#include <linux/android_kabi.h>
 
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
@@ -31,6 +32,8 @@
 
 #include <linux/phy/phy.h>
 
+#include <linux/power_supply.h>
+
 #define DWC3_MSG_MAX	500
 
 /* Global constants */
@@ -55,7 +58,7 @@
 #define DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE	3
 #define DWC3_DEVICE_EVENT_WAKEUP		4
 #define DWC3_DEVICE_EVENT_HIBER_REQ		5
-#define DWC3_DEVICE_EVENT_EOPF			6
+#define DWC3_DEVICE_EVENT_SUSPEND		6
 #define DWC3_DEVICE_EVENT_SOF			7
 #define DWC3_DEVICE_EVENT_ERRATIC_ERROR		9
 #define DWC3_DEVICE_EVENT_CMD_CMPL		10
@@ -141,6 +144,7 @@
 #define DWC3_GHWPARAMS8		0xc600
 #define DWC3_GUCTL3		0xc60c
 #define DWC3_GFLADJ		0xc630
+#define DWC3_GHWPARAMS9		0xc680
 
 /* Device Registers */
 #define DWC3_DCFG		0xc700
@@ -253,9 +257,11 @@
 #define DWC3_GUCTL_HSTINAUTORETRY	BIT(14)
 
 /* Global User Control 1 Register */
-#define DWC3_GUCTL1_PARKMODE_DISABLE_SS	BIT(17)
+#define DWC3_GUCTL1_DEV_DECOUPLE_L1L2_EVT	BIT(31)
 #define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS	BIT(28)
-#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW	BIT(24)
+#define DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK	BIT(26)
+#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW		BIT(24)
+#define DWC3_GUCTL1_PARKMODE_DISABLE_SS		BIT(17)
 
 /* Global Status Register */
 #define DWC3_GSTS_OTG_IP	BIT(10)
@@ -376,6 +382,9 @@
 #define DWC3_GHWPARAMS7_RAM1_DEPTH(n)	((n) & 0xffff)
 #define DWC3_GHWPARAMS7_RAM2_DEPTH(n)	(((n) >> 16) & 0xffff)
 
+/* Global HWPARAMS9 Register */
+#define DWC3_GHWPARAMS9_DEV_TXF_FLUSH_BYPASS	BIT(0)
+
 /* Global Frame Length Adjustment Register */
 #define DWC3_GFLADJ_30MHZ_SDBND_SEL		BIT(7)
 #define DWC3_GFLADJ_30MHZ_MASK			0x3f
@@ -387,6 +396,8 @@
 #define DWC3_GUCTL3_SPLITDISABLE		BIT(14)
 
 /* Device Configuration Register */
+#define DWC3_DCFG_NUMLANES(n)	(((n) & 0x3) << 30) /* DWC_usb32 only */
+
 #define DWC3_DCFG_DEVADDR(addr)	((addr) << 3)
 #define DWC3_DCFG_DEVADDR_MASK	DWC3_DCFG_DEVADDR(0x7f)
 
@@ -401,6 +412,7 @@
 #define DWC3_DCFG_NUMP(n)	(((n) >> DWC3_DCFG_NUMP_SHIFT) & 0x1f)
 #define DWC3_DCFG_NUMP_MASK	(0x1f << DWC3_DCFG_NUMP_SHIFT)
 #define DWC3_DCFG_LPM_CAP	BIT(22)
+#define DWC3_DCFG_IGNSTRMPP	BIT(23)
 
 /* Device Control Register */
 #define DWC3_DCTL_RUN_STOP	BIT(31)
@@ -452,7 +464,7 @@
 #define DWC3_DEVTEN_CMDCMPLTEN		BIT(10)
 #define DWC3_DEVTEN_ERRTICERREN		BIT(9)
 #define DWC3_DEVTEN_SOFEN		BIT(7)
-#define DWC3_DEVTEN_EOPFEN		BIT(6)
+#define DWC3_DEVTEN_U3L2L1SUSPEN	BIT(6)
 #define DWC3_DEVTEN_HIBERNATIONREQEVTEN	BIT(5)
 #define DWC3_DEVTEN_WKUPEVTEN		BIT(4)
 #define DWC3_DEVTEN_ULSTCNGEN		BIT(3)
@@ -460,6 +472,8 @@
 #define DWC3_DEVTEN_USBRSTEN		BIT(1)
 #define DWC3_DEVTEN_DISCONNEVTEN	BIT(0)
 
+#define DWC3_DSTS_CONNLANES(n)		(((n) >> 30) & 0x3) /* DWC_usb32 only */
+
 /* Device Status Register */
 #define DWC3_DSTS_DCNRD			BIT(29)
 
@@ -650,6 +664,8 @@ struct dwc3_event_buffer {
 	dma_addr_t		dma;
 
 	struct dwc3		*dwc;
+
+	ANDROID_KABI_RESERVE(1);
 };
 
 #define DWC3_EP_FLAG_STALLED	BIT(0)
@@ -713,6 +729,7 @@ struct dwc3_ep {
 #define DWC3_EP_FORCE_RESTART_STREAM	BIT(9)
 #define DWC3_EP_FIRST_STREAM_PRIMED	BIT(10)
 #define DWC3_EP_PENDING_CLEAR_STALL	BIT(11)
+#define DWC3_EP_TXFIFO_RESIZED		BIT(12)
 
 	/* This last one is specific to EP0 */
 #define DWC3_EP0_DIR_IN		BIT(31)
@@ -743,6 +760,9 @@ struct dwc3_ep {
 	/* For isochronous START TRANSFER workaround only */
 	u8			combo_num;
 	int			start_cmd_status;
+
+	ANDROID_KABI_RESERVE(1);
+	ANDROID_KABI_RESERVE(2);
 };
 
 enum dwc3_phy {
@@ -841,6 +861,7 @@ struct dwc3_trb {
  * @hwparams6: GHWPARAMS6
  * @hwparams7: GHWPARAMS7
  * @hwparams8: GHWPARAMS8
+ * @hwparams9: GHWPARAMS9
  */
 struct dwc3_hwparams {
 	u32	hwparams0;
@@ -852,13 +873,15 @@ struct dwc3_hwparams {
 	u32	hwparams6;
 	u32	hwparams7;
 	u32	hwparams8;
+	u32	hwparams9;
+
+	ANDROID_KABI_RESERVE(1);
+	ANDROID_KABI_RESERVE(2);
 };
 
 /* HWPARAMS0 */
 #define DWC3_MODE(n)		((n) & 0x7)
 
-#define DWC3_MDWIDTH(n)		(((n) & 0xff00) >> 8)
-
 /* HWPARAMS1 */
 #define DWC3_NUM_INT(n)		(((n) & (0x3f << 15)) >> 15)
 
@@ -905,11 +928,13 @@ struct dwc3_request {
 	unsigned int		remaining;
 
 	unsigned int		status;
-#define DWC3_REQUEST_STATUS_QUEUED	0
-#define DWC3_REQUEST_STATUS_STARTED	1
-#define DWC3_REQUEST_STATUS_CANCELLED	2
-#define DWC3_REQUEST_STATUS_COMPLETED	3
-#define DWC3_REQUEST_STATUS_UNKNOWN	-1
+#define DWC3_REQUEST_STATUS_QUEUED		0
+#define DWC3_REQUEST_STATUS_STARTED		1
+#define DWC3_REQUEST_STATUS_DISCONNECTED	2
+#define DWC3_REQUEST_STATUS_DEQUEUED		3
+#define DWC3_REQUEST_STATUS_STALLED		4
+#define DWC3_REQUEST_STATUS_COMPLETED		5
+#define DWC3_REQUEST_STATUS_UNKNOWN		-1
 
 	u8			epnum;
 	struct dwc3_trb		*trb;
@@ -920,6 +945,9 @@ struct dwc3_request {
 	unsigned int		needs_extra_trb:1;
 	unsigned int		direction:1;
 	unsigned int		mapped:1;
+
+	ANDROID_KABI_RESERVE(1);
+	ANDROID_KABI_RESERVE(2);
 };
 
 /*
@@ -966,6 +994,10 @@ struct dwc3_scratchpad_array {
  * @nr_scratch: number of scratch buffers
  * @u1u2: only used on revisions <1.83a for workaround
  * @maximum_speed: maximum speed requested (mainly for testing purposes)
+ * @max_ssp_rate: SuperSpeed Plus maximum signaling rate and lane count
+ * @gadget_max_speed: maximum gadget speed requested
+ * @gadget_ssp_rate: Gadget driver's maximum supported SuperSpeed Plus signaling
+ *			rate and lane count.
  * @ip: controller's ID
  * @revision: controller's version of an IP
  * @version_type: VERSIONTYPE register contents, a sub release of a revision
@@ -980,6 +1012,9 @@ struct dwc3_scratchpad_array {
  * @role_sw: usb_role_switch handle
  * @role_switch_default_mode: default operation mode of controller while
  *			usb role is USB_ROLE_NONE.
+ * @current_role_sw_mode: current usb role switch mode.
+ * @desired_role_sw_mode: desired usb role switch mode.
+ * @usb_psy: pointer to power supply interface.
  * @usb2_phy: pointer to USB2 PHY
  * @usb3_phy: pointer to USB3 PHY
  * @usb2_generic_phy: pointer to USB2 PHY
@@ -1008,9 +1043,9 @@ struct dwc3_scratchpad_array {
  * @rx_max_burst_prd: max periodic ESS receive burst size
  * @tx_thr_num_pkt_prd: periodic ESS transmit packet count
  * @tx_max_burst_prd: max periodic ESS transmit burst size
+ * @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize
  * @hsphy_interface: "utmi" or "ulpi"
  * @connected: true when we're connected to a host, false otherwise
- * @softconnect: true when gadget connect is called, false when disconnect runs
  * @delayed_status: true when gadget driver asks for delayed status
  * @ep0_bounced: true when we used bounce buffer
  * @ep0_expect_in: true when we expect a DATA IN transfer
@@ -1023,6 +1058,7 @@ struct dwc3_scratchpad_array {
  *	1	- utmi_l1_suspend_n
  * @is_fpga: true when we are using the FPGA board
  * @pending_events: true when we have pending IRQs to be handled
+ * @do_fifo_resize: true when txfifo resizing is enabled for dwc3 endpoints
  * @pullups_connected: true when Run/Stop bit is set
  * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
  * @three_stage_setup: set if we perform a three phase setup
@@ -1065,6 +1101,11 @@ struct dwc3_scratchpad_array {
  * @dis_split_quirk: set to disable split boundary.
  * @imod_interval: set the interrupt moderation interval in 250ns
  *			increments or 0 to disable.
+ * @max_cfg_eps: current max number of IN eps used across all USB configs.
+ * @last_fifo_depth: last fifo depth used to determine next fifo ram start
+ *		     address.
+ * @num_ep_resized: carries the current number endpoints which have had its tx
+ *		    fifo resized.
  */
 struct dwc3 {
 	struct work_struct	drd_work;
@@ -1123,6 +1164,12 @@ struct dwc3 {
 	enum usb_phy_interface	hsphy_mode;
 	struct usb_role_switch	*role_sw;
 	enum usb_dr_mode	role_switch_default_mode;
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+	u32			current_role_sw_mode;
+	u32			desired_role_sw_mode;
+#endif
+
+	struct power_supply	*usb_psy;
 
 	u32			fladj;
 	u32			irq_gadget;
@@ -1133,6 +1180,9 @@ struct dwc3 {
 	u32			nr_scratch;
 	u32			u1u2;
 	u32			maximum_speed;
+	u32			gadget_max_speed;
+	enum usb_ssp_rate	max_ssp_rate;
+	enum usb_ssp_rate	gadget_ssp_rate;
 
 	u32			ip;
 
@@ -1215,11 +1265,11 @@ struct dwc3 {
 	u8			rx_max_burst_prd;
 	u8			tx_thr_num_pkt_prd;
 	u8			tx_max_burst_prd;
+	u8			tx_fifo_resize_max_num;
 
 	const char		*hsphy_interface;
 
 	unsigned		connected:1;
-	unsigned		softconnect:1;
 	unsigned		delayed_status:1;
 	unsigned		ep0_bounced:1;
 	unsigned		ep0_expect_in:1;
@@ -1229,6 +1279,7 @@ struct dwc3 {
 	unsigned		is_utmi_l1_suspend:1;
 	unsigned		is_fpga:1;
 	unsigned		pending_events:1;
+	unsigned		do_fifo_resize:1;
 	unsigned		pullups_connected:1;
 	unsigned		setup_packet_pending:1;
 	unsigned		three_stage_setup:1;
@@ -1262,8 +1313,28 @@ struct dwc3 {
 	unsigned		dis_metastability_quirk:1;
 
 	unsigned		dis_split_quirk:1;
+	unsigned		async_callbacks:1;
 
 	u16			imod_interval;
+
+	int			max_cfg_eps;
+	int			last_fifo_depth;
+	int			num_ep_resized;
+
+	ANDROID_KABI_RESERVE(1);
+	ANDROID_KABI_RESERVE(2);
+	ANDROID_KABI_RESERVE(3);
+	ANDROID_KABI_RESERVE(4);
+};
+
+/**
+ * struct dwc3_vendor - contains parameters without modifying the format of DWC3 core
+ * @dwc: contains dwc3 core reference
+ * @softconnect: true when gadget connect is called, false when disconnect runs
+ */
+struct dwc3_vendor {
+	struct dwc3	dwc;
+	unsigned	softconnect:1;
 };
 
 #define INCRX_BURST_MODE 0
@@ -1356,7 +1427,7 @@ struct dwc3_event_depevt {
  *	3	- ULStChng
  *	4	- WkUpEvt
  *	5	- Reserved
- *	6	- EOPF
+ *	6	- Suspend (EOPF on revisions 2.10a and prior)
  *	7	- SOF
  *	8	- Reserved
  *	9	- ErrticErr
@@ -1453,13 +1524,28 @@ u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
 	 (!(_ip##_VERSIONTYPE_##_to) ||					\
 	  dwc->version_type <= _ip##_VERSIONTYPE_##_to))
 
+/**
+ * dwc3_mdwidth - get MDWIDTH value in bits
+ * @dwc: pointer to our context structure
+ *
+ * Return MDWIDTH configuration value in bits.
+ */
+static inline u32 dwc3_mdwidth(struct dwc3 *dwc)
+{
+	u32 mdwidth;
+
+	mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0);
+	if (DWC3_IP_IS(DWC32))
+		mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
+
+	return mdwidth;
+}
+
 bool dwc3_has_imod(struct dwc3 *dwc);
 
 int dwc3_event_buffers_setup(struct dwc3 *dwc);
 void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
 
-int dwc3_core_soft_reset(struct dwc3 *dwc);
-
 #if IS_ENABLED(CONFIG_USB_DWC3_HOST) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
 int dwc3_host_init(struct dwc3 *dwc);
 void dwc3_host_exit(struct dwc3 *dwc);
@@ -1480,6 +1566,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
 		struct dwc3_gadget_ep_cmd_params *params);
 int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned int cmd,
 		u32 param);
+void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt);
+void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc);
 #else
 static inline int dwc3_gadget_init(struct dwc3 *dwc)
 { return 0; }
@@ -1499,6 +1587,11 @@ static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
 static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
 		int cmd, u32 param)
 { return 0; }
+static inline void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+					     bool interrupt)
+{ }
+static inline void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
+{ }
 #endif
 
 #if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index 74d9c2c38..b2aa7272f 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -221,8 +221,8 @@ static inline const char *dwc3_gadget_event_string(char *str, size_t size,
 		snprintf(str, size, "WakeUp [%s]",
 				dwc3_gadget_link_string(state));
 		break;
-	case DWC3_DEVICE_EVENT_EOPF:
-		snprintf(str, size, "End-Of-Frame [%s]",
+	case DWC3_DEVICE_EVENT_SUSPEND:
+		snprintf(str, size, "Suspend [%s]",
 				dwc3_gadget_link_string(state));
 		break;
 	case DWC3_DEVICE_EVENT_SOF:
@@ -353,8 +353,8 @@ static inline const char *dwc3_gadget_event_type_string(u8 event)
 		return "Wake-Up";
 	case DWC3_DEVICE_EVENT_HIBER_REQ:
 		return "Hibernation";
-	case DWC3_DEVICE_EVENT_EOPF:
-		return "End of Periodic Frame";
+	case DWC3_DEVICE_EVENT_SUSPEND:
+		return "Suspend";
 	case DWC3_DEVICE_EVENT_SOF:
 		return "Start of Frame";
 	case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 3ebe3e6c2..dc81e8349 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -440,6 +440,10 @@ static ssize_t dwc3_mode_write(struct file *file,
 	if (!strncmp(buf, "otg", 3))
 		mode = DWC3_GCTL_PRTCAP_OTG;
 
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+	dwc->desired_role_sw_mode = mode;
+#endif
+
 	dwc3_set_mode(dwc, mode);
 
 	return count;
@@ -638,16 +642,14 @@ static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused)
 	struct dwc3_ep		*dep = s->private;
 	struct dwc3		*dwc = dep->dwc;
 	unsigned long		flags;
-	int			mdwidth;
+	u32			mdwidth;
 	u32			val;
 
 	spin_lock_irqsave(&dwc->lock, flags);
 	val = dwc3_core_fifo_space(dep, DWC3_TXFIFO);
 
 	/* Convert to bytes */
-	mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
-	if (DWC3_IP_IS(DWC32))
-		mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
+	mdwidth = dwc3_mdwidth(dwc);
 
 	val *= mdwidth;
 	val >>= 3;
@@ -662,16 +664,14 @@ static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused)
 	struct dwc3_ep		*dep = s->private;
 	struct dwc3		*dwc = dep->dwc;
 	unsigned long		flags;
-	int			mdwidth;
+	u32			mdwidth;
 	u32			val;
 
 	spin_lock_irqsave(&dwc->lock, flags);
 	val = dwc3_core_fifo_space(dep, DWC3_RXFIFO);
 
 	/* Convert to bytes */
-	mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
-	if (DWC3_IP_IS(DWC32))
-		mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
+	mdwidth = dwc3_mdwidth(dwc);
 
 	val *= mdwidth;
 	val >>= 3;
diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index 0a96f44cc..347795a8b 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -420,6 +420,12 @@ static void dwc3_drd_update(struct dwc3 *dwc)
 		id = extcon_get_state(dwc->edev, EXTCON_USB_HOST);
 		if (id < 0)
 			id = 0;
+
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+		dwc->desired_role_sw_mode = (id ? USB_DR_MODE_HOST :
+					     USB_DR_MODE_PERIPHERAL);
+#endif
+
 		dwc3_set_mode(dwc, id ?
 			      DWC3_GCTL_PRTCAP_HOST :
 			      DWC3_GCTL_PRTCAP_DEVICE);
@@ -431,6 +437,15 @@ static int dwc3_drd_notifier(struct notifier_block *nb,
 {
 	struct dwc3 *dwc = container_of(nb, struct dwc3, edev_nb);
 
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+	if (extcon_get_state(dwc->edev, EXTCON_USB))
+		dwc->desired_role_sw_mode = USB_DR_MODE_PERIPHERAL;
+	else if (extcon_get_state(dwc->edev, EXTCON_USB_HOST))
+		dwc->desired_role_sw_mode = USB_DR_MODE_HOST;
+	else
+		dwc->desired_role_sw_mode = USB_DR_MODE_UNKNOWN;
+#endif
+
 	dwc3_set_mode(dwc, event ?
 		      DWC3_GCTL_PRTCAP_HOST :
 		      DWC3_GCTL_PRTCAP_DEVICE);
@@ -441,8 +456,8 @@ static int dwc3_drd_notifier(struct notifier_block *nb,
 static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
 {
 	struct device *dev = dwc->dev;
-	struct device_node *np_phy, *np_conn;
-	struct extcon_dev *edev;
+	struct device_node *np_phy;
+	struct extcon_dev *edev = NULL;
 	const char *name;
 
 	if (device_property_read_bool(dev, "extcon"))
@@ -462,15 +477,22 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
 		return edev;
 	}
 
+	/*
+	 * Try to get an extcon device from the USB PHY controller's "port"
+	 * node. Check if it has the "port" node first, to avoid printing the
+	 * error message from underlying code, as it's a valid case: extcon
+	 * device (and "port" node) may be missing in case of "usb-role-switch"
+	 * or OTG mode.
+	 */
 	np_phy = of_parse_phandle(dev->of_node, "phys", 0);
-	np_conn = of_graph_get_remote_node(np_phy, -1, -1);
-
-	if (np_conn)
-		edev = extcon_find_edev_by_node(np_conn);
-	else
-		edev = NULL;
+	if (of_graph_is_present(np_phy)) {
+		struct device_node *np_conn;
 
-	of_node_put(np_conn);
+		np_conn = of_graph_get_remote_node(np_phy, -1, -1);
+		if (np_conn)
+			edev = extcon_find_edev_by_node(np_conn);
+		of_node_put(np_conn);
+	}
 	of_node_put(np_phy);
 
 	return edev;
@@ -484,6 +506,10 @@ static int dwc3_usb_role_switch_set(struct usb_role_switch *sw,
 	struct dwc3 *dwc = usb_role_switch_get_drvdata(sw);
 	u32 mode;
 
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+	dwc->desired_role_sw_mode = role;
+#endif
+
 	switch (role) {
 	case USB_ROLE_HOST:
 		mode = DWC3_GCTL_PRTCAP_HOST;
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 3cd294264..658739410 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -597,11 +597,13 @@ static int dwc3_ep0_set_address(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
 
 static int dwc3_ep0_delegate_req(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
 {
-	int ret;
+	int ret = -EINVAL;
 
-	spin_unlock(&dwc->lock);
-	ret = dwc->gadget_driver->setup(dwc->gadget, ctrl);
-	spin_lock(&dwc->lock);
+	if (dwc->async_callbacks) {
+		spin_unlock(&dwc->lock);
+		ret = dwc->gadget_driver->setup(dwc->gadget, ctrl);
+		spin_lock(&dwc->lock);
+	}
 	return ret;
 }
 
@@ -619,6 +621,8 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
 		return -EINVAL;
 
 	case USB_STATE_ADDRESS:
+		dwc3_gadget_clear_tx_fifos(dwc);
+
 		ret = dwc3_ep0_delegate_req(dwc, ctrl);
 		/* if the cfg matches and the cfg is non zero */
 		if (cfg && (!ret || (ret == USB_GADGET_DELAYED_STATUS))) {
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 28a1194f8..8e9a2622d 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -311,24 +311,13 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
 	if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
 		int link_state;
 
-		/*
-		 * Initiate remote wakeup if the link state is in U3 when
-		 * operating in SS/SSP or L1/L2 when operating in HS/FS. If the
-		 * link state is in U1/U2, no remote wakeup is needed. The Start
-		 * Transfer command will initiate the link recovery.
-		 */
 		link_state = dwc3_gadget_get_link_state(dwc);
-		switch (link_state) {
-		case DWC3_LINK_STATE_U2:
-			if (dwc->gadget->speed >= USB_SPEED_SUPER)
-				break;
-
-			fallthrough;
-		case DWC3_LINK_STATE_U3:
+		if (link_state == DWC3_LINK_STATE_U1 ||
+		    link_state == DWC3_LINK_STATE_U2 ||
+		    link_state == DWC3_LINK_STATE_U3) {
 			ret = __dwc3_gadget_wakeup(dwc);
 			dev_WARN_ONCE(dwc->dev, ret, "wakeup failed --> %d\n",
 					ret);
-			break;
 		}
 	}
 
@@ -417,6 +406,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned int cmd,
 
 	return ret;
 }
+EXPORT_SYMBOL_GPL(dwc3_send_gadget_ep_cmd);
 
 static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep)
 {
@@ -640,8 +630,192 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
 	return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, &params);
 }
 
-static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
-		bool interrupt);
+/**
+ * dwc3_gadget_calc_tx_fifo_size - calculates the txfifo size value
+ * @dwc: pointer to the DWC3 context
+ * @nfifos: number of fifos to calculate for
+ *
+ * Calculates the size value based on the equation below:
+ *
+ * DWC3 revision 280A and prior:
+ * fifo_size = mult * (max_packet / mdwidth) + 1;
+ *
+ * DWC3 revision 290A and onwards:
+ * fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1
+ *
+ * The max packet size is set to 1024, as the txfifo requirements mainly apply
+ * to super speed USB use cases.  However, it is safe to overestimate the fifo
+ * allocations for other scenarios, i.e. high speed USB.
+ */
+static int dwc3_gadget_calc_tx_fifo_size(struct dwc3 *dwc, int mult)
+{
+	int max_packet = 1024;
+	int fifo_size;
+	int mdwidth;
+
+	mdwidth = dwc3_mdwidth(dwc);
+
+	/* MDWIDTH is represented in bits, we need it in bytes */
+	mdwidth >>= 3;
+
+	if (DWC3_VER_IS_PRIOR(DWC3, 290A))
+		fifo_size = mult * (max_packet / mdwidth) + 1;
+	else
+		fifo_size = mult * ((max_packet + mdwidth) / mdwidth) + 1;
+	return fifo_size;
+}
+
+/**
+ * dwc3_gadget_clear_tx_fifo_size - Clears txfifo allocation
+ * @dwc: pointer to the DWC3 context
+ *
+ * Iterates through all the endpoint registers and clears the previous txfifo
+ * allocations.
+ */
+void dwc3_gadget_clear_tx_fifos(struct dwc3 *dwc)
+{
+	struct dwc3_ep *dep;
+	int fifo_depth;
+	int size;
+	int num;
+
+	if (!dwc->do_fifo_resize)
+		return;
+
+	/* Read ep0IN related TXFIFO size */
+	dep = dwc->eps[1];
+	size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
+	if (DWC3_IP_IS(DWC3))
+		fifo_depth = DWC3_GTXFIFOSIZ_TXFDEP(size);
+	else
+		fifo_depth = DWC31_GTXFIFOSIZ_TXFDEP(size);
+
+	dwc->last_fifo_depth = fifo_depth;
+	/* Clear existing TXFIFO for all IN eps except ep0 */
+	for (num = 3; num < min_t(int, dwc->num_eps, DWC3_ENDPOINTS_NUM);
+	     num += 2) {
+		dep = dwc->eps[num];
+		/* Don't change TXFRAMNUM on usb31 version */
+		size = DWC3_IP_IS(DWC3) ? 0 :
+			dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1)) &
+				   DWC31_GTXFIFOSIZ_TXFRAMNUM;
+
+		dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num >> 1), size);
+		dep->flags &= ~DWC3_EP_TXFIFO_RESIZED;
+	}
+	dwc->num_ep_resized = 0;
+}
+
+/*
+ * dwc3_gadget_resize_tx_fifos - reallocate fifo spaces for current use-case
+ * @dwc: pointer to our context structure
+ *
+ * This function will a best effort FIFO allocation in order
+ * to improve FIFO usage and throughput, while still allowing
+ * us to enable as many endpoints as possible.
+ *
+ * Keep in mind that this operation will be highly dependent
+ * on the configured size for RAM1 - which contains TxFifo -,
+ * the amount of endpoints enabled on coreConsultant tool, and
+ * the width of the Master Bus.
+ *
+ * In general, FIFO depths are represented with the following equation:
+ *
+ * fifo_size = mult * ((max_packet + mdwidth)/mdwidth + 1) + 1
+ *
+ * In conjunction with dwc3_gadget_check_config(), this resizing logic will
+ * ensure that all endpoints will have enough internal memory for one max
+ * packet per endpoint.
+ */
+static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
+{
+	struct dwc3 *dwc = dep->dwc;
+	int fifo_0_start;
+	int ram1_depth;
+	int fifo_size;
+	int min_depth;
+	int num_in_ep;
+	int remaining;
+	int num_fifos = 1;
+	int fifo;
+	int tmp;
+
+	if (!dwc->do_fifo_resize)
+		return 0;
+
+	/* resize IN endpoints except ep0 */
+	if (!usb_endpoint_dir_in(dep->endpoint.desc) || dep->number <= 1)
+		return 0;
+
+	/* bail if already resized */
+	if (dep->flags & DWC3_EP_TXFIFO_RESIZED)
+		return 0;
+
+	ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
+
+	if ((dep->endpoint.maxburst > 1 &&
+	     usb_endpoint_xfer_bulk(dep->endpoint.desc)) ||
+	    usb_endpoint_xfer_isoc(dep->endpoint.desc))
+		num_fifos = 3;
+
+	if (dep->endpoint.maxburst > 6 &&
+	    usb_endpoint_xfer_bulk(dep->endpoint.desc) && DWC3_IP_IS(DWC31))
+		num_fifos = dwc->tx_fifo_resize_max_num;
+
+	/* FIFO size for a single buffer */
+	fifo = dwc3_gadget_calc_tx_fifo_size(dwc, 1);
+
+	/* Calculate the number of remaining EPs w/o any FIFO */
+	num_in_ep = dwc->max_cfg_eps;
+	num_in_ep -= dwc->num_ep_resized;
+
+	/* Reserve at least one FIFO for the number of IN EPs */
+	min_depth = num_in_ep * (fifo + 1);
+	remaining = ram1_depth - min_depth - dwc->last_fifo_depth;
+	remaining = max_t(int, 0, remaining);
+	/*
+	 * We've already reserved 1 FIFO per EP, so check what we can fit in
+	 * addition to it.  If there is not enough remaining space, allocate
+	 * all the remaining space to the EP.
+	 */
+	fifo_size = (num_fifos - 1) * fifo;
+	if (remaining < fifo_size)
+		fifo_size = remaining;
+
+	fifo_size += fifo;
+	/* Last increment according to the TX FIFO size equation */
+	fifo_size++;
+
+	/* Check if TXFIFOs start at non-zero addr */
+	tmp = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0));
+	fifo_0_start = DWC3_GTXFIFOSIZ_TXFSTADDR(tmp);
+
+	fifo_size |= (fifo_0_start + (dwc->last_fifo_depth << 16));
+	if (DWC3_IP_IS(DWC3))
+		dwc->last_fifo_depth += DWC3_GTXFIFOSIZ_TXFDEP(fifo_size);
+	else
+		dwc->last_fifo_depth += DWC31_GTXFIFOSIZ_TXFDEP(fifo_size);
+
+	/* Check fifo size allocation doesn't exceed available RAM size. */
+	if (dwc->last_fifo_depth >= ram1_depth) {
+		dev_err(dwc->dev, "Fifosize(%d) > RAM size(%d) %s depth:%d\n",
+			dwc->last_fifo_depth, ram1_depth,
+			dep->endpoint.name, fifo_size);
+		if (DWC3_IP_IS(DWC3))
+			fifo_size = DWC3_GTXFIFOSIZ_TXFDEP(fifo_size);
+		else
+			fifo_size = DWC31_GTXFIFOSIZ_TXFDEP(fifo_size);
+
+		dwc->last_fifo_depth -= fifo_size;
+		return -ENOMEM;
+	}
+
+	dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1), fifo_size);
+	dep->flags |= DWC3_EP_TXFIFO_RESIZED;
+	dwc->num_ep_resized++;
+
+	return 0;
+}
 
 /**
  * __dwc3_gadget_ep_enable - initializes a hw endpoint
@@ -660,6 +834,10 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
 	int			ret;
 
 	if (!(dep->flags & DWC3_EP_ENABLED)) {
+		ret = dwc3_gadget_resize_tx_fifos(dep);
+		if (ret)
+			return ret;
+
 		ret = dwc3_gadget_start_config(dep);
 		if (ret)
 			return ret;
@@ -742,8 +920,16 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
 			 * All stream eps will reinitiate stream on NoStream
 			 * rejection until we can determine that the host can
 			 * prime after the first transfer.
+			 *
+			 * However, if the controller is capable of
+			 * TXF_FLUSH_BYPASS, then IN direction endpoints will
+			 * automatically restart the stream without the driver
+			 * initiation.
 			 */
-			dep->flags |= DWC3_EP_FORCE_RESTART_STREAM;
+			if (!dep->direction ||
+			    !(dwc->hwparams.hwparams9 &
+			      DWC3_GHWPARAMS9_DEV_TXF_FLUSH_BYPASS))
+				dep->flags |= DWC3_EP_FORCE_RESTART_STREAM;
 		}
 	}
 
@@ -808,7 +994,7 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep)
 
 	dep->stream_capable = false;
 	dep->type = 0;
-	dep->flags = 0;
+	dep->flags &= DWC3_EP_TXFIFO_RESIZED;
 
 	/* Clear out the ep descriptors for non-ep0 */
 	if (dep->number > 1) {
@@ -971,49 +1157,17 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
 	return trbs_left;
 }
 
-/**
- * dwc3_prepare_one_trb - setup one TRB from one request
- * @dep: endpoint for which this request is prepared
- * @req: dwc3_request pointer
- * @trb_length: buffer size of the TRB
- * @chain: should this TRB be chained to the next?
- * @node: only for isochronous endpoints. First TRB needs different type.
- * @use_bounce_buffer: set to use bounce buffer
- * @must_interrupt: set to interrupt on TRB completion
- */
-static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
-		struct dwc3_request *req, unsigned int trb_length,
-		unsigned int chain, unsigned int node, bool use_bounce_buffer,
-		bool must_interrupt)
+static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
+		dma_addr_t dma, unsigned int length, unsigned int chain,
+		unsigned int node, unsigned int stream_id,
+		unsigned int short_not_ok, unsigned int no_interrupt,
+		unsigned int is_last, bool must_interrupt)
 {
-	struct dwc3_trb		*trb;
-	dma_addr_t		dma;
-	unsigned int		stream_id = req->request.stream_id;
-	unsigned int		short_not_ok = req->request.short_not_ok;
-	unsigned int		no_interrupt = req->request.no_interrupt;
-	unsigned int		is_last = req->request.is_last;
 	struct dwc3		*dwc = dep->dwc;
 	struct usb_gadget	*gadget = dwc->gadget;
 	enum usb_device_speed	speed = gadget->speed;
 
-	if (use_bounce_buffer)
-		dma = dep->dwc->bounce_addr;
-	else if (req->request.num_sgs > 0)
-		dma = sg_dma_address(req->start_sg);
-	else
-		dma = req->request.dma;
-
-	trb = &dep->trb_pool[dep->trb_enqueue];
-
-	if (!req->trb) {
-		dwc3_gadget_move_started_request(req);
-		req->trb = trb;
-		req->trb_dma = dwc3_trb_dma_offset(dep, trb);
-	}
-
-	req->num_trbs++;
-
-	trb->size = DWC3_TRB_SIZE_LENGTH(trb_length);
+	trb->size = DWC3_TRB_SIZE_LENGTH(length);
 	trb->bpl = lower_32_bits(dma);
 	trb->bph = upper_32_bits(dma);
 
@@ -1053,10 +1207,10 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
 				unsigned int mult = 2;
 				unsigned int maxp = usb_endpoint_maxp(ep->desc);
 
-				if (req->request.length <= (2 * maxp))
+				if (length <= (2 * maxp))
 					mult--;
 
-				if (req->request.length <= maxp)
+				if (length <= maxp)
 					mult--;
 
 				trb->size |= DWC3_TRB_SIZE_PCM1(mult);
@@ -1105,19 +1259,6 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
 	if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
 		trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(stream_id);
 
-	/*
-	 * As per data book 4.2.3.2TRB Control Bit Rules section
-	 *
-	 * The controller autonomously checks the HWO field of a TRB to determine if the
-	 * entire TRB is valid. Therefore, software must ensure that the rest of the TRB
-	 * is valid before setting the HWO field to '1'. In most systems, this means that
-	 * software must update the fourth DWORD of a TRB last.
-	 *
-	 * However there is a possibility of CPU re-ordering here which can cause
-	 * controller to observe the HWO bit set prematurely.
-	 * Add a write memory barrier to prevent CPU re-ordering.
-	 */
-	wmb();
 	trb->ctrl |= DWC3_TRB_CTRL_HWO;
 
 	dwc3_ep_inc_enq(dep);
@@ -1125,6 +1266,50 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
 	trace_dwc3_prepare_trb(dep, trb);
 }
 
+/**
+ * dwc3_prepare_one_trb - setup one TRB from one request
+ * @dep: endpoint for which this request is prepared
+ * @req: dwc3_request pointer
+ * @trb_length: buffer size of the TRB
+ * @chain: should this TRB be chained to the next?
+ * @node: only for isochronous endpoints. First TRB needs different type.
+ * @use_bounce_buffer: set to use bounce buffer
+ * @must_interrupt: set to interrupt on TRB completion
+ */
+static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
+		struct dwc3_request *req, unsigned int trb_length,
+		unsigned int chain, unsigned int node, bool use_bounce_buffer,
+		bool must_interrupt)
+{
+	struct dwc3_trb		*trb;
+	dma_addr_t		dma;
+	unsigned int		stream_id = req->request.stream_id;
+	unsigned int		short_not_ok = req->request.short_not_ok;
+	unsigned int		no_interrupt = req->request.no_interrupt;
+	unsigned int		is_last = req->request.is_last;
+
+	if (use_bounce_buffer)
+		dma = dep->dwc->bounce_addr;
+	else if (req->request.num_sgs > 0)
+		dma = sg_dma_address(req->start_sg);
+	else
+		dma = req->request.dma;
+
+	trb = &dep->trb_pool[dep->trb_enqueue];
+
+	if (!req->trb) {
+		dwc3_gadget_move_started_request(req);
+		req->trb = trb;
+		req->trb_dma = dwc3_trb_dma_offset(dep, trb);
+	}
+
+	req->num_trbs++;
+
+	__dwc3_prepare_one_trb(dep, trb, dma, trb_length, chain, node,
+			stream_id, short_not_ok, no_interrupt, is_last,
+			must_interrupt);
+}
+
 static bool dwc3_needs_extra_trb(struct dwc3_ep *dep, struct dwc3_request *req)
 {
 	unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc);
@@ -1417,7 +1602,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
 		dwc3_stop_active_transfer(dep, true, true);
 
 		list_for_each_entry_safe(req, tmp, &dep->started_list, list)
-			dwc3_gadget_move_cancelled_request(req);
+			dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_DEQUEUED);
 
 		/* If ep isn't started, then there's no end transfer pending */
 		if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
@@ -1633,7 +1818,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
 	struct dwc3		*dwc = dep->dwc;
 
 	if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) {
-		dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
+		dev_dbg(dwc->dev, "%s: can't queue to disabled endpoint\n",
 				dep->name);
 		return -ESHUTDOWN;
 	}
@@ -1746,10 +1931,25 @@ static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep)
 {
 	struct dwc3_request		*req;
 	struct dwc3_request		*tmp;
+	struct dwc3			*dwc = dep->dwc;
 
 	list_for_each_entry_safe(req, tmp, &dep->cancelled_list, list) {
 		dwc3_gadget_ep_skip_trbs(dep, req);
-		dwc3_gadget_giveback(dep, req, -ECONNRESET);
+		switch (req->status) {
+		case DWC3_REQUEST_STATUS_DISCONNECTED:
+			dwc3_gadget_giveback(dep, req, -ESHUTDOWN);
+			break;
+		case DWC3_REQUEST_STATUS_DEQUEUED:
+			dwc3_gadget_giveback(dep, req, -ECONNRESET);
+			break;
+		case DWC3_REQUEST_STATUS_STALLED:
+			dwc3_gadget_giveback(dep, req, -EPIPE);
+			break;
+		default:
+			dev_err(dwc->dev, "request cancelled with wrong reason:%d\n", req->status);
+			dwc3_gadget_giveback(dep, req, -ECONNRESET);
+			break;
+		}
 	}
 }
 
@@ -1793,7 +1993,8 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
 			 * cancelled.
 			 */
 			list_for_each_entry_safe(r, t, &dep->started_list, list)
-				dwc3_gadget_move_cancelled_request(r);
+				dwc3_gadget_move_cancelled_request(r,
+						DWC3_REQUEST_STATUS_DEQUEUED);
 
 			dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE;
 
@@ -1814,8 +2015,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
 {
 	struct dwc3_gadget_ep_cmd_params	params;
 	struct dwc3				*dwc = dep->dwc;
-	struct dwc3_request			*req;
-	struct dwc3_request			*tmp;
 	int					ret;
 
 	if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
@@ -1864,16 +2063,14 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
 
 		dwc3_stop_active_transfer(dep, true, true);
 
-		list_for_each_entry_safe(req, tmp, &dep->started_list, list)
-			dwc3_gadget_move_cancelled_request(req);
+		if (!list_empty(&dep->started_list))
+			dep->flags |= DWC3_EP_DELAY_START;
 
 		if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) {
 			dep->flags |= DWC3_EP_PENDING_CLEAR_STALL;
 			return 0;
 		}
 
-		dwc3_gadget_ep_cleanup_cancelled_requests(dep);
-
 		ret = dwc3_send_clear_stall_ep_cmd(dep);
 		if (ret) {
 			dev_err(dwc->dev, "failed to clear STALL on %s\n",
@@ -2072,6 +2269,102 @@ static void dwc3_stop_active_transfers(struct dwc3 *dwc)
 	}
 }
 
+static void __dwc3_gadget_set_ssp_rate(struct dwc3 *dwc)
+{
+	enum usb_ssp_rate	ssp_rate = dwc->gadget_ssp_rate;
+	u32			reg;
+
+	if (ssp_rate == USB_SSP_GEN_UNKNOWN)
+		ssp_rate = dwc->max_ssp_rate;
+
+	reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+	reg &= ~DWC3_DCFG_SPEED_MASK;
+	reg &= ~DWC3_DCFG_NUMLANES(~0);
+
+	if (ssp_rate == USB_SSP_GEN_1x2)
+		reg |= DWC3_DCFG_SUPERSPEED;
+	else if (dwc->max_ssp_rate != USB_SSP_GEN_1x2)
+		reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+
+	if (ssp_rate != USB_SSP_GEN_2x1 &&
+	    dwc->max_ssp_rate != USB_SSP_GEN_2x1)
+		reg |= DWC3_DCFG_NUMLANES(1);
+
+	dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+}
+
+static void __dwc3_gadget_set_speed(struct dwc3 *dwc)
+{
+	enum usb_device_speed	speed;
+	u32			reg;
+
+	speed = dwc->gadget_max_speed;
+	if (speed == USB_SPEED_UNKNOWN || speed > dwc->maximum_speed)
+		speed = dwc->maximum_speed;
+
+	if (speed == USB_SPEED_SUPER_PLUS &&
+	    DWC3_IP_IS(DWC32)) {
+		__dwc3_gadget_set_ssp_rate(dwc);
+		return;
+	}
+
+	reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+	reg &= ~(DWC3_DCFG_SPEED_MASK);
+
+	/*
+	 * WORKAROUND: DWC3 revision < 2.20a have an issue
+	 * which would cause metastability state on Run/Stop
+	 * bit if we try to force the IP to USB2-only mode.
+	 *
+	 * Because of that, we cannot configure the IP to any
+	 * speed other than the SuperSpeed
+	 *
+	 * Refers to:
+	 *
+	 * STAR#9000525659: Clock Domain Crossing on DCTL in
+	 * USB 2.0 Mode
+	 */
+	if (DWC3_VER_IS_PRIOR(DWC3, 220A) &&
+	    !dwc->dis_metastability_quirk) {
+		reg |= DWC3_DCFG_SUPERSPEED;
+	} else {
+		switch (speed) {
+		case USB_SPEED_LOW:
+			reg |= DWC3_DCFG_LOWSPEED;
+			break;
+		case USB_SPEED_FULL:
+			reg |= DWC3_DCFG_FULLSPEED;
+			break;
+		case USB_SPEED_HIGH:
+			reg |= DWC3_DCFG_HIGHSPEED;
+			break;
+		case USB_SPEED_SUPER:
+			reg |= DWC3_DCFG_SUPERSPEED;
+			break;
+		case USB_SPEED_SUPER_PLUS:
+			if (DWC3_IP_IS(DWC3))
+				reg |= DWC3_DCFG_SUPERSPEED;
+			else
+				reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+			break;
+		default:
+			dev_err(dwc->dev, "invalid speed (%d)\n", speed);
+
+			if (DWC3_IP_IS(DWC3))
+				reg |= DWC3_DCFG_SUPERSPEED;
+			else
+				reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+		}
+	}
+
+	if (DWC3_IP_IS(DWC32) &&
+	    speed > USB_SPEED_UNKNOWN &&
+	    speed < USB_SPEED_SUPER_PLUS)
+		reg &= ~DWC3_DCFG_NUMLANES(~0);
+
+	dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+}
+
 static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
 {
 	u32			reg;
@@ -2094,6 +2387,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
 		if (dwc->has_hibernation)
 			reg |= DWC3_DCTL_KEEP_CONNECT;
 
+		__dwc3_gadget_set_speed(dwc);
 		dwc->pullups_connected = true;
 	} else {
 		reg &= ~DWC3_DCTL_RUN_STOP;
@@ -2121,42 +2415,16 @@ static void dwc3_gadget_disable_irq(struct dwc3 *dwc);
 static void __dwc3_gadget_stop(struct dwc3 *dwc);
 static int __dwc3_gadget_start(struct dwc3 *dwc);
 
-static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&dwc->lock, flags);
-	dwc->connected = false;
-
-	/*
-	 * In the Synopsys DesignWare Cores USB3 Databook Rev. 3.30a
-	 * Section 4.1.8 Table 4-7, it states that for a device-initiated
-	 * disconnect, the SW needs to ensure that it sends "a DEPENDXFER
-	 * command for any active transfers" before clearing the RunStop
-	 * bit.
-	 */
-	dwc3_stop_active_transfers(dwc);
-	__dwc3_gadget_stop(dwc);
-	spin_unlock_irqrestore(&dwc->lock, flags);
-
-	/*
-	 * Note: if the GEVNTCOUNT indicates events in the event buffer, the
-	 * driver needs to acknowledge them before the controller can halt.
-	 * Simply let the interrupt handler acknowledges and handle the
-	 * remaining event generated by the controller while polling for
-	 * DSTS.DEVCTLHLT.
-	 */
-	return dwc3_gadget_run_stop(dwc, false, false);
-}
-
 static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
 {
 	struct dwc3		*dwc = gadget_to_dwc(g);
+	struct dwc3_vendor	*vdwc = container_of(dwc, struct dwc3_vendor, dwc);
+	unsigned long		flags;
 	int			ret;
 
 	is_on = !!is_on;
+	vdwc->softconnect = is_on;
 
-	dwc->softconnect = is_on;
 	/*
 	 * Per databook, when we want to stop the gadget, if a control transfer
 	 * is still in process, complete it and get the core into setup phase.
@@ -2192,27 +2460,50 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
 		return 0;
 	}
 
-	if (dwc->pullups_connected == is_on) {
-		pm_runtime_put(dwc->dev);
-		return 0;
-	}
+	/*
+	 * Synchronize and disable any further event handling while controller
+	 * is being enabled/disabled.
+	 */
+	disable_irq(dwc->irq_gadget);
+
+	spin_lock_irqsave(&dwc->lock, flags);
 
 	if (!is_on) {
-		ret = dwc3_gadget_soft_disconnect(dwc);
-	} else {
+		u32 count;
+
+		dwc->connected = false;
 		/*
-		 * In the Synopsys DWC_usb31 1.90a programming guide section
-		 * 4.1.9, it specifies that for a reconnect after a
-		 * device-initiated disconnect requires a core soft reset
-		 * (DCTL.CSftRst) before enabling the run/stop bit.
+		 * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
+		 * Section 4.1.8 Table 4-7, it states that for a device-initiated
+		 * disconnect, the SW needs to ensure that it sends "a DEPENDXFER
+		 * command for any active transfers" before clearing the RunStop
+		 * bit.
 		 */
-		dwc3_core_soft_reset(dwc);
+		dwc3_stop_active_transfers(dwc);
+		__dwc3_gadget_stop(dwc);
 
-		dwc3_event_buffers_setup(dwc);
+		/*
+		 * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
+		 * Section 1.3.4, it mentions that for the DEVCTRLHLT bit, the
+		 * "software needs to acknowledge the events that are generated
+		 * (by writing to GEVNTCOUNTn) while it is waiting for this bit
+		 * to be set to '1'."
+		 */
+		count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
+		count &= DWC3_GEVNTCOUNT_MASK;
+		if (count > 0) {
+			dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count);
+			dwc->ev_buf->lpos = (dwc->ev_buf->lpos + count) %
+						dwc->ev_buf->length;
+		}
+	} else {
 		__dwc3_gadget_start(dwc);
-		ret = dwc3_gadget_run_stop(dwc, true, false);
 	}
 
+	ret = dwc3_gadget_run_stop(dwc, is_on, false);
+	spin_unlock_irqrestore(&dwc->lock, flags);
+	enable_irq(dwc->irq_gadget);
+
 	pm_runtime_put(dwc->dev);
 
 	return ret;
@@ -2223,8 +2514,7 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
 	u32			reg;
 
 	/* Enable all but Start and End of Frame IRQs */
-	reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
-			DWC3_DEVTEN_EVNTOVERFLOWEN |
+	reg = (DWC3_DEVTEN_EVNTOVERFLOWEN |
 			DWC3_DEVTEN_CMDCMPLTEN |
 			DWC3_DEVTEN_ERRTICERREN |
 			DWC3_DEVTEN_WKUPEVTEN |
@@ -2237,7 +2527,7 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
 
 	/* On 2.30a and above this bit enables U3/L2-L1 Suspend Events */
 	if (!DWC3_VER_IS_PRIOR(DWC3, 230A))
-		reg |= DWC3_DEVTEN_EOPFEN;
+		reg |= DWC3_DEVTEN_U3L2L1SUSPEN;
 
 	dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
 }
@@ -2280,9 +2570,7 @@ static void dwc3_gadget_setup_nump(struct dwc3 *dwc)
 	u32 reg;
 
 	ram2_depth = DWC3_GHWPARAMS7_RAM2_DEPTH(dwc->hwparams.hwparams7);
-	mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0);
-	if (DWC3_IP_IS(DWC32))
-		mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
+	mdwidth = dwc3_mdwidth(dwc);
 
 	nump = ((ram2_depth * mdwidth / 8) - 24 - 16) / 1024;
 	nump = min_t(u32, nump, 16);
@@ -2300,6 +2588,15 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
 	int			ret = 0;
 	u32			reg;
 
+	/*
+	 * If the DWC3 is in runtime suspend, the clocks maybe
+	 * disabled, so avoid enable the DWC3 endpoints here.
+	 * The DWC3 runtime PM resume routine will handle the
+	 * gadget start sequence.
+	 */
+	if (pm_runtime_suspended(dwc->dev))
+		return ret;
+
 	/*
 	 * Use IMOD if enabled via dwc->imod_interval. Otherwise, if
 	 * the core supports IMOD, disable it.
@@ -2328,6 +2625,17 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
 
 	dwc3_gadget_setup_nump(dwc);
 
+	/*
+	 * Currently the controller handles single stream only. So, Ignore
+	 * Packet Pending bit for stream selection and don't search for another
+	 * stream if the host sends Data Packet with PP=0 (for OUT direction) or
+	 * ACK with NumP=0 and PP=0 (for IN direction). This slightly improves
+	 * the stream performance.
+	 */
+	reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+	reg |= DWC3_DCFG_IGNSTRMPP;
+	dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
 	/* Start with SuperSpeed Default */
 	dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
 
@@ -2415,6 +2723,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
 
 	spin_lock_irqsave(&dwc->lock, flags);
 	dwc->gadget_driver	= NULL;
+	dwc->max_cfg_eps = 0;
 	spin_unlock_irqrestore(&dwc->lock, flags);
 
 	free_irq(dwc->irq_gadget, dwc->ev_buf);
@@ -2466,59 +2775,94 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
 {
 	struct dwc3		*dwc = gadget_to_dwc(g);
 	unsigned long		flags;
-	u32			reg;
 
 	spin_lock_irqsave(&dwc->lock, flags);
-	reg = dwc3_readl(dwc->regs, DWC3_DCFG);
-	reg &= ~(DWC3_DCFG_SPEED_MASK);
+	dwc->gadget_max_speed = speed;
+	spin_unlock_irqrestore(&dwc->lock, flags);
+}
 
-	/*
-	 * WORKAROUND: DWC3 revision < 2.20a have an issue
-	 * which would cause metastability state on Run/Stop
-	 * bit if we try to force the IP to USB2-only mode.
-	 *
-	 * Because of that, we cannot configure the IP to any
-	 * speed other than the SuperSpeed
-	 *
-	 * Refers to:
-	 *
-	 * STAR#9000525659: Clock Domain Crossing on DCTL in
-	 * USB 2.0 Mode
-	 */
-	if (DWC3_VER_IS_PRIOR(DWC3, 220A) &&
-	    !dwc->dis_metastability_quirk) {
-		reg |= DWC3_DCFG_SUPERSPEED;
-	} else {
-		switch (speed) {
-		case USB_SPEED_LOW:
-			reg |= DWC3_DCFG_LOWSPEED;
-			break;
-		case USB_SPEED_FULL:
-			reg |= DWC3_DCFG_FULLSPEED;
-			break;
-		case USB_SPEED_HIGH:
-			reg |= DWC3_DCFG_HIGHSPEED;
-			break;
-		case USB_SPEED_SUPER:
-			reg |= DWC3_DCFG_SUPERSPEED;
-			break;
-		case USB_SPEED_SUPER_PLUS:
-			if (DWC3_IP_IS(DWC3))
-				reg |= DWC3_DCFG_SUPERSPEED;
-			else
-				reg |= DWC3_DCFG_SUPERSPEED_PLUS;
-			break;
-		default:
-			dev_err(dwc->dev, "invalid speed (%d)\n", speed);
+static void dwc3_gadget_set_ssp_rate(struct usb_gadget *g,
+				     enum usb_ssp_rate rate)
+{
+	struct dwc3		*dwc = gadget_to_dwc(g);
+	unsigned long		flags;
 
-			if (DWC3_IP_IS(DWC3))
-				reg |= DWC3_DCFG_SUPERSPEED;
-			else
-				reg |= DWC3_DCFG_SUPERSPEED_PLUS;
-		}
+	spin_lock_irqsave(&dwc->lock, flags);
+	dwc->gadget_max_speed = USB_SPEED_SUPER_PLUS;
+	dwc->gadget_ssp_rate = rate;
+	spin_unlock_irqrestore(&dwc->lock, flags);
+}
+
+static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
+{
+	struct dwc3		*dwc = gadget_to_dwc(g);
+	union power_supply_propval	val = {0};
+	int				ret;
+
+	if (dwc->usb2_phy)
+		return usb_phy_set_power(dwc->usb2_phy, mA);
+
+	if (!dwc->usb_psy)
+		return -EOPNOTSUPP;
+
+	val.intval = 1000 * mA;
+	ret = power_supply_set_property(dwc->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val);
+
+	return ret;
+}
+
+/**
+ * dwc3_gadget_check_config - ensure dwc3 can support the USB configuration
+ * @g: pointer to the USB gadget
+ *
+ * Used to record the maximum number of endpoints being used in a USB composite
+ * device. (across all configurations)  This is to be used in the calculation
+ * of the TXFIFO sizes when resizing internal memory for individual endpoints.
+ * It will help ensured that the resizing logic reserves enough space for at
+ * least one max packet.
+ */
+static int dwc3_gadget_check_config(struct usb_gadget *g)
+{
+	struct dwc3 *dwc = gadget_to_dwc(g);
+	struct usb_ep *ep;
+	int fifo_size = 0;
+	int ram1_depth;
+	int ep_num = 0;
+
+	if (!dwc->do_fifo_resize)
+		return 0;
+
+	list_for_each_entry(ep, &g->ep_list, ep_list) {
+		/* Only interested in the IN endpoints */
+		if (ep->claimed && (ep->address & USB_DIR_IN))
+			ep_num++;
 	}
-	dwc3_writel(dwc->regs, DWC3_DCFG, reg);
 
+	if (ep_num <= dwc->max_cfg_eps)
+		return 0;
+
+	/* Update the max number of eps in the composition */
+	dwc->max_cfg_eps = ep_num;
+
+	fifo_size = dwc3_gadget_calc_tx_fifo_size(dwc, dwc->max_cfg_eps);
+	/* Based on the equation, increment by one for every ep */
+	fifo_size += dwc->max_cfg_eps;
+
+	/* Check if we can fit a single fifo per endpoint */
+	ram1_depth = DWC3_RAM1_DEPTH(dwc->hwparams.hwparams7);
+	if (fifo_size > ram1_depth)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void dwc3_gadget_async_callbacks(struct usb_gadget *g, bool enable)
+{
+	struct dwc3		*dwc = gadget_to_dwc(g);
+	unsigned long		flags;
+
+	spin_lock_irqsave(&dwc->lock, flags);
+	dwc->async_callbacks = enable;
 	spin_unlock_irqrestore(&dwc->lock, flags);
 }
 
@@ -2530,7 +2874,11 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
 	.udc_start		= dwc3_gadget_start,
 	.udc_stop		= dwc3_gadget_stop,
 	.udc_set_speed		= dwc3_gadget_set_speed,
+	.udc_set_ssp_rate	= dwc3_gadget_set_ssp_rate,
 	.get_config_params	= dwc3_gadget_config_params,
+	.vbus_draw		= dwc3_gadget_vbus_draw,
+	.check_config		= dwc3_gadget_check_config,
+	.udc_async_callbacks	= dwc3_gadget_async_callbacks,
 };
 
 /* -------------------------------------------------------------------------- */
@@ -2553,12 +2901,10 @@ static int dwc3_gadget_init_control_endpoint(struct dwc3_ep *dep)
 static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
 {
 	struct dwc3 *dwc = dep->dwc;
-	int mdwidth;
+	u32 mdwidth;
 	int size;
 
-	mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
-	if (DWC3_IP_IS(DWC32))
-		mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
+	mdwidth = dwc3_mdwidth(dwc);
 
 	/* MDWIDTH is represented in bits, we need it in bytes */
 	mdwidth /= 8;
@@ -2600,12 +2946,10 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
 static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
 {
 	struct dwc3 *dwc = dep->dwc;
-	int mdwidth;
+	u32 mdwidth;
 	int size;
 
-	mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
-	if (DWC3_IP_IS(DWC32))
-		mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
+	mdwidth = dwc3_mdwidth(dwc);
 
 	/* MDWIDTH is represented in bits, convert to bytes */
 	mdwidth /= 8;
@@ -2857,7 +3201,6 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
 		const struct dwc3_event_depevt *event,
 		struct dwc3_request *req, int status)
 {
-	int request_status;
 	int ret;
 
 	if (req->request.num_mapped_sgs)
@@ -2878,35 +3221,7 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
 		req->needs_extra_trb = false;
 	}
 
-	/*
-	 * The event status only reflects the status of the TRB with IOC set.
-	 * For the requests that don't set interrupt on completion, the driver
-	 * needs to check and return the status of the completed TRBs associated
-	 * with the request. Use the status of the last TRB of the request.
-	 */
-	if (req->request.no_interrupt) {
-		struct dwc3_trb *trb;
-
-		trb = dwc3_ep_prev_trb(dep, dep->trb_dequeue);
-		switch (DWC3_TRB_SIZE_TRBSTS(trb->size)) {
-		case DWC3_TRBSTS_MISSED_ISOC:
-			/* Isoc endpoint only */
-			request_status = -EXDEV;
-			break;
-		case DWC3_TRB_STS_XFER_IN_PROG:
-			/* Applicable when End Transfer with ForceRM=0 */
-		case DWC3_TRBSTS_SETUP_PENDING:
-			/* Control endpoint only */
-		case DWC3_TRBSTS_OK:
-		default:
-			request_status = 0;
-			break;
-		}
-	} else {
-		request_status = status;
-	}
-
-	dwc3_gadget_giveback(dep, req, request_status);
+	dwc3_gadget_giveback(dep, req, status);
 
 out:
 	return ret;
@@ -2931,6 +3246,11 @@ static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep,
 static bool dwc3_gadget_ep_should_continue(struct dwc3_ep *dep)
 {
 	struct dwc3_request	*req;
+	struct dwc3		*dwc = dep->dwc;
+
+	if (!dep->endpoint.desc || !dwc->pullups_connected ||
+	    !dwc->connected)
+		return false;
 
 	if (!list_empty(&dep->pending_list))
 		return true;
@@ -2958,14 +3278,14 @@ static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep,
 	struct dwc3		*dwc = dep->dwc;
 	bool			no_started_trb = true;
 
+	if (!dep->endpoint.desc)
+		return no_started_trb;
+
 	dwc3_gadget_ep_cleanup_completed_requests(dep, event, status);
 
 	if (dep->flags & DWC3_EP_END_TRANSFER_PENDING)
 		goto out;
 
-	if (!dep->endpoint.desc)
-		return no_started_trb;
-
 	if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
 		list_empty(&dep->started_list) &&
 		(list_empty(&dep->pending_list) || status == -EXDEV))
@@ -3064,14 +3384,6 @@ static void dwc3_gadget_endpoint_command_complete(struct dwc3_ep *dep,
 	if (cmd != DWC3_DEPCMD_ENDTRANSFER)
 		return;
 
-	/*
-	 * The END_TRANSFER command will cause the controller to generate a
-	 * NoStream Event, and it's not due to the host DP NoStream rejection.
-	 * Ignore the next NoStream event.
-	 */
-	if (dep->stream_capable)
-		dep->flags |= DWC3_EP_IGNORE_NEXT_NOSTREAM;
-
 	dep->flags &= ~DWC3_EP_END_TRANSFER_PENDING;
 	dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
 	dwc3_gadget_ep_cleanup_cancelled_requests(dep);
@@ -3210,7 +3522,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
 
 static void dwc3_disconnect_gadget(struct dwc3 *dwc)
 {
-	if (dwc->gadget_driver && dwc->gadget_driver->disconnect) {
+	if (dwc->async_callbacks && dwc->gadget_driver->disconnect) {
 		spin_unlock(&dwc->lock);
 		dwc->gadget_driver->disconnect(dwc->gadget);
 		spin_lock(&dwc->lock);
@@ -3219,7 +3531,7 @@ static void dwc3_disconnect_gadget(struct dwc3 *dwc)
 
 static void dwc3_suspend_gadget(struct dwc3 *dwc)
 {
-	if (dwc->gadget_driver && dwc->gadget_driver->suspend) {
+	if (dwc->async_callbacks && dwc->gadget_driver->suspend) {
 		spin_unlock(&dwc->lock);
 		dwc->gadget_driver->suspend(dwc->gadget);
 		spin_lock(&dwc->lock);
@@ -3228,7 +3540,7 @@ static void dwc3_suspend_gadget(struct dwc3 *dwc)
 
 static void dwc3_resume_gadget(struct dwc3 *dwc)
 {
-	if (dwc->gadget_driver && dwc->gadget_driver->resume) {
+	if (dwc->async_callbacks && dwc->gadget_driver->resume) {
 		spin_unlock(&dwc->lock);
 		dwc->gadget_driver->resume(dwc->gadget);
 		spin_lock(&dwc->lock);
@@ -3240,14 +3552,14 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
 	if (!dwc->gadget_driver)
 		return;
 
-	if (dwc->gadget->speed != USB_SPEED_UNKNOWN) {
+	if (dwc->async_callbacks && dwc->gadget->speed != USB_SPEED_UNKNOWN) {
 		spin_unlock(&dwc->lock);
 		usb_gadget_udc_reset(dwc->gadget, dwc->gadget_driver);
 		spin_lock(&dwc->lock);
 	}
 }
 
-static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
 	bool interrupt)
 {
 	struct dwc3_gadget_ep_cmd_params params;
@@ -3294,11 +3606,20 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
 	WARN_ON_ONCE(ret);
 	dep->resource_index = 0;
 
+	/*
+	 * The END_TRANSFER command will cause the controller to generate a
+	 * NoStream Event, and it's not due to the host DP NoStream rejection.
+	 * Ignore the next NoStream event.
+	 */
+	if (dep->stream_capable)
+		dep->flags |= DWC3_EP_IGNORE_NEXT_NOSTREAM;
+
 	if (!interrupt)
 		dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
 	else
 		dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
 }
+EXPORT_SYMBOL_GPL(dwc3_stop_active_transfer);
 
 static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
 {
@@ -3413,12 +3734,18 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
 	struct dwc3_ep		*dep;
 	int			ret;
 	u32			reg;
+	u8			lanes = 1;
 	u8			speed;
 
 	reg = dwc3_readl(dwc->regs, DWC3_DSTS);
 	speed = reg & DWC3_DSTS_CONNECTSPD;
 	dwc->speed = speed;
 
+	if (DWC3_IP_IS(DWC32))
+		lanes = DWC3_DSTS_CONNLANES(reg) + 1;
+
+	dwc->gadget->ssp_rate = USB_SSP_GEN_UNKNOWN;
+
 	/*
 	 * RAMClkSel is reset to 0 after USB reset, so it must be reprogrammed
 	 * each time on Connect Done.
@@ -3433,6 +3760,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
 		dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
 		dwc->gadget->ep0->maxpacket = 512;
 		dwc->gadget->speed = USB_SPEED_SUPER_PLUS;
+
+		if (lanes > 1)
+			dwc->gadget->ssp_rate = USB_SSP_GEN_2x2;
+		else
+			dwc->gadget->ssp_rate = USB_SSP_GEN_2x1;
 		break;
 	case DWC3_DSTS_SUPERSPEED:
 		/*
@@ -3454,6 +3786,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
 		dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
 		dwc->gadget->ep0->maxpacket = 512;
 		dwc->gadget->speed = USB_SPEED_SUPER;
+
+		if (lanes > 1) {
+			dwc->gadget->speed = USB_SPEED_SUPER_PLUS;
+			dwc->gadget->ssp_rate = USB_SSP_GEN_1x2;
+		}
 		break;
 	case DWC3_DSTS_HIGHSPEED:
 		dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
@@ -3545,7 +3882,7 @@ static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
 	 * implemented.
 	 */
 
-	if (dwc->gadget_driver && dwc->gadget_driver->resume) {
+	if (dwc->async_callbacks && dwc->gadget_driver->resume) {
 		spin_unlock(&dwc->lock);
 		dwc->gadget_driver->resume(dwc->gadget);
 		spin_lock(&dwc->lock);
@@ -3690,6 +4027,7 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
 {
 	switch (event->type) {
 	case DWC3_DEVICE_EVENT_DISCONNECT:
+		usb_notify_online_status(false);
 		dwc3_gadget_disconnect_interrupt(dwc);
 		break;
 	case DWC3_DEVICE_EVENT_RESET:
@@ -3697,6 +4035,7 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
 		break;
 	case DWC3_DEVICE_EVENT_CONNECT_DONE:
 		dwc3_gadget_conndone_interrupt(dwc);
+		usb_notify_online_status(true);
 		break;
 	case DWC3_DEVICE_EVENT_WAKEUP:
 		dwc3_gadget_wakeup_interrupt(dwc);
@@ -3711,7 +4050,7 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
 	case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
 		dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
 		break;
-	case DWC3_DEVICE_EVENT_EOPF:
+	case DWC3_DEVICE_EVENT_SUSPEND:
 		/* It changed to be suspend event for version 2.30a and above */
 		if (!DWC3_VER_IS_PRIOR(DWC3, 230A)) {
 			/*
@@ -3779,6 +4118,7 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
 	}
 
 	evt->count = 0;
+	evt->flags &= ~DWC3_EVENT_PENDING;
 	ret = IRQ_HANDLED;
 
 	/* Unmask interrupt */
@@ -3791,9 +4131,6 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
 		dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), dwc->imod_interval);
 	}
 
-	/* Keep the clearing of DWC3_EVENT_PENDING at the end */
-	evt->flags &= ~DWC3_EVENT_PENDING;
-
 	return ret;
 }
 
@@ -3804,11 +4141,9 @@ static irqreturn_t dwc3_thread_interrupt(int irq, void *_evt)
 	unsigned long flags;
 	irqreturn_t ret = IRQ_NONE;
 
-	local_bh_disable();
 	spin_lock_irqsave(&dwc->lock, flags);
 	ret = dwc3_process_event_buf(evt);
 	spin_unlock_irqrestore(&dwc->lock, flags);
-	local_bh_enable();
 
 	return ret;
 }
@@ -3959,6 +4294,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
 	dev->platform_data		= dwc;
 	dwc->gadget->ops		= &dwc3_gadget_ops;
 	dwc->gadget->speed		= USB_SPEED_UNKNOWN;
+	dwc->gadget->ssp_rate		= USB_SSP_GEN_UNKNOWN;
 	dwc->gadget->sg_supported	= true;
 	dwc->gadget->name		= "dwc3-gadget";
 	dwc->gadget->lpm_capable	= !dwc->usb2_gadget_lpm_disable;
@@ -3985,6 +4321,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
 				dwc->revision);
 
 	dwc->gadget->max_speed		= dwc->maximum_speed;
+	dwc->gadget->max_ssp_rate	= dwc->max_ssp_rate;
 
 	/*
 	 * REVISIT: Here we should clear all pending IRQs to be
@@ -4001,7 +4338,10 @@ int dwc3_gadget_init(struct dwc3 *dwc)
 		goto err5;
 	}
 
-	dwc3_gadget_set_speed(dwc->gadget, dwc->maximum_speed);
+	if (DWC3_IP_IS(DWC32) && dwc->maximum_speed == USB_SPEED_SUPER_PLUS)
+		dwc3_gadget_set_ssp_rate(dwc->gadget, dwc->max_ssp_rate);
+	else
+		dwc3_gadget_set_speed(dwc->gadget, dwc->maximum_speed);
 
 	return 0;
 
@@ -4056,9 +4396,10 @@ int dwc3_gadget_suspend(struct dwc3 *dwc)
 
 int dwc3_gadget_resume(struct dwc3 *dwc)
 {
+	struct dwc3_vendor	*vdwc = container_of(dwc, struct dwc3_vendor, dwc);
 	int			ret;
 
-	if (!dwc->gadget_driver || !dwc->softconnect)
+	if (!dwc->gadget_driver || !vdwc->softconnect)
 		return 0;
 
 	ret = __dwc3_gadget_start(dwc);
diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
index 0cd281949..77df4b6d6 100644
--- a/drivers/usb/dwc3/gadget.h
+++ b/drivers/usb/dwc3/gadget.h
@@ -90,15 +90,17 @@ static inline void dwc3_gadget_move_started_request(struct dwc3_request *req)
 /**
  * dwc3_gadget_move_cancelled_request - move @req to the cancelled_list
  * @req: the request to be moved
+ * @reason: cancelled reason for the dwc3 request
  *
  * Caller should take care of locking. This function will move @req from its
  * current list to the endpoint's cancelled_list.
  */
-static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req)
+static inline void dwc3_gadget_move_cancelled_request(struct dwc3_request *req,
+		unsigned int reason)
 {
 	struct dwc3_ep		*dep = req->dep;
 
-	req->status = DWC3_REQUEST_STATUS_CANCELLED;
+	req->status = reason;
 	list_move_tail(&req->list, &dep->cancelled_list);
 }
 
diff --git a/drivers/usb/dwc3/trace.c b/drivers/usb/dwc3/trace.c
index 1b45a9723..8a4fe12bb 100644
--- a/drivers/usb/dwc3/trace.c
+++ b/drivers/usb/dwc3/trace.c
@@ -9,3 +9,10 @@
 
 #define CREATE_TRACE_POINTS
 #include "trace.h"
+
+EXPORT_TRACEPOINT_SYMBOL_GPL(dwc3_ep_queue);
+EXPORT_TRACEPOINT_SYMBOL_GPL(dwc3_readl);
+EXPORT_TRACEPOINT_SYMBOL_GPL(dwc3_writel);
+EXPORT_TRACEPOINT_SYMBOL_GPL(dwc3_event);
+EXPORT_TRACEPOINT_SYMBOL_GPL(dwc3_ctrl_req);
+EXPORT_TRACEPOINT_SYMBOL_GPL(dwc3_complete_trb);
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index a98079990..504c1cbc2 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/device.h>
 #include <linux/utsname.h>
+#include <linux/bitfield.h>
 
 #include <linux/usb/composite.h>
 #include <linux/usb/otg.h>
@@ -734,47 +735,77 @@ static int bos_desc(struct usb_composite_dev *cdev)
 	/* The SuperSpeedPlus USB Device Capability descriptor */
 	if (gadget_is_superspeed_plus(cdev->gadget)) {
 		struct usb_ssp_cap_descriptor *ssp_cap;
+		u8 ssac = 1;
+		u8 ssic;
+		int i;
 
-		ssp_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
-		bos->bNumDeviceCaps++;
+		if (cdev->gadget->max_ssp_rate == USB_SSP_GEN_2x2)
+			ssac = 3;
 
 		/*
-		 * Report typical values.
+		 * Paired RX and TX sublink speed attributes share
+		 * the same SSID.
 		 */
+		ssic = (ssac + 1) / 2 - 1;
+
+		ssp_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
+		bos->bNumDeviceCaps++;
 
-		le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SSP_CAP_SIZE(1));
-		ssp_cap->bLength = USB_DT_USB_SSP_CAP_SIZE(1);
+		le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SSP_CAP_SIZE(ssac));
+		ssp_cap->bLength = USB_DT_USB_SSP_CAP_SIZE(ssac);
 		ssp_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
 		ssp_cap->bDevCapabilityType = USB_SSP_CAP_TYPE;
 		ssp_cap->bReserved = 0;
 		ssp_cap->wReserved = 0;
 
-		/* SSAC = 1 (2 attributes) */
-		ssp_cap->bmAttributes = cpu_to_le32(1);
+		ssp_cap->bmAttributes =
+			cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_ATTRIBS, ssac) |
+				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_IDS, ssic));
 
-		/* Min RX/TX Lane Count = 1 */
 		ssp_cap->wFunctionalitySupport =
-			cpu_to_le16((1 << 8) | (1 << 12));
+			cpu_to_le16(FIELD_PREP(USB_SSP_MIN_SUBLINK_SPEED_ATTRIBUTE_ID, 0) |
+				    FIELD_PREP(USB_SSP_MIN_RX_LANE_COUNT, 1) |
+				    FIELD_PREP(USB_SSP_MIN_TX_LANE_COUNT, 1));
 
 		/*
-		 * bmSublinkSpeedAttr[0]:
-		 *   ST  = Symmetric, RX
-		 *   LSE =  3 (Gbps)
-		 *   LP  =  1 (SuperSpeedPlus)
-		 *   LSM = 10 (10 Gbps)
-		 */
-		ssp_cap->bmSublinkSpeedAttr[0] =
-			cpu_to_le32((3 << 4) | (1 << 14) | (0xa << 16));
-		/*
-		 * bmSublinkSpeedAttr[1] =
-		 *   ST  = Symmetric, TX
-		 *   LSE =  3 (Gbps)
-		 *   LP  =  1 (SuperSpeedPlus)
-		 *   LSM = 10 (10 Gbps)
+		 * Use 1 SSID if the gadget supports up to gen2x1 or not
+		 * specified:
+		 * - SSID 0 for symmetric RX/TX sublink speed of 10 Gbps.
+		 *
+		 * Use 1 SSID if the gadget supports up to gen1x2:
+		 * - SSID 0 for symmetric RX/TX sublink speed of 5 Gbps.
+		 *
+		 * Use 2 SSIDs if the gadget supports up to gen2x2:
+		 * - SSID 0 for symmetric RX/TX sublink speed of 5 Gbps.
+		 * - SSID 1 for symmetric RX/TX sublink speed of 10 Gbps.
 		 */
-		ssp_cap->bmSublinkSpeedAttr[1] =
-			cpu_to_le32((3 << 4) | (1 << 14) |
-				    (0xa << 16) | (1 << 7));
+		for (i = 0; i < ssac + 1; i++) {
+			u8 ssid;
+			u8 mantissa;
+			u8 type;
+
+			ssid = i >> 1;
+
+			if (cdev->gadget->max_ssp_rate == USB_SSP_GEN_2x1 ||
+			    cdev->gadget->max_ssp_rate == USB_SSP_GEN_UNKNOWN)
+				mantissa = 10;
+			else
+				mantissa = 5 << ssid;
+
+			if (i % 2)
+				type = USB_SSP_SUBLINK_SPEED_ST_SYM_TX;
+			else
+				type = USB_SSP_SUBLINK_SPEED_ST_SYM_RX;
+
+			ssp_cap->bmSublinkSpeedAttr[i] =
+				cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_SSID, ssid) |
+					    FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSE,
+						       USB_SSP_SUBLINK_SPEED_LSE_GBPS) |
+					    FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST, type) |
+					    FIELD_PREP(USB_SSP_SUBLINK_SPEED_LP,
+						       USB_SSP_SUBLINK_SPEED_LP_SSP) |
+					    FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSM, mantissa));
+		}
 	}
 
 	return le16_to_cpu(bos->wTotalLength);
@@ -1648,18 +1679,6 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 	struct usb_function		*f = NULL;
 	u8				endp;
 
-	if (w_length > USB_COMP_EP0_BUFSIZ) {
-		if (ctrl->bRequestType & USB_DIR_IN) {
-			/* Cast away the const, we are going to overwrite on purpose. */
-			__le16 *temp = (__le16 *)&ctrl->wLength;
-
-			*temp = cpu_to_le16(USB_COMP_EP0_BUFSIZ);
-			w_length = USB_COMP_EP0_BUFSIZ;
-		} else {
-			goto done;
-		}
-	}
-
 	/* partial re-init of the response message; the function or the
 	 * gadget might need to intercept e.g. a control-OUT completion
 	 * when we delegate to it.
@@ -1944,9 +1963,6 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 				if (w_index != 0x5 || (w_value >> 8))
 					break;
 				interface = w_value & 0xFF;
-				if (interface >= MAX_CONFIG_INTERFACES ||
-				    !os_desc_cfg->interface[interface])
-					break;
 				buf[6] = w_index;
 				count = count_ext_prop(os_desc_cfg,
 					interface);
@@ -2061,7 +2077,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 	return value;
 }
 
-void composite_disconnect(struct usb_gadget *gadget)
+static void __composite_disconnect(struct usb_gadget *gadget)
 {
 	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
 	unsigned long			flags;
@@ -2078,6 +2094,23 @@ void composite_disconnect(struct usb_gadget *gadget)
 	spin_unlock_irqrestore(&cdev->lock, flags);
 }
 
+void composite_disconnect(struct usb_gadget *gadget)
+{
+	usb_gadget_vbus_draw(gadget, 0);
+	__composite_disconnect(gadget);
+}
+
+void composite_reset(struct usb_gadget *gadget)
+{
+	/*
+	 * Section 1.4.13 Standard Downstream Port of the USB battery charging
+	 * specification v1.2 states that a device connected on a SDP shall only
+	 * draw at max 100mA while in a connected, but unconfigured state.
+	 */
+	usb_gadget_vbus_draw(gadget, 100);
+	__composite_disconnect(gadget);
+}
+
 /*-------------------------------------------------------------------------*/
 
 static ssize_t suspended_show(struct device *dev, struct device_attribute *attr,
@@ -2176,7 +2209,7 @@ int composite_dev_prepare(struct usb_composite_driver *composite,
 	if (!cdev->req)
 		return -ENOMEM;
 
-	cdev->req->buf = kzalloc(USB_COMP_EP0_BUFSIZ, GFP_KERNEL);
+	cdev->req->buf = kmalloc(USB_COMP_EP0_BUFSIZ, GFP_KERNEL);
 	if (!cdev->req->buf)
 		goto fail;
 
@@ -2398,7 +2431,7 @@ static const struct usb_gadget_driver composite_driver_template = {
 	.unbind		= composite_unbind,
 
 	.setup		= composite_setup,
-	.reset		= composite_disconnect,
+	.reset		= composite_reset,
 	.disconnect	= composite_disconnect,
 
 	.suspend	= composite_suspend,
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index d51ea1c05..f2a2f1193 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -10,6 +10,32 @@
 #include "u_f.h"
 #include "u_os_desc.h"
 
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+#include <linux/platform_device.h>
+#include <linux/kdev_t.h>
+#include <linux/usb/ch9.h>
+
+#ifdef CONFIG_USB_CONFIGFS_F_ACC
+extern int acc_ctrlrequest(struct usb_composite_dev *cdev,
+				const struct usb_ctrlrequest *ctrl);
+void acc_disconnect(void);
+#endif
+static struct class *android_class;
+static struct device *android_device;
+static int index;
+static int gadget_index;
+
+struct device *create_function_device(char *name)
+{
+	if (android_device && !IS_ERR(android_device))
+		return device_create(android_class, android_device,
+			MKDEV(0, index++), NULL, name);
+	else
+		return ERR_PTR(-EINVAL);
+}
+EXPORT_SYMBOL_GPL(create_function_device);
+#endif
+
 int check_user_usb_string(const char *name,
 		struct usb_gadget_strings *stringtab_dev)
 {
@@ -51,6 +77,12 @@ struct gadget_info {
 	char qw_sign[OS_STRING_QW_SIGN_LEN];
 	spinlock_t spinlock;
 	bool unbind;
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+	bool connected;
+	bool sw_connected;
+	struct work_struct work;
+	struct device *dev;
+#endif
 };
 
 static inline struct gadget_info *to_gadget_info(struct config_item *item)
@@ -272,7 +304,7 @@ static ssize_t gadget_dev_desc_UDC_store(struct config_item *item,
 
 	mutex_lock(&gi->lock);
 
-	if (!strlen(name)) {
+	if (!strlen(name) || strcmp(name, "none") == 0) {
 		ret = unregister_gadget(gi);
 		if (ret)
 			goto err;
@@ -1270,6 +1302,9 @@ static void purge_configs_funcs(struct gadget_info *gi)
 					f->name, f);
 				f->unbind(c, f);
 			}
+
+			if (f->bind_deactivated)
+				usb_function_activate(f);
 		}
 		c->next_interface_id = 0;
 		memset(c->interface, 0, sizeof(c->interface));
@@ -1404,6 +1439,10 @@ static int configfs_composite_bind(struct usb_gadget *gadget,
 				goto err_purge_funcs;
 			}
 		}
+		ret = usb_gadget_check_config(cdev->gadget);
+		if (ret)
+			goto err_purge_funcs;
+
 		usb_ep_autoconfig_reset(cdev->gadget);
 	}
 	if (cdev->use_os_string) {
@@ -1422,6 +1461,57 @@ static int configfs_composite_bind(struct usb_gadget *gadget,
 	return ret;
 }
 
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+static void android_work(struct work_struct *data)
+{
+	struct gadget_info *gi = container_of(data, struct gadget_info, work);
+	struct usb_composite_dev *cdev = &gi->cdev;
+	char *disconnected[2] = { "USB_STATE=DISCONNECTED", NULL };
+	char *connected[2]    = { "USB_STATE=CONNECTED", NULL };
+	char *configured[2]   = { "USB_STATE=CONFIGURED", NULL };
+	/* 0-connected 1-configured 2-disconnected*/
+	bool status[3] = { false, false, false };
+	unsigned long flags;
+	bool uevent_sent = false;
+
+	spin_lock_irqsave(&cdev->lock, flags);
+	if (cdev->config)
+		status[1] = true;
+
+	if (gi->connected != gi->sw_connected) {
+		if (gi->connected)
+			status[0] = true;
+		else
+			status[2] = true;
+		gi->sw_connected = gi->connected;
+	}
+	spin_unlock_irqrestore(&cdev->lock, flags);
+
+	if (status[0]) {
+		kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, connected);
+		pr_info("%s: sent uevent %s\n", __func__, connected[0]);
+		uevent_sent = true;
+	}
+
+	if (status[1]) {
+		kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, configured);
+		pr_info("%s: sent uevent %s\n", __func__, configured[0]);
+		uevent_sent = true;
+	}
+
+	if (status[2]) {
+		kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, disconnected);
+		pr_info("%s: sent uevent %s\n", __func__, disconnected[0]);
+		uevent_sent = true;
+	}
+
+	if (!uevent_sent) {
+		pr_info("%s: did not send uevent (%d %d %p)\n", __func__,
+			gi->connected, gi->sw_connected, cdev->config);
+	}
+}
+#endif
+
 static void configfs_composite_unbind(struct usb_gadget *gadget)
 {
 	struct usb_composite_dev	*cdev;
@@ -1449,6 +1539,60 @@ static void configfs_composite_unbind(struct usb_gadget *gadget)
 	spin_unlock_irqrestore(&gi->spinlock, flags);
 }
 
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+static int android_setup(struct usb_gadget *gadget,
+			const struct usb_ctrlrequest *c)
+{
+	struct usb_composite_dev *cdev;
+	unsigned long flags;
+	struct gadget_info *gi;
+	int value = -EOPNOTSUPP;
+	struct usb_function_instance *fi;
+
+	if (!android_device)
+		return 0;
+
+	gi = dev_get_drvdata(android_device);
+	spin_lock_irqsave(&gi->spinlock, flags);
+	cdev = get_gadget_data(gadget);
+	if (!cdev || gi->unbind) {
+		spin_unlock_irqrestore(&gi->spinlock, flags);
+		return 0;
+	}
+
+	if (c->bRequest == USB_REQ_GET_DESCRIPTOR &&
+	    (c->wValue >> 8) == USB_DT_CONFIG && !gi->connected) {
+		gi->connected = 1;
+		schedule_work(&gi->work);
+	}
+
+	list_for_each_entry(fi, &gi->available_func, cfs_list) {
+		if (fi != NULL && fi->f != NULL && fi->f->setup != NULL) {
+			value = fi->f->setup(fi->f, c);
+			if (value >= 0)
+				break;
+		}
+	}
+
+#ifdef CONFIG_USB_CONFIGFS_F_ACC
+	if (value < 0)
+		value = acc_ctrlrequest(cdev, c);
+#endif
+
+	if (value < 0)
+		value = composite_setup(gadget, c);
+
+	if (c->bRequest == USB_REQ_SET_CONFIGURATION &&
+						cdev->config) {
+		schedule_work(&gi->work);
+	}
+	spin_unlock_irqrestore(&gi->spinlock, flags);
+
+	return value;
+}
+
+#else // CONFIG_USB_CONFIGFS_UEVENT
+
 static int configfs_composite_setup(struct usb_gadget *gadget,
 		const struct usb_ctrlrequest *ctrl)
 {
@@ -1474,6 +1618,8 @@ static int configfs_composite_setup(struct usb_gadget *gadget,
 	return ret;
 }
 
+#endif // CONFIG_USB_CONFIGFS_UEVENT
+
 static void configfs_composite_disconnect(struct usb_gadget *gadget)
 {
 	struct usb_composite_dev *cdev;
@@ -1484,6 +1630,14 @@ static void configfs_composite_disconnect(struct usb_gadget *gadget)
 	if (!cdev)
 		return;
 
+#ifdef CONFIG_USB_CONFIGFS_F_ACC
+	/*
+	 * accessory HID support can be active while the
+	 * accessory function is not actually enabled,
+	 * so we need to inform it when we are disconnected.
+	 */
+	acc_disconnect();
+#endif
 	gi = container_of(cdev, struct gadget_info, cdev);
 	spin_lock_irqsave(&gi->spinlock, flags);
 	cdev = get_gadget_data(gadget);
@@ -1492,10 +1646,36 @@ static void configfs_composite_disconnect(struct usb_gadget *gadget)
 		return;
 	}
 
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+	gi->connected = 0;
+	schedule_work(&gi->work);
+#endif
 	composite_disconnect(gadget);
 	spin_unlock_irqrestore(&gi->spinlock, flags);
 }
 
+static void configfs_composite_reset(struct usb_gadget *gadget)
+{
+	struct usb_composite_dev *cdev;
+	struct gadget_info *gi;
+	unsigned long flags;
+
+	cdev = get_gadget_data(gadget);
+	if (!cdev)
+		return;
+
+	gi = container_of(cdev, struct gadget_info, cdev);
+	spin_lock_irqsave(&gi->spinlock, flags);
+	cdev = get_gadget_data(gadget);
+	if (!cdev || gi->unbind) {
+		spin_unlock_irqrestore(&gi->spinlock, flags);
+		return;
+	}
+
+	composite_reset(gadget);
+	spin_unlock_irqrestore(&gi->spinlock, flags);
+}
+
 static void configfs_composite_suspend(struct usb_gadget *gadget)
 {
 	struct usb_composite_dev *cdev;
@@ -1544,10 +1724,13 @@ static const struct usb_gadget_driver configfs_driver_template = {
 	.bind           = configfs_composite_bind,
 	.unbind         = configfs_composite_unbind,
 
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+	.setup          = android_setup,
+#else
 	.setup          = configfs_composite_setup,
-	.reset          = configfs_composite_disconnect,
+#endif
+	.reset          = configfs_composite_reset,
 	.disconnect     = configfs_composite_disconnect,
-
 	.suspend	= configfs_composite_suspend,
 	.resume		= configfs_composite_resume,
 
@@ -1559,6 +1742,91 @@ static const struct usb_gadget_driver configfs_driver_template = {
 	.match_existing_only = 1,
 };
 
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+static ssize_t state_show(struct device *pdev, struct device_attribute *attr,
+			char *buf)
+{
+	struct gadget_info *dev = dev_get_drvdata(pdev);
+	struct usb_composite_dev *cdev;
+	char *state = "DISCONNECTED";
+	unsigned long flags;
+
+	if (!dev)
+		goto out;
+
+	cdev = &dev->cdev;
+
+	if (!cdev)
+		goto out;
+
+	spin_lock_irqsave(&cdev->lock, flags);
+	if (cdev->config)
+		state = "CONFIGURED";
+	else if (dev->connected)
+		state = "CONNECTED";
+	spin_unlock_irqrestore(&cdev->lock, flags);
+out:
+	return sprintf(buf, "%s\n", state);
+}
+
+static DEVICE_ATTR(state, S_IRUGO, state_show, NULL);
+
+static struct device_attribute *android_usb_attributes[] = {
+	&dev_attr_state,
+	NULL
+};
+
+static int android_device_create(struct gadget_info *gi)
+{
+	struct device_attribute **attrs;
+	struct device_attribute *attr;
+
+	INIT_WORK(&gi->work, android_work);
+	gi->dev = device_create(android_class, NULL,
+			MKDEV(0, 0), NULL, "android%d", gadget_index++);
+	if (IS_ERR(gi->dev))
+		return PTR_ERR(gi->dev);
+
+	dev_set_drvdata(gi->dev, gi);
+	if (!android_device)
+		android_device = gi->dev;
+
+	attrs = android_usb_attributes;
+	while ((attr = *attrs++)) {
+		int err;
+
+		err = device_create_file(gi->dev, attr);
+		if (err) {
+			device_destroy(gi->dev->class,
+				       gi->dev->devt);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static void android_device_destroy(struct gadget_info *gi)
+{
+	struct device_attribute **attrs;
+	struct device_attribute *attr;
+
+	attrs = android_usb_attributes;
+	while ((attr = *attrs++))
+		device_remove_file(gi->dev, attr);
+	device_destroy(gi->dev->class, gi->dev->devt);
+}
+#else
+static inline int android_device_create(struct gadget_info *gi)
+{
+	return 0;
+}
+
+static inline void android_device_destroy(struct gadget_info *gi)
+{
+}
+#endif
+
 static struct config_group *gadgets_make(
 		struct config_group *group,
 		const char *name)
@@ -1611,7 +1879,11 @@ static struct config_group *gadgets_make(
 	if (!gi->composite.gadget_driver.function)
 		goto err;
 
+	if (android_device_create(gi) < 0)
+		goto err;
+
 	return &gi->group;
+
 err:
 	kfree(gi);
 	return ERR_PTR(-ENOMEM);
@@ -1619,7 +1891,11 @@ static struct config_group *gadgets_make(
 
 static void gadgets_drop(struct config_group *group, struct config_item *item)
 {
+	struct gadget_info *gi;
+
+	gi = container_of(to_config_group(item), struct gadget_info, group);
 	config_item_put(item);
+	android_device_destroy(gi);
 }
 
 static struct configfs_group_operations gadgets_ops = {
@@ -1659,6 +1935,13 @@ static int __init gadget_cfs_init(void)
 	config_group_init(&gadget_subsys.su_group);
 
 	ret = configfs_register_subsystem(&gadget_subsys);
+
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+	android_class = class_create(THIS_MODULE, "android_usb");
+	if (IS_ERR(android_class))
+		return PTR_ERR(android_class);
+#endif
+
 	return ret;
 }
 module_init(gadget_cfs_init);
@@ -1666,5 +1949,10 @@ module_init(gadget_cfs_init);
 static void __exit gadget_cfs_exit(void)
 {
 	configfs_unregister_subsystem(&gadget_subsys);
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+	if (!IS_ERR(android_class))
+		class_destroy(android_class);
+#endif
+
 }
 module_exit(gadget_cfs_exit);
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 1eb4fa2e6..177645f32 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -67,6 +67,9 @@ struct usb_ep *usb_ep_autoconfig_ss(
 )
 {
 	struct usb_ep	*ep;
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+	u8 type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+#endif
 
 	if (gadget->ops->match_ep) {
 		ep = gadget->ops->match_ep(gadget, desc, ep_comp);
@@ -110,6 +113,27 @@ struct usb_ep *usb_ep_autoconfig_ss(
 	ep->desc = NULL;
 	ep->comp_desc = NULL;
 	ep->claimed = true;
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+	ep->transfer_type = type;
+	if (gadget_is_superspeed(gadget) && ep_comp) {
+		switch (type) {
+		case USB_ENDPOINT_XFER_ISOC:
+			/* mult: bits 1:0 of bmAttributes */
+			ep->mult = (ep_comp->bmAttributes & 0x3) + 1;
+			fallthrough;
+		case USB_ENDPOINT_XFER_BULK:
+		case USB_ENDPOINT_XFER_INT:
+			ep->maxburst = ep_comp->bMaxBurst + 1;
+			break;
+		default:
+			break;
+		}
+	} else if (gadget_is_dualspeed(gadget) &&
+		   (type == USB_ENDPOINT_XFER_ISOC ||
+		    type == USB_ENDPOINT_XFER_INT)) {
+		ep->mult = usb_endpoint_maxp_mult(desc);
+	}
+#endif
 	return ep;
 }
 EXPORT_SYMBOL_GPL(usb_ep_autoconfig_ss);
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index bb0d92837..01648330a 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -39,7 +39,7 @@
 #include "u_fs.h"
 #include "u_f.h"
 #include "u_os_desc.h"
-#include "configfs.h"
+#include "../configfs.h"
 
 #define FUNCTIONFS_MAGIC	0xa647361 /* Chosen by a honest dice roll ;) */
 
@@ -71,7 +71,7 @@ struct ffs_function {
 	struct ffs_data			*ffs;
 
 	struct ffs_ep			*eps;
-	u8				eps_revmap[16];
+	u8				eps_revmap[32];
 	short				*interfaces_nums;
 
 	struct usb_function		function;
@@ -1710,24 +1710,16 @@ static void ffs_data_put(struct ffs_data *ffs)
 
 static void ffs_data_closed(struct ffs_data *ffs)
 {
-	struct ffs_epfile *epfiles;
-	unsigned long flags;
-
 	ENTER();
 
 	if (atomic_dec_and_test(&ffs->opened)) {
 		if (ffs->no_disconnect) {
 			ffs->state = FFS_DEACTIVATED;
-			spin_lock_irqsave(&ffs->eps_lock, flags);
-			epfiles = ffs->epfiles;
-			ffs->epfiles = NULL;
-			spin_unlock_irqrestore(&ffs->eps_lock,
-							flags);
-
-			if (epfiles)
-				ffs_epfiles_destroy(epfiles,
-						 ffs->eps_count);
-
+			if (ffs->epfiles) {
+				ffs_epfiles_destroy(ffs->epfiles,
+						   ffs->eps_count);
+				ffs->epfiles = NULL;
+			}
 			if (ffs->setup_state == FFS_SETUP_PENDING)
 				__ffs_ep0_stall(ffs);
 		} else {
@@ -1774,34 +1766,17 @@ static struct ffs_data *ffs_data_new(const char *dev_name)
 
 static void ffs_data_clear(struct ffs_data *ffs)
 {
-	struct ffs_epfile *epfiles;
-	unsigned long flags;
-
 	ENTER();
 
 	ffs_closed(ffs);
 
 	BUG_ON(ffs->gadget);
 
-	spin_lock_irqsave(&ffs->eps_lock, flags);
-	epfiles = ffs->epfiles;
-	ffs->epfiles = NULL;
-	spin_unlock_irqrestore(&ffs->eps_lock, flags);
-
-	/*
-	 * potential race possible between ffs_func_eps_disable
-	 * & ffs_epfile_release therefore maintaining a local
-	 * copy of epfile will save us from use-after-free.
-	 */
-	if (epfiles) {
-		ffs_epfiles_destroy(epfiles, ffs->eps_count);
-		ffs->epfiles = NULL;
-	}
+	if (ffs->epfiles)
+		ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count);
 
-	if (ffs->ffs_eventfd) {
+	if (ffs->ffs_eventfd)
 		eventfd_ctx_put(ffs->ffs_eventfd);
-		ffs->ffs_eventfd = NULL;
-	}
 
 	kfree(ffs->raw_descs_data);
 	kfree(ffs->raw_strings);
@@ -1814,6 +1789,7 @@ static void ffs_data_reset(struct ffs_data *ffs)
 
 	ffs_data_clear(ffs);
 
+	ffs->epfiles = NULL;
 	ffs->raw_descs_data = NULL;
 	ffs->raw_descs = NULL;
 	ffs->raw_strings = NULL;
@@ -1942,15 +1918,12 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
 
 static void ffs_func_eps_disable(struct ffs_function *func)
 {
-	struct ffs_ep *ep;
-	struct ffs_epfile *epfile;
-	unsigned short count;
+	struct ffs_ep *ep         = func->eps;
+	struct ffs_epfile *epfile = func->ffs->epfiles;
+	unsigned count            = func->ffs->eps_count;
 	unsigned long flags;
 
 	spin_lock_irqsave(&func->ffs->eps_lock, flags);
-	count = func->ffs->eps_count;
-	epfile = func->ffs->epfiles;
-	ep = func->eps;
 	while (count--) {
 		/* pending requests get nuked */
 		if (likely(ep->ep))
@@ -1968,18 +1941,14 @@ static void ffs_func_eps_disable(struct ffs_function *func)
 
 static int ffs_func_eps_enable(struct ffs_function *func)
 {
-	struct ffs_data *ffs;
-	struct ffs_ep *ep;
-	struct ffs_epfile *epfile;
-	unsigned short count;
+	struct ffs_data *ffs      = func->ffs;
+	struct ffs_ep *ep         = func->eps;
+	struct ffs_epfile *epfile = ffs->epfiles;
+	unsigned count            = ffs->eps_count;
 	unsigned long flags;
 	int ret = 0;
 
 	spin_lock_irqsave(&func->ffs->eps_lock, flags);
-	ffs = func->ffs;
-	ep = func->eps;
-	epfile = ffs->epfiles;
-	count = ffs->eps_count;
 	while(count--) {
 		ep->ep->driver_data = ep;
 
@@ -2827,7 +2796,7 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
 	struct ffs_function *func = priv;
 	struct ffs_ep *ffs_ep;
 	unsigned ep_desc_id;
-	int idx;
+	int idx, ep_num;
 	static const char *speed_names[] = { "full", "high", "super" };
 
 	if (type != FFS_DESCRIPTOR)
@@ -2900,8 +2869,9 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
 
 		ffs_ep->ep  = ep;
 		ffs_ep->req = req;
-		func->eps_revmap[ds->bEndpointAddress &
-				 USB_ENDPOINT_NUMBER_MASK] = idx + 1;
+		ep_num = ((ds->bEndpointAddress & USB_ENDPOINT_DIR_MASK) >> 3) |
+			 (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+		func->eps_revmap[ep_num] = idx + 1;
 		/*
 		 * If we use virtual address mapping, we restore
 		 * original bEndpointAddress value.
@@ -3436,7 +3406,10 @@ static void ffs_func_resume(struct usb_function *f)
 
 static int ffs_func_revmap_ep(struct ffs_function *func, u8 num)
 {
-	num = func->eps_revmap[num & USB_ENDPOINT_NUMBER_MASK];
+	int ep_num = ((num & USB_ENDPOINT_DIR_MASK) >> 3) |
+		     (num & USB_ENDPOINT_NUMBER_MASK);
+
+	num = func->eps_revmap[ep_num];
 	return num ? num : -EDOM;
 }
 
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 950c9435b..86bc2bacc 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -224,7 +224,7 @@
 
 #include <linux/nospec.h>
 
-#include "configfs.h"
+#include "../configfs.h"
 
 
 /*------------------------------------------------------------------------*/
@@ -2301,6 +2301,16 @@ static void fsg_disable(struct usb_function *f)
 {
 	struct fsg_dev *fsg = fsg_from_func(f);
 
+	/* Disable the endpoints */
+	if (fsg->bulk_in_enabled) {
+		usb_ep_disable(fsg->bulk_in);
+		fsg->bulk_in_enabled = 0;
+	}
+	if (fsg->bulk_out_enabled) {
+		usb_ep_disable(fsg->bulk_out);
+		fsg->bulk_out_enabled = 0;
+	}
+
 	__raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE, NULL);
 }
 
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index 5d39aff26..c7dea38d9 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -124,6 +124,18 @@ static struct usb_interface_descriptor uvc_streaming_intf_alt0 = {
 	.iInterface		= 0,
 };
 
+static struct usb_interface_descriptor uvc_bulk_streaming_intf_alt0 = {
+	.bLength		= USB_DT_INTERFACE_SIZE,
+	.bDescriptorType	= USB_DT_INTERFACE,
+	.bInterfaceNumber	= UVC_INTF_VIDEO_STREAMING,
+	.bAlternateSetting	= 0,
+	.bNumEndpoints		= 1,
+	.bInterfaceClass	= USB_CLASS_VIDEO,
+	.bInterfaceSubClass	= UVC_SC_VIDEOSTREAMING,
+	.bInterfaceProtocol	= 0x00,
+	.iInterface		= 0,
+};
+
 static struct usb_interface_descriptor uvc_streaming_intf_alt1 = {
 	.bLength		= USB_DT_INTERFACE_SIZE,
 	.bDescriptorType	= USB_DT_INTERFACE,
@@ -147,6 +159,16 @@ static struct usb_endpoint_descriptor uvc_fs_streaming_ep = {
 	 */
 };
 
+static struct usb_endpoint_descriptor uvc_fs_bulk_streaming_ep = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+	.bEndpointAddress	= USB_DIR_IN,
+	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
+	/* The wMaxPacketSize and bInterval values will be initialized from
+	 * module parameters.
+	 */
+};
+
 static struct usb_endpoint_descriptor uvc_hs_streaming_ep = {
 	.bLength		= USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType	= USB_DT_ENDPOINT,
@@ -158,6 +180,16 @@ static struct usb_endpoint_descriptor uvc_hs_streaming_ep = {
 	 */
 };
 
+static struct usb_endpoint_descriptor uvc_hs_bulk_streaming_ep = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+	.bEndpointAddress	= USB_DIR_IN,
+	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
+	/* The wMaxPacketSize and bInterval values will be initialized from
+	 * module parameters.
+	 */
+};
+
 static struct usb_endpoint_descriptor uvc_ss_streaming_ep = {
 	.bLength		= USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType	= USB_DT_ENDPOINT,
@@ -170,6 +202,17 @@ static struct usb_endpoint_descriptor uvc_ss_streaming_ep = {
 	 */
 };
 
+static struct usb_endpoint_descriptor uvc_ss_bulk_streaming_ep = {
+	.bLength		= USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType	= USB_DT_ENDPOINT,
+
+	.bEndpointAddress	= USB_DIR_IN,
+	.bmAttributes		= USB_ENDPOINT_XFER_BULK,
+	/* The wMaxPacketSize and bInterval values will be initialized from
+	 * module parameters.
+	 */
+};
+
 static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp = {
 	.bLength		= sizeof(uvc_ss_streaming_comp),
 	.bDescriptorType	= USB_DT_SS_ENDPOINT_COMP,
@@ -178,18 +221,36 @@ static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp = {
 	 */
 };
 
+static struct usb_ss_ep_comp_descriptor uvc_ss_bulk_streaming_comp = {
+	.bLength		= sizeof(uvc_ss_bulk_streaming_comp),
+	.bDescriptorType	= USB_DT_SS_ENDPOINT_COMP,
+	/* The bMaxBurst, bmAttributes and wBytesPerInterval values will be
+	 * initialized from module parameters.
+	 */
+};
+
 static const struct usb_descriptor_header * const uvc_fs_streaming[] = {
 	(struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
 	(struct usb_descriptor_header *) &uvc_fs_streaming_ep,
 	NULL,
 };
 
+static const struct usb_descriptor_header * const uvc_fs_bulk_streaming[] = {
+	(struct usb_descriptor_header *)&uvc_fs_bulk_streaming_ep,
+	NULL,
+};
+
 static const struct usb_descriptor_header * const uvc_hs_streaming[] = {
 	(struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
 	(struct usb_descriptor_header *) &uvc_hs_streaming_ep,
 	NULL,
 };
 
+static const struct usb_descriptor_header * const uvc_hs_bulk_streaming[] = {
+	(struct usb_descriptor_header *)&uvc_hs_bulk_streaming_ep,
+	NULL,
+};
+
 static const struct usb_descriptor_header * const uvc_ss_streaming[] = {
 	(struct usb_descriptor_header *) &uvc_streaming_intf_alt1,
 	(struct usb_descriptor_header *) &uvc_ss_streaming_ep,
@@ -197,6 +258,12 @@ static const struct usb_descriptor_header * const uvc_ss_streaming[] = {
 	NULL,
 };
 
+static const struct usb_descriptor_header * const uvc_ss_bulk_streaming[] = {
+	(struct usb_descriptor_header *)&uvc_ss_bulk_streaming_ep,
+	(struct usb_descriptor_header *)&uvc_ss_bulk_streaming_comp,
+	NULL,
+};
+
 /* --------------------------------------------------------------------------
  * Control requests
  */
@@ -208,6 +275,10 @@ uvc_function_ep0_complete(struct usb_ep *ep, struct usb_request *req)
 	struct v4l2_event v4l2_event;
 	struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
 
+	uvc_trace(UVC_TRACE_CONTROL,
+		  "event_setup_out %d, data len %d\n",
+		  uvc->event_setup_out, req->actual);
+
 	if (uvc->event_setup_out) {
 		uvc->event_setup_out = 0;
 
@@ -227,6 +298,11 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
 	struct v4l2_event v4l2_event;
 	struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
 
+	uvc_trace(UVC_TRACE_CONTROL,
+		  "setup request %02x %02x value %04x index %04x %04x\n",
+		  ctrl->bRequestType, ctrl->bRequest, le16_to_cpu(ctrl->wValue),
+		  le16_to_cpu(ctrl->wIndex), le16_to_cpu(ctrl->wLength));
+
 	if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS) {
 		uvcg_info(f, "invalid request type\n");
 		return -EINVAL;
@@ -261,15 +337,27 @@ static int
 uvc_function_get_alt(struct usb_function *f, unsigned interface)
 {
 	struct uvc_device *uvc = to_uvc(f);
+	struct f_uvc_opts *opts;
 
 	uvcg_info(f, "%s(%u)\n", __func__, interface);
 
+	opts = fi_to_f_uvc_opts(f->fi);
+
 	if (interface == uvc->control_intf)
 		return 0;
 	else if (interface != uvc->streaming_intf)
 		return -EINVAL;
-	else
+	else if (!opts->streaming_bulk)
 		return uvc->video.ep->enabled ? 1 : 0;
+	else
+		/*
+		 * Alt settings in an interface are supported only for
+		 * ISOC endpoints as there are different alt-settings for
+		 * zero-bandwidth and full-bandwidth cases, but the same
+		 * is not true for BULK endpoints, as they have a single
+		 * alt-setting.
+		 */
+		return 0;
 }
 
 static int
@@ -279,10 +367,13 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
 	struct usb_composite_dev *cdev = f->config->cdev;
 	struct v4l2_event v4l2_event;
 	struct uvc_event *uvc_event = (void *)&v4l2_event.u.data;
+	struct f_uvc_opts *opts;
 	int ret;
 
 	uvcg_info(f, "%s(%u, %u)\n", __func__, interface, alt);
 
+	opts = fi_to_f_uvc_opts(f->fi);
+
 	if (interface == uvc->control_intf) {
 		if (alt)
 			return -EINVAL;
@@ -296,6 +387,14 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
 
 		usb_ep_enable(uvc->control_ep);
 
+		if (uvc->event_suspend) {
+			memset(&v4l2_event, 0, sizeof(v4l2_event));
+			v4l2_event.type = UVC_EVENT_RESUME;
+			v4l2_event_queue(&uvc->vdev, &v4l2_event);
+			uvc->event_suspend = 0;
+			uvc_trace(UVC_TRACE_SUSPEND, "send UVC_EVENT_RESUME\n");
+		}
+
 		if (uvc->state == UVC_STATE_DISCONNECTED) {
 			memset(&v4l2_event, 0, sizeof(v4l2_event));
 			v4l2_event.type = UVC_EVENT_CONNECT;
@@ -311,49 +410,94 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt)
 	if (interface != uvc->streaming_intf)
 		return -EINVAL;
 
-	/* TODO
-	if (usb_endpoint_xfer_bulk(&uvc->desc.vs_ep))
-		return alt ? -EINVAL : 0;
-	*/
+	if (!opts->streaming_bulk) {
+		switch (alt) {
+		case 0:
+			if (uvc->state != UVC_STATE_STREAMING)
+				return 0;
 
-	switch (alt) {
-	case 0:
-		if (uvc->state != UVC_STATE_STREAMING)
+			if (uvc->video.ep)
+				usb_ep_disable(uvc->video.ep);
+
+			memset(&v4l2_event, 0, sizeof(v4l2_event));
+			v4l2_event.type = UVC_EVENT_STREAMOFF;
+			v4l2_event_queue(&uvc->vdev, &v4l2_event);
+
+			uvc->state = UVC_STATE_CONNECTED;
 			return 0;
 
-		if (uvc->video.ep)
-			usb_ep_disable(uvc->video.ep);
+		case 1:
+			if (uvc->state != UVC_STATE_CONNECTED)
+				return 0;
 
-		memset(&v4l2_event, 0, sizeof(v4l2_event));
-		v4l2_event.type = UVC_EVENT_STREAMOFF;
-		v4l2_event_queue(&uvc->vdev, &v4l2_event);
+			if (!uvc->video.ep)
+				return -EINVAL;
 
-		uvc->state = UVC_STATE_CONNECTED;
-		return 0;
+			INFO(cdev, "reset UVC\n");
+			usb_ep_disable(uvc->video.ep);
 
-	case 1:
-		if (uvc->state != UVC_STATE_CONNECTED)
-			return 0;
+			ret = config_ep_by_speed(f->config->cdev->gadget,
+						 &uvc->func, uvc->video.ep);
+			if (ret)
+				return ret;
+			usb_ep_enable(uvc->video.ep);
+
+			memset(&v4l2_event, 0, sizeof(v4l2_event));
+			v4l2_event.type = UVC_EVENT_STREAMON;
+			v4l2_event_queue(&uvc->vdev, &v4l2_event);
+			return USB_GADGET_DELAYED_STATUS;
 
-		if (!uvc->video.ep)
+		default:
 			return -EINVAL;
+		}
+	} else {
+		switch (uvc->state) {
+		case UVC_STATE_CONNECTED:
+			if (uvc->video.ep &&
+			    !uvc->video.ep->enabled) {
+				/*
+				 * Enable the video streaming endpoint,
+				 * but don't change the 'uvc->state'.
+				 */
+				ret = config_ep_by_speed(cdev->gadget,
+							 &uvc->func,
+							 uvc->video.ep);
+				if (ret)
+					return ret;
+				ret = usb_ep_enable(uvc->video.ep);
+				if (ret)
+					return ret;
+			} else {
+				memset(&v4l2_event, 0, sizeof(v4l2_event));
+				v4l2_event.type = UVC_EVENT_STREAMON;
+				v4l2_event_queue(&uvc->vdev, &v4l2_event);
+
+				uvc->state = UVC_STATE_STREAMING;
+			}
+			return 0;
 
-		uvcg_info(f, "reset UVC\n");
-		usb_ep_disable(uvc->video.ep);
+		case UVC_STATE_STREAMING:
+			if (!alt) {
+				INFO(cdev, "bulk streaming intf not support alt 0\n");
+				return 0;
+			}
 
-		ret = config_ep_by_speed(f->config->cdev->gadget,
-				&(uvc->func), uvc->video.ep);
-		if (ret)
-			return ret;
-		usb_ep_enable(uvc->video.ep);
+			if (uvc->video.ep &&
+			    uvc->video.ep->enabled) {
+				ret = usb_ep_disable(uvc->video.ep);
+				if (ret)
+					return ret;
+			}
 
-		memset(&v4l2_event, 0, sizeof(v4l2_event));
-		v4l2_event.type = UVC_EVENT_STREAMON;
-		v4l2_event_queue(&uvc->vdev, &v4l2_event);
-		return USB_GADGET_DELAYED_STATUS;
+			memset(&v4l2_event, 0, sizeof(v4l2_event));
+			v4l2_event.type = UVC_EVENT_STREAMOFF;
+			v4l2_event_queue(&uvc->vdev, &v4l2_event);
+			uvc->state = UVC_STATE_CONNECTED;
+			return 0;
 
-	default:
-		return -EINVAL;
+		default:
+			return -EINVAL;
+		}
 	}
 }
 
@@ -375,6 +519,30 @@ uvc_function_disable(struct usb_function *f)
 	usb_ep_disable(uvc->control_ep);
 }
 
+static void uvc_function_suspend(struct usb_function *f)
+{
+	struct uvc_device *uvc = to_uvc(f);
+	struct v4l2_event v4l2_event;
+
+	memset(&v4l2_event, 0, sizeof(v4l2_event));
+	v4l2_event.type = UVC_EVENT_SUSPEND;
+	v4l2_event_queue(&uvc->vdev, &v4l2_event);
+	uvc->event_suspend = 1;
+	uvc_trace(UVC_TRACE_SUSPEND, "send UVC_EVENT_SUSPEND\n");
+}
+
+static void uvc_function_resume(struct usb_function *f)
+{
+	struct uvc_device *uvc = to_uvc(f);
+	struct v4l2_event v4l2_event;
+
+	memset(&v4l2_event, 0, sizeof(v4l2_event));
+	v4l2_event.type = UVC_EVENT_RESUME;
+	v4l2_event_queue(&uvc->vdev, &v4l2_event);
+	uvc->event_suspend = 0;
+	uvc_trace(UVC_TRACE_SUSPEND, "send UVC_EVENT_RESUME\n");
+}
+
 /* --------------------------------------------------------------------------
  * Connection / disconnection
  */
@@ -468,32 +636,45 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
 	const struct uvc_descriptor_header * const *uvc_streaming_cls;
 	const struct usb_descriptor_header * const *uvc_streaming_std;
 	const struct usb_descriptor_header * const *src;
+	struct usb_interface_descriptor *streaming_intf_alt0;
 	struct usb_descriptor_header **dst;
 	struct usb_descriptor_header **hdr;
+	struct f_uvc_opts *opts;
 	unsigned int control_size;
 	unsigned int streaming_size;
 	unsigned int n_desc;
 	unsigned int bytes;
 	void *mem;
 
+	opts = fi_to_f_uvc_opts(uvc->func.fi);
+
 	switch (speed) {
 	case USB_SPEED_SUPER:
 		uvc_control_desc = uvc->desc.ss_control;
 		uvc_streaming_cls = uvc->desc.ss_streaming;
-		uvc_streaming_std = uvc_ss_streaming;
+		if (!opts->streaming_bulk)
+			uvc_streaming_std = uvc_ss_streaming;
+		else
+			uvc_streaming_std = uvc_ss_bulk_streaming;
 		break;
 
 	case USB_SPEED_HIGH:
 		uvc_control_desc = uvc->desc.fs_control;
 		uvc_streaming_cls = uvc->desc.hs_streaming;
-		uvc_streaming_std = uvc_hs_streaming;
+		if (!opts->streaming_bulk)
+			uvc_streaming_std = uvc_hs_streaming;
+		else
+			uvc_streaming_std = uvc_hs_bulk_streaming;
 		break;
 
 	case USB_SPEED_FULL:
 	default:
 		uvc_control_desc = uvc->desc.fs_control;
 		uvc_streaming_cls = uvc->desc.fs_streaming;
-		uvc_streaming_std = uvc_fs_streaming;
+		if (!opts->streaming_bulk)
+			uvc_streaming_std = uvc_fs_streaming;
+		else
+			uvc_streaming_std = uvc_fs_bulk_streaming;
 		break;
 	}
 
@@ -513,12 +694,17 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
 	 * uvc_{fs|hs}_streaming
 	 */
 
+	if (!opts->streaming_bulk)
+		streaming_intf_alt0 = &uvc_streaming_intf_alt0;
+	else
+		streaming_intf_alt0 = &uvc_bulk_streaming_intf_alt0;
+
 	/* Count descriptors and compute their size. */
 	control_size = 0;
 	streaming_size = 0;
 	bytes = uvc_iad.bLength + uvc_control_intf.bLength
 	      + uvc_control_ep.bLength + uvc_control_cs_ep.bLength
-	      + uvc_streaming_intf_alt0.bLength;
+	      + streaming_intf_alt0->bLength;
 
 	if (speed == USB_SPEED_SUPER) {
 		bytes += uvc_ss_control_comp.bLength;
@@ -568,7 +754,7 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
 		UVC_COPY_DESCRIPTOR(mem, dst, &uvc_ss_control_comp);
 
 	UVC_COPY_DESCRIPTOR(mem, dst, &uvc_control_cs_ep);
-	UVC_COPY_DESCRIPTOR(mem, dst, &uvc_streaming_intf_alt0);
+	UVC_COPY_DESCRIPTOR(mem, dst, streaming_intf_alt0);
 
 	uvc_streaming_header = mem;
 	UVC_COPY_DESCRIPTORS(mem, dst,
@@ -593,15 +779,24 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
 	struct usb_ep *ep;
 	struct f_uvc_opts *opts;
 	int ret = -EINVAL;
+	u8 address;
 
 	uvcg_info(f, "%s()\n", __func__);
 
 	opts = fi_to_f_uvc_opts(f->fi);
 	/* Sanity check the streaming endpoint module parameters.
 	 */
-	opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U);
-	opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, 1U, 3072U);
-	opts->streaming_maxburst = min(opts->streaming_maxburst, 15U);
+	if (!opts->streaming_bulk) {
+		opts->streaming_interval = clamp(opts->streaming_interval,
+						 1U, 16U);
+		opts->streaming_maxpacket = clamp(opts->streaming_maxpacket,
+						  1U, 3072U);
+		opts->streaming_maxburst = min(opts->streaming_maxburst, 15U);
+	} else {
+		opts->streaming_maxpacket = clamp(opts->streaming_maxpacket,
+						  1U, 1024U);
+		opts->streaming_maxburst = min(opts->streaming_maxburst, 15U);
+	}
 
 	/* For SS, wMaxPacketSize has to be 1024 if bMaxBurst is not 0 */
 	if (opts->streaming_maxburst &&
@@ -628,26 +823,46 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
 		max_packet_size = opts->streaming_maxpacket / 3;
 	}
 
-	uvc_fs_streaming_ep.wMaxPacketSize =
-		cpu_to_le16(min(opts->streaming_maxpacket, 1023U));
-	uvc_fs_streaming_ep.bInterval = opts->streaming_interval;
-
-	uvc_hs_streaming_ep.wMaxPacketSize =
-		cpu_to_le16(max_packet_size | ((max_packet_mult - 1) << 11));
-
-	/* A high-bandwidth endpoint must specify a bInterval value of 1 */
-	if (max_packet_mult > 1)
-		uvc_hs_streaming_ep.bInterval = 1;
-	else
-		uvc_hs_streaming_ep.bInterval = opts->streaming_interval;
-
-	uvc_ss_streaming_ep.wMaxPacketSize = cpu_to_le16(max_packet_size);
-	uvc_ss_streaming_ep.bInterval = opts->streaming_interval;
-	uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1;
-	uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst;
-	uvc_ss_streaming_comp.wBytesPerInterval =
-		cpu_to_le16(max_packet_size * max_packet_mult *
-			    (opts->streaming_maxburst + 1));
+	if (!opts->streaming_bulk) {
+		uvc_fs_streaming_ep.wMaxPacketSize =
+			cpu_to_le16(min(opts->streaming_maxpacket, 1023U));
+		uvc_fs_streaming_ep.bInterval = opts->streaming_interval;
+
+		uvc_hs_streaming_ep.wMaxPacketSize =
+			cpu_to_le16(max_packet_size |
+				    ((max_packet_mult - 1) << 11));
+
+		/* A high-bandwidth endpoint must specify a bInterval value of 1 */
+		if (max_packet_mult > 1)
+			uvc_hs_streaming_ep.bInterval = 1;
+		else
+			uvc_hs_streaming_ep.bInterval = opts->streaming_interval;
+
+		uvc_ss_streaming_ep.wMaxPacketSize =
+			cpu_to_le16(max_packet_size);
+		uvc_ss_streaming_ep.bInterval = opts->streaming_interval;
+		uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1;
+		uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst;
+		uvc_ss_streaming_comp.wBytesPerInterval =
+			cpu_to_le16(max_packet_size * max_packet_mult *
+				    (opts->streaming_maxburst + 1));
+	} else {
+		uvc_fs_bulk_streaming_ep.wMaxPacketSize =
+			cpu_to_le16(min(opts->streaming_maxpacket, 64U));
+
+		uvc_hs_bulk_streaming_ep.wMaxPacketSize =
+			cpu_to_le16(min(opts->streaming_maxpacket, 512U));
+
+		uvc_ss_bulk_streaming_ep.wMaxPacketSize =
+			cpu_to_le16(max_packet_size);
+		uvc_ss_bulk_streaming_comp.bMaxBurst = opts->streaming_maxburst;
+		/*
+		 * As per USB 3.1 spec "Table 9-26. SuperSpeed Endpoint
+		 * Companion Descriptor", the wBytesPerInterval must be
+		 * set to zero for bulk endpoints.
+		 */
+		uvc_ss_bulk_streaming_comp.wBytesPerInterval = 0;
+	}
 
 	/* Allocate endpoints. */
 	ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep);
@@ -657,23 +872,57 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
 	}
 	uvc->control_ep = ep;
 
-	if (gadget_is_superspeed(c->cdev->gadget))
-		ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep,
-					  &uvc_ss_streaming_comp);
-	else if (gadget_is_dualspeed(cdev->gadget))
-		ep = usb_ep_autoconfig(cdev->gadget, &uvc_hs_streaming_ep);
-	else
-		ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep);
+	if (gadget_is_superspeed(c->cdev->gadget)) {
+		if (!opts->streaming_bulk)
+			ep = usb_ep_autoconfig_ss(cdev->gadget,
+						  &uvc_ss_streaming_ep,
+						  &uvc_ss_streaming_comp);
+		else
+			ep = usb_ep_autoconfig_ss(cdev->gadget,
+						  &uvc_ss_bulk_streaming_ep,
+						  &uvc_ss_bulk_streaming_comp);
+	} else if (gadget_is_dualspeed(cdev->gadget)) {
+		if (!opts->streaming_bulk) {
+			ep = usb_ep_autoconfig(cdev->gadget,
+					       &uvc_hs_streaming_ep);
+		} else {
+			ep = usb_ep_autoconfig(cdev->gadget,
+					       &uvc_hs_bulk_streaming_ep);
+			/*
+			 * In ep_matches(), it will set wMaxPacketSize to 64
+			 * bytes if ep is Bulk and ep_comp is NULL for hs/fs
+			 * bulk maxpacket. So we need to set hs bulk maxpacket
+			 * 512 bytes again here.
+			 */
+			uvc_hs_bulk_streaming_ep.wMaxPacketSize =
+				cpu_to_le16(min(opts->streaming_maxpacket,
+						512U));
+		}
+	} else {
+		if (!opts->streaming_bulk)
+			ep = usb_ep_autoconfig(cdev->gadget,
+					       &uvc_fs_streaming_ep);
+		else
+			ep = usb_ep_autoconfig(cdev->gadget,
+					       &uvc_fs_bulk_streaming_ep);
+	}
 
 	if (!ep) {
 		uvcg_info(f, "Unable to allocate streaming EP\n");
 		goto error;
 	}
 	uvc->video.ep = ep;
+	address = uvc->video.ep->address;
 
-	uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
-	uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
-	uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address;
+	if (!opts->streaming_bulk) {
+		uvc_fs_streaming_ep.bEndpointAddress = address;
+		uvc_hs_streaming_ep.bEndpointAddress = address;
+		uvc_ss_streaming_ep.bEndpointAddress = address;
+	} else {
+		uvc_fs_bulk_streaming_ep.bEndpointAddress = address;
+		uvc_hs_bulk_streaming_ep.bEndpointAddress = address;
+		uvc_ss_bulk_streaming_ep.bEndpointAddress = address;
+	}
 
 	us = usb_gstrings_attach(cdev, uvc_function_strings,
 				 ARRAY_SIZE(uvc_en_us_strings));
@@ -684,8 +933,12 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
 	uvc_iad.iFunction = us[UVC_STRING_CONTROL_IDX].id;
 	uvc_control_intf.iInterface = us[UVC_STRING_CONTROL_IDX].id;
 	ret = us[UVC_STRING_STREAMING_IDX].id;
-	uvc_streaming_intf_alt0.iInterface = ret;
-	uvc_streaming_intf_alt1.iInterface = ret;
+	if (!opts->streaming_bulk) {
+		uvc_streaming_intf_alt0.iInterface = ret;
+		uvc_streaming_intf_alt1.iInterface = ret;
+	} else {
+		uvc_bulk_streaming_intf_alt0.iInterface = ret;
+	}
 
 	/* Allocate interface IDs. */
 	if ((ret = usb_interface_id(c, f)) < 0)
@@ -697,8 +950,14 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
 
 	if ((ret = usb_interface_id(c, f)) < 0)
 		goto error;
-	uvc_streaming_intf_alt0.bInterfaceNumber = ret;
-	uvc_streaming_intf_alt1.bInterfaceNumber = ret;
+
+	if (!opts->streaming_bulk) {
+		uvc_streaming_intf_alt0.bInterfaceNumber = ret;
+		uvc_streaming_intf_alt1.bInterfaceNumber = ret;
+	} else {
+		uvc_bulk_streaming_intf_alt0.bInterfaceNumber = ret;
+	}
+
 	uvc->streaming_intf = ret;
 	opts->streaming_interface = ret;
 
@@ -748,6 +1007,8 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
 	if (ret < 0)
 		goto v4l2_error;
 
+	if (opts->streaming_bulk)
+		uvc->video.max_payload_size = uvc->video.imagesize;
 	/* Register a V4L2 device. */
 	ret = uvc_register_video(uvc);
 	if (ret < 0) {
@@ -786,6 +1047,7 @@ static struct usb_function_instance *uvc_alloc_inst(void)
 	struct uvc_camera_terminal_descriptor *cd;
 	struct uvc_processing_unit_descriptor *pd;
 	struct uvc_output_terminal_descriptor *od;
+	struct UVC_EXTENSION_UNIT_DESCRIPTOR(1, 1) *ed;
 	struct uvc_color_matching_descriptor *md;
 	struct uvc_descriptor_header **ctl_cls;
 	int ret;
@@ -835,6 +1097,34 @@ static struct usb_function_instance *uvc_alloc_inst(void)
 	od->bSourceID			= 2;
 	od->iTerminal			= 0;
 
+	ed = &opts->uvc_extension;
+	ed->bLength = UVC_DT_EXTENSION_UNIT_SIZE(1, 1);
+	ed->bDescriptorType = USB_DT_CS_INTERFACE;
+	ed->bDescriptorSubType = UVC_VC_EXTENSION_UNIT;
+	ed->bUnitID = 6;
+	ed->guidExtensionCode[0] = 0xa2;
+	ed->guidExtensionCode[1] = 0x9e;
+	ed->guidExtensionCode[2] = 0x76;
+	ed->guidExtensionCode[3] = 0x41;
+	ed->guidExtensionCode[4] = 0xde;
+	ed->guidExtensionCode[5] = 0x04;
+	ed->guidExtensionCode[6] = 0x47;
+	ed->guidExtensionCode[7] = 0xe3;
+	ed->guidExtensionCode[8] = 0x8b;
+	ed->guidExtensionCode[9] = 0x2b;
+	ed->guidExtensionCode[10] = 0xf4;
+	ed->guidExtensionCode[11] = 0x34;
+	ed->guidExtensionCode[12] = 0x1a;
+	ed->guidExtensionCode[13] = 0xff;
+	ed->guidExtensionCode[14] = 0x00;
+	ed->guidExtensionCode[15] = 0x3b;
+	ed->bNumControls = 3;
+	ed->bNrInPins = 1;
+	ed->baSourceID[0] = 2;
+	ed->bControlSize = 1;
+	ed->bmControls[0] = 7;
+	ed->iExtension = 0;
+
 	md = &opts->uvc_color_matching;
 	md->bLength			= UVC_DT_COLOR_MATCHING_SIZE;
 	md->bDescriptorType		= USB_DT_CS_INTERFACE;
@@ -849,7 +1139,8 @@ static struct usb_function_instance *uvc_alloc_inst(void)
 	ctl_cls[1] = (struct uvc_descriptor_header *)cd;
 	ctl_cls[2] = (struct uvc_descriptor_header *)pd;
 	ctl_cls[3] = (struct uvc_descriptor_header *)od;
-	ctl_cls[4] = NULL;	/* NULL-terminate */
+	ctl_cls[4] = (struct uvc_descriptor_header *)ed;
+	ctl_cls[5] = NULL;	/* NULL-terminate */
 	opts->fs_control =
 		(const struct uvc_descriptor_header * const *)ctl_cls;
 
@@ -859,12 +1150,15 @@ static struct usb_function_instance *uvc_alloc_inst(void)
 	ctl_cls[1] = (struct uvc_descriptor_header *)cd;
 	ctl_cls[2] = (struct uvc_descriptor_header *)pd;
 	ctl_cls[3] = (struct uvc_descriptor_header *)od;
-	ctl_cls[4] = NULL;	/* NULL-terminate */
+	ctl_cls[4] = (struct uvc_descriptor_header *)ed;
+	ctl_cls[5] = NULL;	/* NULL-terminate */
 	opts->ss_control =
 		(const struct uvc_descriptor_header * const *)ctl_cls;
 
 	opts->streaming_interval = 1;
 	opts->streaming_maxpacket = 1024;
+	opts->uvc_num_request = UVC_NUM_REQUESTS;
+	opts->pm_qos_latency = 0;
 
 	ret = uvcg_attach_configfs(opts);
 	if (ret < 0) {
@@ -884,42 +1178,17 @@ static void uvc_free(struct usb_function *f)
 	kfree(uvc);
 }
 
-static void uvc_function_unbind(struct usb_configuration *c,
-				struct usb_function *f)
+static void uvc_unbind(struct usb_configuration *c, struct usb_function *f)
 {
 	struct usb_composite_dev *cdev = c->cdev;
 	struct uvc_device *uvc = to_uvc(f);
-	long wait_ret = 1;
 
-	uvcg_info(f, "%s()\n", __func__);
-
-	/* If we know we're connected via v4l2, then there should be a cleanup
-	 * of the device from userspace either via UVC_EVENT_DISCONNECT or
-	 * though the video device removal uevent. Allow some time for the
-	 * application to close out before things get deleted.
-	 */
-	if (uvc->func_connected) {
-		uvcg_dbg(f, "waiting for clean disconnect\n");
-		wait_ret = wait_event_interruptible_timeout(uvc->func_connected_queue,
-				uvc->func_connected == false, msecs_to_jiffies(500));
-		uvcg_dbg(f, "done waiting with ret: %ld\n", wait_ret);
-	}
+	uvcg_info(f, "%s\n", __func__);
 
 	device_remove_file(&uvc->vdev.dev, &dev_attr_function_name);
 	video_unregister_device(&uvc->vdev);
 	v4l2_device_unregister(&uvc->v4l2_dev);
 
-	if (uvc->func_connected) {
-		/* Wait for the release to occur to ensure there are no longer any
-		 * pending operations that may cause panics when resources are cleaned
-		 * up.
-		 */
-		uvcg_warn(f, "%s no clean disconnect, wait for release\n", __func__);
-		wait_ret = wait_event_interruptible_timeout(uvc->func_connected_queue,
-				uvc->func_connected == false, msecs_to_jiffies(1000));
-		uvcg_dbg(f, "done waiting for release with ret: %ld\n", wait_ret);
-	}
-
 	usb_ep_free_request(cdev->gadget->ep0, uvc->control_req);
 	kfree(uvc->control_buf);
 
@@ -938,7 +1207,6 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
 
 	mutex_init(&uvc->video.mutex);
 	uvc->state = UVC_STATE_DISCONNECTED;
-	init_waitqueue_head(&uvc->func_connected_queue);
 	opts = fi_to_f_uvc_opts(fi);
 
 	mutex_lock(&opts->lock);
@@ -969,12 +1237,14 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
 	/* Register the function. */
 	uvc->func.name = "uvc";
 	uvc->func.bind = uvc_function_bind;
-	uvc->func.unbind = uvc_function_unbind;
+	uvc->func.unbind = uvc_unbind;
 	uvc->func.get_alt = uvc_function_get_alt;
 	uvc->func.set_alt = uvc_function_set_alt;
 	uvc->func.disable = uvc_function_disable;
 	uvc->func.setup = uvc_function_setup;
 	uvc->func.free_func = uvc_free;
+	uvc->func.suspend = uvc_function_suspend;
+	uvc->func.resume = uvc_function_resume;
 	uvc->func.bind_deactivated = true;
 
 	return &uvc->func;
diff --git a/drivers/usb/gadget/function/u_audio.h b/drivers/usb/gadget/function/u_audio.h
index 5ea6b86f1..a218cdf77 100644
--- a/drivers/usb/gadget/function/u_audio.h
+++ b/drivers/usb/gadget/function/u_audio.h
@@ -11,6 +11,14 @@
 
 #include <linux/usb/composite.h>
 
+/*
+ * Same maximum frequency deviation on the slower side as in
+ * sound/usb/endpoint.c. Value is expressed in per-mil deviation.
+ * The maximum deviation on the faster side will be provided as
+ * parameter, as it impacts the endpoint required bandwidth.
+ */
+#define FBACK_SLOW_MAX	250
+
 struct uac_params {
 	/* playback */
 	int p_chmask;	/* channel mask */
@@ -23,6 +31,7 @@ struct uac_params {
 	int c_ssize;	/* sample size */
 
 	int req_number; /* number of preallocated requests */
+	int fb_max;	/* upper frequency drift feedback limit per-mil */
 };
 
 struct g_audio {
@@ -30,7 +39,10 @@ struct g_audio {
 	struct usb_gadget *gadget;
 
 	struct usb_ep *in_ep;
+
 	struct usb_ep *out_ep;
+	/* feedback IN endpoint corresponding to out_ep */
+	struct usb_ep *in_ep_fback;
 
 	/* Max packet size for all in_ep possible speeds */
 	unsigned int in_ep_maxpsize;
diff --git a/drivers/usb/gadget/function/u_ether.h b/drivers/usb/gadget/function/u_ether.h
index 10dd64068..40144546d 100644
--- a/drivers/usb/gadget/function/u_ether.h
+++ b/drivers/usb/gadget/function/u_ether.h
@@ -244,6 +244,18 @@ unsigned gether_get_qmult(struct net_device *net);
  */
 int gether_get_ifname(struct net_device *net, char *name, int len);
 
+/**
+ * gether_set_ifname - set an ethernet-over-usb link interface name
+ * @net: device representing this link
+ * @name: new interface name
+ * @len: length of @name
+ *
+ * This sets the interface name of this ethernet-over-usb link.
+ * A single terminating newline, if any, is ignored.
+ * Returns zero on success, else negative errno.
+ */
+int gether_set_ifname(struct net_device *net, const char *name, int len);
+
 void gether_cleanup(struct eth_dev *dev);
 
 /* connect/disconnect is handled by individual functions */
diff --git a/drivers/usb/gadget/function/u_ether_configfs.h b/drivers/usb/gadget/function/u_ether_configfs.h
index f982e18a5..f558c3139 100644
--- a/drivers/usb/gadget/function/u_ether_configfs.h
+++ b/drivers/usb/gadget/function/u_ether_configfs.h
@@ -148,7 +148,20 @@ out:									\
 		return ret;						\
 	}								\
 									\
-	CONFIGFS_ATTR_RO(_f_##_opts_, ifname)
+	static ssize_t _f_##_opts_ifname_store(struct config_item *item, \
+					       const char *page, size_t len)\
+	{								\
+		struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item);	\
+		int ret = -EBUSY;					\
+									\
+		mutex_lock(&opts->lock);				\
+		if (!opts->refcnt)					\
+			ret = gether_set_ifname(opts->net, page, len);	\
+		mutex_unlock(&opts->lock);				\
+		return ret ?: len;					\
+	}								\
+									\
+	CONFIGFS_ATTR(_f_##_opts_, ifname)
 
 #define USB_ETHER_CONFIGFS_ITEM_ATTR_U8_RW(_f_, _n_)			\
 	static ssize_t _f_##_opts_##_n_##_show(struct config_item *item,\
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index 2caccbb6e..1e59204ec 100644
--- a/drivers/usb/gadget/function/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -258,9 +258,7 @@ __acquires(&port->port_lock)
 		list_del(&req->list);
 		req->zero = kfifo_is_empty(&port->port_write_buf);
 
-		pr_vdebug("ttyGS%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n",
-			  port->port_num, len, *((u8 *)req->buf),
-			  *((u8 *)req->buf+1), *((u8 *)req->buf+2));
+		pr_vdebug("ttyGS%d: tx len=%d, %3ph ...\n", port->port_num, len, req->buf);
 
 		/* Drop lock while we call out of driver; completions
 		 * could be issued while we do so.  Disconnection may
@@ -346,7 +344,7 @@ __acquires(&port->port_lock)
 }
 
 /*
- * RX tasklet takes data out of the RX queue and hands it up to the TTY
+ * RX work takes data out of the RX queue and hands it up to the TTY
  * layer until it refuses to take any more data (or is throttled back).
  * Then it issues reads for any further data.
  *
@@ -709,7 +707,7 @@ static void gs_close(struct tty_struct *tty, struct file *file)
 
 	/* Iff we're disconnected, there can be no I/O in flight so it's
 	 * ok to free the circular buffer; else just scrub it.  And don't
-	 * let the push tasklet fire again until we're re-opened.
+	 * let the push async work fire again until we're re-opened.
 	 */
 	if (gser == NULL)
 		kfifo_free(&port->port_write_buf);
diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h
index b50357111..179d3ef6a 100644
--- a/drivers/usb/gadget/function/u_uac2.h
+++ b/drivers/usb/gadget/function/u_uac2.h
@@ -21,7 +21,9 @@
 #define UAC2_DEF_CCHMASK 0x3
 #define UAC2_DEF_CSRATE 64000
 #define UAC2_DEF_CSSIZE 2
+#define UAC2_DEF_CSYNC		USB_ENDPOINT_SYNC_ASYNC
 #define UAC2_DEF_REQ_NUM 2
+#define UAC2_DEF_FB_MAX 5
 
 struct f_uac2_opts {
 	struct usb_function_instance	func_inst;
@@ -31,7 +33,9 @@ struct f_uac2_opts {
 	int				c_chmask;
 	int				c_srate;
 	int				c_ssize;
+	int				c_sync;
 	int				req_number;
+	int				fb_max;
 	bool				bound;
 
 	struct mutex			lock;
diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h
index 9a01a7d4f..3e92a28d8 100644
--- a/drivers/usb/gadget/function/u_uvc.h
+++ b/drivers/usb/gadget/function/u_uvc.h
@@ -18,15 +18,18 @@
 #include <linux/usb/video.h>
 
 #define fi_to_f_uvc_opts(f)	container_of(f, struct f_uvc_opts, func_inst)
+DECLARE_UVC_EXTENSION_UNIT_DESCRIPTOR(1, 1);
 
 struct f_uvc_opts {
 	struct usb_function_instance			func_inst;
+	bool						streaming_bulk;
 	unsigned int					streaming_interval;
 	unsigned int					streaming_maxpacket;
 	unsigned int					streaming_maxburst;
 
 	unsigned int					control_interface;
 	unsigned int					streaming_interface;
+	unsigned int					uvc_num_request;
 
 	/*
 	 * Control descriptors array pointers for full-/high-speed and
@@ -51,6 +54,7 @@ struct f_uvc_opts {
 	struct uvc_camera_terminal_descriptor		uvc_camera_terminal;
 	struct uvc_processing_unit_descriptor		uvc_processing;
 	struct uvc_output_terminal_descriptor		uvc_output_terminal;
+	struct UVC_EXTENSION_UNIT_DESCRIPTOR(1, 1)	uvc_extension;
 	struct uvc_color_matching_descriptor		uvc_color_matching;
 
 	/*
@@ -60,8 +64,8 @@ struct f_uvc_opts {
 	 * descriptors. Used by configfs only, must not be touched by legacy
 	 * gadgets.
 	 */
-	struct uvc_descriptor_header			*uvc_fs_control_cls[5];
-	struct uvc_descriptor_header			*uvc_ss_control_cls[5];
+	struct uvc_descriptor_header			*uvc_fs_control_cls[6];
+	struct uvc_descriptor_header			*uvc_ss_control_cls[6];
 
 	/*
 	 * Streaming descriptors for full-speed, high-speed and super-speed.
@@ -81,6 +85,7 @@ struct f_uvc_opts {
 	 */
 	struct mutex			lock;
 	int				refcnt;
+	int				pm_qos_latency;
 };
 
 #endif /* U_UVC_H */
diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h
index 6c4fc4913..8fb61023d 100644
--- a/drivers/usb/gadget/function/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
@@ -14,7 +14,7 @@
 #include <linux/spinlock.h>
 #include <linux/usb/composite.h>
 #include <linux/videodev2.h>
-#include <linux/wait.h>
+#include <linux/pm_qos.h>
 
 #include <media/v4l2-device.h>
 #include <media/v4l2-dev.h>
@@ -69,6 +69,7 @@ extern unsigned int uvc_gadget_trace_param;
 #define UVC_NUM_REQUESTS			4
 #define UVC_MAX_REQUEST_SIZE			64
 #define UVC_MAX_EVENTS				4
+#define UVC_MAX_NUM_REQUESTS			8
 
 /* ------------------------------------------------------------------------
  * Structures
@@ -90,8 +91,8 @@ struct uvc_video {
 
 	/* Requests */
 	unsigned int req_size;
-	struct usb_request *req[UVC_NUM_REQUESTS];
-	__u8 *req_buffer[UVC_NUM_REQUESTS];
+	struct usb_request *req[UVC_MAX_NUM_REQUESTS];
+	__u8 *req_buffer[UVC_MAX_NUM_REQUESTS];
 	struct list_head req_free;
 	spinlock_t req_lock;
 
@@ -118,8 +119,8 @@ struct uvc_device {
 	enum uvc_state state;
 	struct usb_function func;
 	struct uvc_video video;
-	bool func_connected;
-	wait_queue_head_t func_connected_queue;
+	/* for creating and issuing QoS requests */
+	struct pm_qos_request pm_qos;
 
 	/* Descriptors */
 	struct {
@@ -140,6 +141,7 @@ struct uvc_device {
 	/* Events */
 	unsigned int event_length;
 	unsigned int event_setup_out : 1;
+	unsigned int event_suspend : 1;
 };
 
 static inline struct uvc_device *to_uvc(struct usb_function *f)
@@ -150,7 +152,6 @@ static inline struct uvc_device *to_uvc(struct usb_function *f)
 struct uvc_file_handle {
 	struct v4l2_fh vfh;
 	struct uvc_video *device;
-	bool is_uvc_app_handle;
 };
 
 #define to_uvc_file_handle(handle) \
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
index 00fb58e50..62c20c0bc 100644
--- a/drivers/usb/gadget/function/uvc_configfs.c
+++ b/drivers/usb/gadget/function/uvc_configfs.c
@@ -12,6 +12,7 @@
 
 #include <linux/sort.h>
 
+#include "uvc.h"
 #include "u_uvc.h"
 #include "uvc_configfs.h"
 
@@ -767,11 +768,13 @@ static const struct uvcg_config_group_type uvcg_control_grp_type = {
 static const char * const uvcg_format_names[] = {
 	"uncompressed",
 	"mjpeg",
+	"framebased",
 };
 
 enum uvcg_format_type {
 	UVCG_UNCOMPRESSED = 0,
 	UVCG_MJPEG,
+	UVCG_FRAMEBASED,
 };
 
 struct uvcg_format {
@@ -1077,9 +1080,15 @@ struct uvcg_frame {
 		u16	w_height;
 		u32	dw_min_bit_rate;
 		u32	dw_max_bit_rate;
+		/*
+		 * dw_max_video_frame_buffer_size is only for uncompressed and
+		 * mjpeg format
+		 */
 		u32	dw_max_video_frame_buffer_size;
 		u32	dw_default_frame_interval;
 		u8	b_frame_interval_type;
+		/* dw_bytes_perline is only for framebased format */
+		u32	dw_bytes_perline;
 	} __attribute__((packed)) frame;
 	u32 *dw_frame_interval;
 };
@@ -1190,6 +1199,7 @@ UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, 32);
 UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, 32);
 UVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize, 32);
 UVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval, 32);
+UVCG_FRAME_ATTR(dw_bytes_perline, dwBytesPerLine, 32);
 
 #undef UVCG_FRAME_ATTR
 
@@ -1324,7 +1334,7 @@ static ssize_t uvcg_frame_dw_frame_interval_store(struct config_item *item,
 
 UVC_ATTR(uvcg_frame_, dw_frame_interval, dwFrameInterval);
 
-static struct configfs_attribute *uvcg_frame_attrs[] = {
+static struct configfs_attribute *uvcg_frame_attrs1[] = {
 	&uvcg_frame_attr_b_frame_index,
 	&uvcg_frame_attr_bm_capabilities,
 	&uvcg_frame_attr_w_width,
@@ -1337,9 +1347,28 @@ static struct configfs_attribute *uvcg_frame_attrs[] = {
 	NULL,
 };
 
-static const struct config_item_type uvcg_frame_type = {
+static struct configfs_attribute *uvcg_frame_attrs2[] = {
+	&uvcg_frame_attr_b_frame_index,
+	&uvcg_frame_attr_bm_capabilities,
+	&uvcg_frame_attr_w_width,
+	&uvcg_frame_attr_w_height,
+	&uvcg_frame_attr_dw_min_bit_rate,
+	&uvcg_frame_attr_dw_max_bit_rate,
+	&uvcg_frame_attr_dw_default_frame_interval,
+	&uvcg_frame_attr_dw_frame_interval,
+	&uvcg_frame_attr_dw_bytes_perline,
+	NULL,
+};
+
+static const struct config_item_type uvcg_frame_type1 = {
 	.ct_item_ops	= &uvcg_config_item_ops,
-	.ct_attrs	= uvcg_frame_attrs,
+	.ct_attrs	= uvcg_frame_attrs1,
+	.ct_owner	= THIS_MODULE,
+};
+
+static const struct config_item_type uvcg_frame_type2 = {
+	.ct_item_ops	= &uvcg_config_item_ops,
+	.ct_attrs	= uvcg_frame_attrs2,
 	.ct_owner	= THIS_MODULE,
 };
 
@@ -1363,6 +1392,7 @@ static struct config_item *uvcg_frame_make(struct config_group *group,
 	h->frame.dw_max_bit_rate		= 55296000;
 	h->frame.dw_max_video_frame_buffer_size	= 460800;
 	h->frame.dw_default_frame_interval	= 666666;
+	h->frame.dw_bytes_perline = 0;
 
 	opts_item = group->cg_item.ci_parent->ci_parent->ci_parent;
 	opts = to_f_uvc_opts(opts_item);
@@ -1375,6 +1405,9 @@ static struct config_item *uvcg_frame_make(struct config_group *group,
 	} else if (fmt->type == UVCG_MJPEG) {
 		h->frame.b_descriptor_subtype = UVC_VS_FRAME_MJPEG;
 		h->fmt_type = UVCG_MJPEG;
+	} else if (fmt->type == UVCG_FRAMEBASED) {
+		h->frame.b_descriptor_subtype = UVC_VS_FRAME_FRAME_BASED;
+		h->fmt_type = UVCG_FRAMEBASED;
 	} else {
 		mutex_unlock(&opts->lock);
 		kfree(h);
@@ -1383,7 +1416,10 @@ static struct config_item *uvcg_frame_make(struct config_group *group,
 	++fmt->num_frames;
 	mutex_unlock(&opts->lock);
 
-	config_item_init_type_name(&h->item, name, &uvcg_frame_type);
+	if (fmt->type == UVCG_FRAMEBASED)
+		config_item_init_type_name(&h->item, name, &uvcg_frame_type2);
+	else
+		config_item_init_type_name(&h->item, name, &uvcg_frame_type1);
 
 	return &h->item;
 }
@@ -1413,7 +1449,8 @@ static void uvcg_format_set_indices(struct config_group *fmt)
 	list_for_each_entry(ci, &fmt->cg_children, ci_entry) {
 		struct uvcg_frame *frm;
 
-		if (ci->ci_type != &uvcg_frame_type)
+		if (ci->ci_type != &uvcg_frame_type1 &&
+		    ci->ci_type != &uvcg_frame_type2)
 			continue;
 
 		frm = to_uvcg_frame(ci);
@@ -1856,6 +1893,261 @@ static const struct uvcg_config_group_type uvcg_mjpeg_grp_type = {
 	.name = "mjpeg",
 };
 
+/* -----------------------------------------------------------------------------
+ * streaming/framebased/<NAME>
+ */
+
+struct uvcg_framebased {
+	struct uvcg_format		fmt;
+	struct uvc_format_framebased	desc;
+};
+
+static struct uvcg_framebased *to_uvcg_framebased(struct config_item *item)
+{
+	return container_of(
+		container_of(to_config_group(item), struct uvcg_format, group),
+		struct uvcg_framebased, fmt);
+}
+
+static struct configfs_group_operations uvcg_framebased_group_ops = {
+	.make_item		= uvcg_frame_make,
+	.drop_item		= uvcg_frame_drop,
+};
+
+#define UVCG_FRAMEBASED_ATTR_RO(cname, aname, bits)			\
+static ssize_t uvcg_framebased_##cname##_show(struct config_item *item,\
+					char *page)			\
+{									\
+	struct uvcg_framebased *u = to_uvcg_framebased(item);		\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;	\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+UVC_ATTR_RO(uvcg_framebased_, cname, aname)
+
+#define UVCG_FRAMEBASED_ATTR(cname, aname, bits)			\
+static ssize_t uvcg_framebased_##cname##_show(struct config_item *item,\
+				char *page)\
+{									\
+	struct uvcg_framebased *u = to_uvcg_framebased(item);		\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;	\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%u\n", le##bits##_to_cpu(u->desc.aname));\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+static ssize_t								\
+uvcg_framebased_##cname##_store(struct config_item *item,		\
+			   const char *page, size_t len)		\
+{									\
+	struct uvcg_framebased *u = to_uvcg_framebased(item);		\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;	\
+	int ret;							\
+	u8 num;								\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	if (u->fmt.linked || opts->refcnt) {				\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	ret = kstrtou8(page, 0, &num);					\
+	if (ret)							\
+		goto end;						\
+									\
+	if (num > 255) {						\
+		ret = -EINVAL;						\
+		goto end;						\
+	}								\
+	u->desc.aname = num;						\
+	ret = len;							\
+end:									\
+	mutex_unlock(&opts->lock);					\
+	mutex_unlock(su_mutex);						\
+	return ret;							\
+}									\
+									\
+UVC_ATTR(uvcg_framebased_, cname, aname)
+
+UVCG_FRAMEBASED_ATTR_RO(b_format_index, bFormatIndex, 8);
+UVCG_FRAMEBASED_ATTR_RO(b_bits_per_pixel, bBitsPerPixel, 8);
+UVCG_FRAMEBASED_ATTR(b_default_frame_index, bDefaultFrameIndex, 8);
+UVCG_FRAMEBASED_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, 8);
+UVCG_FRAMEBASED_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, 8);
+UVCG_FRAMEBASED_ATTR_RO(bm_interface_flags, bmInterfaceFlags, 8);
+
+#undef UVCG_FRAMEBASED_ATTR
+#undef UVCG_FRAMEBASED_ATTR_RO
+
+static ssize_t uvcg_framebased_guid_format_show(struct config_item *item,
+							char *page)
+{
+	struct uvcg_framebased *ch = to_uvcg_framebased(item);
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+	struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+
+	mutex_lock(&opts->lock);
+	memcpy(page, ch->desc.guidFormat, sizeof(ch->desc.guidFormat));
+	mutex_unlock(&opts->lock);
+
+	mutex_unlock(su_mutex);
+
+	return sizeof(ch->desc.guidFormat);
+}
+
+static ssize_t uvcg_framebased_guid_format_store(struct config_item *item,
+						   const char *page, size_t len)
+{
+	struct uvcg_framebased *ch = to_uvcg_framebased(item);
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+	struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex;
+	int ret;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+
+	mutex_lock(&opts->lock);
+	if (ch->fmt.linked || opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	memcpy(ch->desc.guidFormat, page,
+	       min(sizeof(ch->desc.guidFormat), len));
+	ret = sizeof(ch->desc.guidFormat);
+
+end:
+	mutex_unlock(&opts->lock);
+	mutex_unlock(su_mutex);
+	return ret;
+}
+
+UVC_ATTR(uvcg_framebased_, guid_format, guidFormat);
+
+static inline ssize_t
+uvcg_framebased_bma_controls_show(struct config_item *item, char *page)
+{
+	struct uvcg_framebased *u = to_uvcg_framebased(item);
+
+	return uvcg_format_bma_controls_show(&u->fmt, page);
+}
+
+static inline ssize_t
+uvcg_framebased_bma_controls_store(struct config_item *item,
+				     const char *page, size_t len)
+{
+	struct uvcg_framebased *u = to_uvcg_framebased(item);
+
+	return uvcg_format_bma_controls_store(&u->fmt, page, len);
+}
+
+UVC_ATTR(uvcg_framebased_, bma_controls, bmaControls);
+
+static struct configfs_attribute *uvcg_framebased_attrs[] = {
+	&uvcg_framebased_attr_b_format_index,
+	&uvcg_framebased_attr_b_default_frame_index,
+	&uvcg_framebased_attr_b_bits_per_pixel,
+	&uvcg_framebased_attr_b_aspect_ratio_x,
+	&uvcg_framebased_attr_b_aspect_ratio_y,
+	&uvcg_framebased_attr_bm_interface_flags,
+	&uvcg_framebased_attr_bma_controls,
+	&uvcg_framebased_attr_guid_format,
+	NULL,
+};
+
+static const struct config_item_type uvcg_framebased_type = {
+	.ct_item_ops	= &uvcg_config_item_ops,
+	.ct_group_ops	= &uvcg_framebased_group_ops,
+	.ct_attrs	= uvcg_framebased_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_group *uvcg_framebased_make(struct config_group *group,
+						   const char *name)
+{
+	static char guid[] = { /*Declear frame frame based as H264*/
+		'H',  '2',  '6',  '4', 0x00, 0x00, 0x10, 0x00,
+		0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
+	};
+	struct uvcg_framebased *f;
+
+	f = kzalloc(sizeof(*f), GFP_KERNEL);
+	if (!f)
+		return ERR_PTR(-ENOMEM);
+
+	f->desc.bLength			= UVC_DT_FORMAT_FRAMEBASED_SIZE;
+	f->desc.bDescriptorType		= USB_DT_CS_INTERFACE;
+	f->desc.bDescriptorSubType	= UVC_VS_FORMAT_FRAME_BASED;
+	memcpy(f->desc.guidFormat, guid, sizeof(guid));
+	f->desc.bBitsPerPixel		= 16;
+	f->desc.bDefaultFrameIndex	= 1;
+	f->desc.bAspectRatioX		= 0;
+	f->desc.bAspectRatioY		= 0;
+	f->desc.bmInterfaceFlags	= 0;
+	f->desc.bCopyProtect		= 0;
+	f->desc.bVariableSize		= 1;
+
+	f->fmt.type = UVCG_FRAMEBASED;
+	config_group_init_type_name(&f->fmt.group, name,
+				    &uvcg_framebased_type);
+
+	return &f->fmt.group;
+}
+
+static struct configfs_group_operations uvcg_framebased_grp_ops = {
+	.make_group		= uvcg_framebased_make,
+};
+static const struct uvcg_config_group_type uvcg_framebased_grp_type = {
+	.type = {
+		.ct_item_ops	= &uvcg_config_item_ops,
+		.ct_group_ops	= &uvcg_framebased_grp_ops,
+		.ct_owner	= THIS_MODULE,
+	},
+	.name = "framebased",
+};
+
 /* -----------------------------------------------------------------------------
  * streaming/color_matching/default
  */
@@ -2049,6 +2341,10 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n,
 				container_of(fmt, struct uvcg_mjpeg, fmt);
 
 			*size += sizeof(m->desc);
+		} else if (fmt->type == UVCG_FRAMEBASED) {
+			struct uvcg_framebased *f =
+				container_of(fmt, struct uvcg_framebased, fmt);
+			*size += sizeof(f->desc);
 		} else {
 			return -EINVAL;
 		}
@@ -2059,6 +2355,11 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n,
 		int sz = sizeof(frm->dw_frame_interval);
 
 		*size += sizeof(frm->frame);
+		/*
+		 * framebased has duplicate member with uncompressed and
+		 * mjpeg, so minus it
+		 */
+		*size -= sizeof(u32);
 		*size += frm->frame.b_frame_interval_type * sz;
 	}
 	break;
@@ -2069,6 +2370,27 @@ static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n,
 	return 0;
 }
 
+static int __uvcg_copy_framebased_desc(void *dest, struct uvcg_frame *frm,
+				       int sz)
+{
+	struct uvc_frame_framebased *desc = dest;
+
+	desc->bLength = frm->frame.b_length;
+	desc->bDescriptorType = frm->frame.b_descriptor_type;
+	desc->bDescriptorSubType = frm->frame.b_descriptor_subtype;
+	desc->bFrameIndex = frm->frame.b_frame_index;
+	desc->bmCapabilities = frm->frame.bm_capabilities;
+	desc->wWidth = frm->frame.w_width;
+	desc->wHeight = frm->frame.w_height;
+	desc->dwMinBitRate = frm->frame.dw_min_bit_rate;
+	desc->dwMaxBitRate = frm->frame.dw_max_bit_rate;
+	desc->dwDefaultFrameInterval = frm->frame.dw_default_frame_interval;
+	desc->bFrameIntervalType = frm->frame.b_frame_interval_type;
+	desc->dwBytesPerLine = frm->frame.dw_bytes_perline;
+
+	return 0;
+}
+
 /*
  * Fill an array of streaming descriptors.
  *
@@ -2123,6 +2445,15 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n,
 			m->desc.bNumFrameDescriptors = fmt->num_frames;
 			memcpy(*dest, &m->desc, sizeof(m->desc));
 			*dest += sizeof(m->desc);
+		} else if (fmt->type == UVCG_FRAMEBASED) {
+			struct uvcg_framebased *f =
+				container_of(fmt, struct uvcg_framebased,
+					     fmt);
+
+			f->desc.bFormatIndex = n + 1;
+			f->desc.bNumFrameDescriptors = fmt->num_frames;
+			memcpy(*dest, &f->desc, sizeof(f->desc));
+			*dest += sizeof(f->desc);
 		} else {
 			return -EINVAL;
 		}
@@ -2132,8 +2463,11 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n,
 		struct uvcg_frame *frm = priv1;
 		struct uvc_descriptor_header *h = *dest;
 
-		sz = sizeof(frm->frame);
-		memcpy(*dest, &frm->frame, sz);
+		sz = sizeof(frm->frame) - 4;
+		if (frm->fmt_type != UVCG_FRAMEBASED)
+			memcpy(*dest, &frm->frame, sz);
+		else
+			__uvcg_copy_framebased_desc(*dest, frm, sz);
 		*dest += sz;
 		sz = frm->frame.b_frame_interval_type *
 			sizeof(*frm->dw_frame_interval);
@@ -2145,6 +2479,9 @@ static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n,
 		else if (frm->fmt_type == UVCG_MJPEG)
 			h->bLength = UVC_DT_FRAME_MJPEG_SIZE(
 				frm->frame.b_frame_interval_type);
+		else if (frm->fmt_type == UVCG_FRAMEBASED)
+			h->bLength = UVC_DT_FRAME_FRAMEBASED_SIZE(
+				 frm->frame.b_frame_interval_type);
 	}
 	break;
 	}
@@ -2357,6 +2694,7 @@ static const struct uvcg_config_group_type uvcg_streaming_grp_type = {
 		&uvcg_streaming_header_grp_type,
 		&uvcg_uncompressed_grp_type,
 		&uvcg_mjpeg_grp_type,
+		&uvcg_framebased_grp_type,
 		&uvcg_color_matching_grp_type,
 		&uvcg_streaming_class_grp_type,
 		NULL,
@@ -2424,16 +2762,22 @@ end:									\
 									\
 UVC_ATTR(f_uvc_opts_, cname, cname)
 
+UVCG_OPTS_ATTR(streaming_bulk, streaming_bulk, 1);
 UVCG_OPTS_ATTR(streaming_interval, streaming_interval, 16);
 UVCG_OPTS_ATTR(streaming_maxpacket, streaming_maxpacket, 3072);
 UVCG_OPTS_ATTR(streaming_maxburst, streaming_maxburst, 15);
+UVCG_OPTS_ATTR(uvc_num_request, uvc_num_request, UVC_MAX_NUM_REQUESTS);
+UVCG_OPTS_ATTR(pm_qos_latency, pm_qos_latency, PM_QOS_LATENCY_ANY);
 
 #undef UVCG_OPTS_ATTR
 
 static struct configfs_attribute *uvc_attrs[] = {
+	&f_uvc_opts_attr_streaming_bulk,
 	&f_uvc_opts_attr_streaming_interval,
 	&f_uvc_opts_attr_streaming_maxpacket,
 	&f_uvc_opts_attr_streaming_maxburst,
+	&f_uvc_opts_attr_uvc_num_request,
+	&f_uvc_opts_attr_pm_qos_latency,
 	NULL,
 };
 
diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c
index cab1e3046..0f5e6fb93 100644
--- a/drivers/usb/gadget/function/uvc_queue.c
+++ b/drivers/usb/gadget/function/uvc_queue.c
@@ -124,6 +124,14 @@ int uvcg_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
 	queue->queue.mem_ops = &vb2_vmalloc_memops;
 	queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
 				     | V4L2_BUF_FLAG_TSTAMP_SRC_EOF;
+	/*
+	 * For rockchip platform, the userspace uvc application
+	 * use bytesused == 0 as a way to indicate that the data
+	 * is all zero and unused.
+	 */
+#ifdef CONFIG_ARCH_ROCKCHIP
+	queue->queue.allow_zero_bytesused = 1;
+#endif
 	ret = vb2_queue_init(&queue->queue);
 	if (ret)
 		return ret;
diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index 65abd55ce..2528c8942 100644
--- a/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
@@ -41,6 +41,7 @@ uvc_send_response(struct uvc_device *uvc, struct uvc_request_data *data)
 	req->length = min_t(unsigned int, uvc->event_length, data->length);
 	req->zero = data->length < uvc->event_length;
 
+	uvc_trace(UVC_TRACE_CONTROL, "%s: req len %d\n", __func__, req->length);
 	memcpy(req->buf, data->data, req->length);
 
 	return usb_ep_queue(cdev->gadget->ep0, req, GFP_KERNEL);
@@ -58,6 +59,8 @@ struct uvc_format {
 static struct uvc_format uvc_formats[] = {
 	{ 16, V4L2_PIX_FMT_YUYV  },
 	{ 0,  V4L2_PIX_FMT_MJPEG },
+	{ 0,  V4L2_PIX_FMT_H264  },
+	{ 0,  V4L2_PIX_FMT_H265  },
 };
 
 static int
@@ -201,11 +204,21 @@ uvc_v4l2_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
 		return ret;
 
 	/*
-	 * Complete the alternate setting selection setup phase now that
-	 * userspace is ready to provide video frames.
+	 * Alt settings in an interface are supported only
+	 * for ISOC endpoints as there are different alt-
+	 * settings for zero-bandwidth and full-bandwidth
+	 * cases, but the same is not true for BULK endpoints,
+	 * as they have a single alt-setting.
 	 */
-	uvc_function_setup_continue(uvc);
-	uvc->state = UVC_STATE_STREAMING;
+	if (!usb_endpoint_xfer_bulk(video->ep->desc)) {
+		/*
+		 * Complete the alternate setting selection
+		 * setup phase now that userspace is ready
+		 * to provide video frames.
+		 */
+		uvc_function_setup_continue(uvc);
+		uvc->state = UVC_STATE_STREAMING;
+	}
 
 	return 0;
 }
@@ -227,56 +240,17 @@ static int
 uvc_v4l2_subscribe_event(struct v4l2_fh *fh,
 			 const struct v4l2_event_subscription *sub)
 {
-	struct uvc_device *uvc = video_get_drvdata(fh->vdev);
-	struct uvc_file_handle *handle = to_uvc_file_handle(fh);
-	int ret;
-
 	if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
 		return -EINVAL;
 
-	if (sub->type == UVC_EVENT_SETUP && uvc->func_connected)
-		return -EBUSY;
-
-	ret = v4l2_event_subscribe(fh, sub, 2, NULL);
-	if (ret < 0)
-		return ret;
-
-	if (sub->type == UVC_EVENT_SETUP) {
-		uvc->func_connected = true;
-		handle->is_uvc_app_handle = true;
-		uvc_function_connect(uvc);
-	}
-
-	return 0;
-}
-
-static void uvc_v4l2_disable(struct uvc_device *uvc)
-{
-	uvc_function_disconnect(uvc);
-	uvcg_video_enable(&uvc->video, 0);
-	uvcg_free_buffers(&uvc->video.queue);
-	uvc->func_connected = false;
-	wake_up_interruptible(&uvc->func_connected_queue);
+	return v4l2_event_subscribe(fh, sub, 2, NULL);
 }
 
 static int
 uvc_v4l2_unsubscribe_event(struct v4l2_fh *fh,
 			   const struct v4l2_event_subscription *sub)
 {
-	struct uvc_device *uvc = video_get_drvdata(fh->vdev);
-	struct uvc_file_handle *handle = to_uvc_file_handle(fh);
-	int ret;
-
-	ret = v4l2_event_unsubscribe(fh, sub);
-	if (ret < 0)
-		return ret;
-
-	if (sub->type == UVC_EVENT_SETUP && handle->is_uvc_app_handle) {
-		uvc_v4l2_disable(uvc);
-		handle->is_uvc_app_handle = false;
-	}
-
-	return 0;
+	return v4l2_event_unsubscribe(fh, sub);
 }
 
 static long
@@ -331,6 +305,7 @@ uvc_v4l2_open(struct file *file)
 	handle->device = &uvc->video;
 	file->private_data = &handle->vfh;
 
+	uvc_function_connect(uvc);
 	return 0;
 }
 
@@ -342,9 +317,11 @@ uvc_v4l2_release(struct file *file)
 	struct uvc_file_handle *handle = to_uvc_file_handle(file->private_data);
 	struct uvc_video *video = handle->device;
 
+	uvc_function_disconnect(uvc);
+
 	mutex_lock(&video->mutex);
-	if (handle->is_uvc_app_handle)
-		uvc_v4l2_disable(uvc);
+	uvcg_video_enable(video, 0);
+	uvcg_free_buffers(&video->queue);
 	mutex_unlock(&video->mutex);
 
 	file->private_data = NULL;
@@ -390,6 +367,9 @@ const struct v4l2_file_operations uvc_v4l2_fops = {
 	.open		= uvc_v4l2_open,
 	.release	= uvc_v4l2_release,
 	.unlocked_ioctl	= video_ioctl2,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl32	= video_ioctl2,
+#endif
 	.mmap		= uvc_v4l2_mmap,
 	.poll		= uvc_v4l2_poll,
 #ifndef CONFIG_MMU
diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 5ce548c23..cae5c2b62 100644
--- a/drivers/usb/gadget/function/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
@@ -12,12 +12,14 @@
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
 #include <linux/usb/video.h>
+#include <linux/pm_qos.h>
 
 #include <media/v4l2-dev.h>
 
 #include "uvc.h"
 #include "uvc_queue.h"
 #include "uvc_video.h"
+#include "u_uvc.h"
 
 /* --------------------------------------------------------------------------
  * Video codecs
@@ -87,6 +89,7 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video,
 		video->fid ^= UVC_STREAM_FID;
 
 		video->payload_size = 0;
+		req->zero = 1;
 	}
 
 	if (video->payload_size == video->max_payload_size ||
@@ -135,7 +138,7 @@ static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req)
 			 ret);
 
 		/* Isochronous endpoints can't be halted. */
-		if (usb_endpoint_xfer_bulk(video->ep->desc))
+		if (video->ep->desc && usb_endpoint_xfer_bulk(video->ep->desc))
 			usb_ep_set_halt(video->ep);
 	}
 
@@ -176,8 +179,13 @@ static int
 uvc_video_free_requests(struct uvc_video *video)
 {
 	unsigned int i;
+	struct uvc_device *uvc;
+	struct f_uvc_opts *opts;
 
-	for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
+	uvc = container_of(video, struct uvc_device, video);
+	opts = fi_to_f_uvc_opts(uvc->func.fi);
+
+	for (i = 0; i < opts->uvc_num_request; ++i) {
 		if (video->req[i]) {
 			usb_ep_free_request(video->ep, video->req[i]);
 			video->req[i] = NULL;
@@ -200,14 +208,24 @@ uvc_video_alloc_requests(struct uvc_video *video)
 	unsigned int req_size;
 	unsigned int i;
 	int ret = -ENOMEM;
+	struct uvc_device *uvc;
+	struct f_uvc_opts *opts;
+
+	uvc = container_of(video, struct uvc_device, video);
+	opts = fi_to_f_uvc_opts(uvc->func.fi);
 
 	BUG_ON(video->req_size);
 
-	req_size = video->ep->maxpacket
-		 * max_t(unsigned int, video->ep->maxburst, 1)
-		 * (video->ep->mult);
+	if (!usb_endpoint_xfer_bulk(video->ep->desc)) {
+		req_size = video->ep->maxpacket
+			 * max_t(unsigned int, video->ep->maxburst, 1)
+			 * (video->ep->mult);
+	} else {
+		req_size = video->ep->maxpacket
+			 * max_t(unsigned int, video->ep->maxburst, 1);
+	}
 
-	for (i = 0; i < UVC_NUM_REQUESTS; ++i) {
+	for (i = 0; i < opts->uvc_num_request; ++i) {
 		video->req_buffer[i] = kmalloc(req_size, GFP_KERNEL);
 		if (video->req_buffer[i] == NULL)
 			goto error;
@@ -301,6 +319,8 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
 {
 	unsigned int i;
 	int ret;
+	struct uvc_device *uvc;
+	struct f_uvc_opts *opts;
 
 	if (video->ep == NULL) {
 		uvcg_info(&video->uvc->func,
@@ -308,19 +328,25 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
 		return -ENODEV;
 	}
 
+	uvc = container_of(video, struct uvc_device, video);
+	opts = fi_to_f_uvc_opts(uvc->func.fi);
+
 	if (!enable) {
 		cancel_work_sync(&video->pump);
 		uvcg_queue_cancel(&video->queue, 0);
 
-		for (i = 0; i < UVC_NUM_REQUESTS; ++i)
+		for (i = 0; i < opts->uvc_num_request; ++i)
 			if (video->req[i])
 				usb_ep_dequeue(video->ep, video->req[i]);
 
 		uvc_video_free_requests(video);
 		uvcg_queue_enable(&video->queue, 0);
+		if (cpu_latency_qos_request_active(&uvc->pm_qos))
+			cpu_latency_qos_remove_request(&uvc->pm_qos);
 		return 0;
 	}
 
+	cpu_latency_qos_add_request(&uvc->pm_qos, opts->pm_qos_latency);
 	if ((ret = uvcg_queue_enable(&video->queue, 1)) < 0)
 		return ret;
 
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
index f28e1bbd5..6c8842e54 100644
--- a/drivers/usb/gadget/udc/Kconfig
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -330,7 +330,6 @@ config USB_AMD5536UDC
 config USB_FSL_QE
 	tristate "Freescale QE/CPM USB Device Controller"
 	depends on FSL_SOC && (QUICC_ENGINE || CPM)
-	depends on !64BIT || BROKEN
 	help
 	   Some of Freescale PowerPC processors have a Full Speed
 	   QE/CPM2 USB controller, which support device mode with 4
diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c
index 3a3b5a03d..a0c281293 100644
--- a/drivers/usb/gadget/udc/core.c
+++ b/drivers/usb/gadget/udc/core.c
@@ -29,6 +29,7 @@
  * @list: for use by the udc class driver
  * @vbus: for udcs who care about vbus status, this value is real vbus status;
  * for udcs who do not care about vbus status, this value is always true
+ * @started: the UDC's started state. True if the UDC had started.
  *
  * This represents the internal data structure which is used by the UDC-class
  * to hold information about udc driver and gadget together.
@@ -39,6 +40,7 @@ struct usb_udc {
 	struct device			dev;
 	struct list_head		list;
 	bool				vbus;
+	bool				started;
 };
 
 static struct class *udc_class;
@@ -87,7 +89,7 @@ EXPORT_SYMBOL_GPL(usb_ep_set_maxpacket_limit);
  * configurable, with more generic names like "ep-a".  (remember that for
  * USB, "in" means "towards the USB host".)
  *
- * This routine must be called in process context.
+ * This routine may be called in an atomic (interrupt) context.
  *
  * returns zero, or a negative error code.
  */
@@ -132,7 +134,7 @@ EXPORT_SYMBOL_GPL(usb_ep_enable);
  * gadget drivers must call usb_ep_enable() again before queueing
  * requests to the endpoint.
  *
- * This routine must be called in process context.
+ * This routine may be called in an atomic (interrupt) context.
  *
  * returns zero, or a negative error code.
  */
@@ -755,7 +757,7 @@ int usb_gadget_deactivate(struct usb_gadget *gadget)
 {
 	int ret = 0;
 
-	if (gadget->deactivated)
+	if (!gadget || gadget->deactivated)
 		goto out;
 
 	if (gadget->connected) {
@@ -1004,6 +1006,25 @@ int usb_gadget_ep_match_desc(struct usb_gadget *gadget,
 }
 EXPORT_SYMBOL_GPL(usb_gadget_ep_match_desc);
 
+/**
+ * usb_gadget_check_config - checks if the UDC can support the binded
+ *	configuration
+ * @gadget: controller to check the USB configuration
+ *
+ * Ensure that a UDC is able to support the requested resources by a
+ * configuration, and that there are no resource limitations, such as
+ * internal memory allocated to all requested endpoints.
+ *
+ * Returns zero on success, else a negative errno.
+ */
+int usb_gadget_check_config(struct usb_gadget *gadget)
+{
+	if (gadget->ops->check_config)
+		return gadget->ops->check_config(gadget);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(usb_gadget_check_config);
+
 /* ------------------------------------------------------------------------- */
 
 static void usb_gadget_state_work(struct work_struct *work)
@@ -1085,7 +1106,18 @@ EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
  */
 static inline int usb_gadget_udc_start(struct usb_udc *udc)
 {
-	return udc->gadget->ops->udc_start(udc->gadget, udc->driver);
+	int ret;
+
+	if (udc->started) {
+		dev_err(&udc->dev, "UDC had already started\n");
+		return -EBUSY;
+	}
+
+	ret = udc->gadget->ops->udc_start(udc->gadget, udc->driver);
+	if (!ret)
+		udc->started = true;
+
+	return ret;
 }
 
 /**
@@ -1101,7 +1133,13 @@ static inline int usb_gadget_udc_start(struct usb_udc *udc)
  */
 static inline void usb_gadget_udc_stop(struct usb_udc *udc)
 {
+	if (!udc->started) {
+		dev_err(&udc->dev, "UDC had already stopped\n");
+		return;
+	}
+
 	udc->gadget->ops->udc_stop(udc->gadget);
+	udc->started = false;
 }
 
 /**
@@ -1117,12 +1155,65 @@ static inline void usb_gadget_udc_stop(struct usb_udc *udc)
 static inline void usb_gadget_udc_set_speed(struct usb_udc *udc,
 					    enum usb_device_speed speed)
 {
-	if (udc->gadget->ops->udc_set_speed) {
-		enum usb_device_speed s;
+	struct usb_gadget *gadget = udc->gadget;
+	enum usb_device_speed s;
 
-		s = min(speed, udc->gadget->max_speed);
-		udc->gadget->ops->udc_set_speed(udc->gadget, s);
-	}
+	if (speed == USB_SPEED_UNKNOWN)
+		s = gadget->max_speed;
+	else
+		s = min(speed, gadget->max_speed);
+
+	if (s == USB_SPEED_SUPER_PLUS && gadget->ops->udc_set_ssp_rate)
+		gadget->ops->udc_set_ssp_rate(gadget, gadget->max_ssp_rate);
+	else if (gadget->ops->udc_set_speed)
+		gadget->ops->udc_set_speed(gadget, s);
+}
+
+/**
+ * usb_gadget_enable_async_callbacks - tell usb device controller to enable asynchronous callbacks
+ * @udc: The UDC which should enable async callbacks
+ *
+ * This routine is used when binding gadget drivers.  It undoes the effect
+ * of usb_gadget_disable_async_callbacks(); the UDC driver should enable IRQs
+ * (if necessary) and resume issuing callbacks.
+ *
+ * This routine will always be called in process context.
+ */
+static inline void usb_gadget_enable_async_callbacks(struct usb_udc *udc)
+{
+	struct usb_gadget *gadget = udc->gadget;
+
+	if (gadget->ops->udc_async_callbacks)
+		gadget->ops->udc_async_callbacks(gadget, true);
+}
+
+/**
+ * usb_gadget_disable_async_callbacks - tell usb device controller to disable asynchronous callbacks
+ * @udc: The UDC which should disable async callbacks
+ *
+ * This routine is used when unbinding gadget drivers.  It prevents a race:
+ * The UDC driver doesn't know when the gadget driver's ->unbind callback
+ * runs, so unless it is told to disable asynchronous callbacks, it might
+ * issue a callback (such as ->disconnect) after the unbind has completed.
+ *
+ * After this function runs, the UDC driver must suppress all ->suspend,
+ * ->resume, ->disconnect, ->reset, and ->setup callbacks to the gadget driver
+ * until async callbacks are again enabled.  A simple-minded but effective
+ * way to accomplish this is to tell the UDC hardware not to generate any
+ * more IRQs.
+ *
+ * Request completion callbacks must still be issued.  However, it's okay
+ * to defer them until the request is cancelled, since the pull-up will be
+ * turned off during the time period when async callbacks are disabled.
+ *
+ * This routine will always be called in process context.
+ */
+static inline void usb_gadget_disable_async_callbacks(struct usb_udc *udc)
+{
+	struct usb_gadget *gadget = udc->gadget;
+
+	if (gadget->ops->udc_async_callbacks)
+		gadget->ops->udc_async_callbacks(gadget, false);
 }
 
 /**
@@ -1225,6 +1316,8 @@ int usb_add_gadget(struct usb_gadget *gadget)
 	udc->gadget = gadget;
 	gadget->udc = udc;
 
+	udc->started = false;
+
 	mutex_lock(&udc_lock);
 	list_add_tail(&udc->list, &udc_list);
 
@@ -1337,6 +1430,7 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)
 	kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
 
 	usb_gadget_disconnect(udc->gadget);
+	usb_gadget_disable_async_callbacks(udc);
 	if (udc->gadget->irq)
 		synchronize_irq(udc->gadget->irq);
 	udc->driver->unbind(udc->gadget);
@@ -1416,6 +1510,7 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
 		driver->unbind(udc->gadget);
 		goto err1;
 	}
+	usb_gadget_enable_async_callbacks(udc);
 	usb_udc_connect_control(udc);
 
 	kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
diff --git a/drivers/usb/gadget/udc/trace.c b/drivers/usb/gadget/udc/trace.c
index 7430624c0..e0e617280 100644
--- a/drivers/usb/gadget/udc/trace.c
+++ b/drivers/usb/gadget/udc/trace.c
@@ -8,3 +8,6 @@
 
 #define CREATE_TRACE_POINTS
 #include "trace.h"
+
+EXPORT_TRACEPOINT_SYMBOL_GPL(usb_gadget_connect);
+EXPORT_TRACEPOINT_SYMBOL_GPL(usb_gadget_disconnect);
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index bc731332f..2c7155fae 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -19,9 +19,7 @@ ifneq ($(CONFIG_USB_XHCI_DBGCAP), )
 	xhci-hcd-y += xhci-dbgcap.o xhci-dbgtty.o
 endif
 
-ifneq ($(CONFIG_USB_XHCI_MTK), )
-	xhci-hcd-y += xhci-mtk-sch.o
-endif
+xhci-mtk-hcd-y := xhci-mtk.o xhci-mtk-sch.o
 
 xhci-plat-hcd-y := xhci-plat.o
 ifneq ($(CONFIG_USB_XHCI_MVEBU), )
@@ -75,7 +73,7 @@ obj-$(CONFIG_USB_XHCI_PCI)	+= xhci-pci.o
 obj-$(CONFIG_USB_XHCI_PCI_RENESAS)	+= xhci-pci-renesas.o
 obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o
 obj-$(CONFIG_USB_XHCI_HISTB)	+= xhci-histb.o
-obj-$(CONFIG_USB_XHCI_MTK)	+= xhci-mtk.o
+obj-$(CONFIG_USB_XHCI_MTK)	+= xhci-mtk-hcd.o
 obj-$(CONFIG_USB_XHCI_TEGRA)	+= xhci-tegra.o
 obj-$(CONFIG_USB_SL811_HCD)	+= sl811-hcd.o
 obj-$(CONFIG_USB_SL811_CS)	+= sl811_cs.o
diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
index 2dcfc67f2..3e48737f8 100644
--- a/drivers/usb/host/ehci-platform.c
+++ b/drivers/usb/host/ehci-platform.c
@@ -28,6 +28,7 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/reset.h>
 #include <linux/sys_soc.h>
 #include <linux/timer.h>
@@ -56,6 +57,37 @@ struct ehci_platform_priv {
 
 static const char hcd_name[] = "ehci-platform";
 
+static void ehci_rockchip_relinquish_port(struct usb_hcd *hcd, int portnum)
+{
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+	u32 __iomem *status_reg = &ehci->regs->port_status[--portnum];
+	u32 portsc;
+
+	portsc = ehci_readl(ehci, status_reg);
+	portsc &= ~(PORT_OWNER | PORT_RWC_BITS);
+
+	ehci_writel(ehci, portsc, status_reg);
+}
+
+#define USIC_MICROFRAME_OFFSET	0x90
+#define USIC_SCALE_DOWN_OFFSET	0xa0
+#define USIC_ENABLE_OFFSET	0xb0
+#define USIC_ENABLE		BIT(0)
+#define USIC_SCALE_DOWN		BIT(2)
+#define USIC_MICROFRAME_COUNT	0x1d4d
+
+static void ehci_usic_init(struct usb_hcd *hcd)
+{
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+
+	ehci_writel(ehci, USIC_ENABLE,
+		    hcd->regs + USIC_ENABLE_OFFSET);
+	ehci_writel(ehci, USIC_MICROFRAME_COUNT,
+		    hcd->regs + USIC_MICROFRAME_OFFSET);
+	ehci_writel(ehci, USIC_SCALE_DOWN,
+		    hcd->regs + USIC_SCALE_DOWN_OFFSET);
+}
+
 static int ehci_platform_reset(struct usb_hcd *hcd)
 {
 	struct platform_device *pdev = to_platform_device(hcd->self.controller);
@@ -303,6 +335,12 @@ static int ehci_platform_probe(struct platform_device *dev)
 		if (soc_device_match(quirk_poll_match))
 			priv->quirk_poll = true;
 
+		if (of_machine_is_compatible("rockchip,rk3288") &&
+		    of_property_read_bool(dev->dev.of_node,
+					  "rockchip-relinquish-port"))
+			ehci_platform_hc_driver.relinquish_port =
+					  ehci_rockchip_relinquish_port;
+
 		for (clk = 0; clk < EHCI_MAX_CLKS; clk++) {
 			priv->clks[clk] = of_clk_get(dev->dev.of_node, clk);
 			if (IS_ERR(priv->clks[clk])) {
@@ -351,6 +389,9 @@ static int ehci_platform_probe(struct platform_device *dev)
 	}
 #endif
 
+	pm_runtime_set_active(&dev->dev);
+	pm_runtime_enable(&dev->dev);
+	pm_runtime_get_sync(&dev->dev);
 	if (pdata->power_on) {
 		err = pdata->power_on(dev);
 		if (err < 0)
@@ -370,6 +411,9 @@ static int ehci_platform_probe(struct platform_device *dev)
 	if (err)
 		goto err_power;
 
+	if (of_usb_get_phy_mode(dev->dev.of_node) == USBPHY_INTERFACE_MODE_HSIC)
+		ehci_usic_init(hcd);
+
 	device_wakeup_enable(hcd->self.controller);
 	device_enable_async_suspend(hcd->self.controller);
 	platform_set_drvdata(dev, hcd);
@@ -383,6 +427,8 @@ static int ehci_platform_probe(struct platform_device *dev)
 	if (pdata->power_off)
 		pdata->power_off(dev);
 err_reset:
+	pm_runtime_put_sync(&dev->dev);
+	pm_runtime_disable(&dev->dev);
 	reset_control_assert(priv->rsts);
 err_put_clks:
 	while (--clk >= 0)
@@ -418,6 +464,9 @@ static int ehci_platform_remove(struct platform_device *dev)
 
 	usb_put_hcd(hcd);
 
+	pm_runtime_put_sync(&dev->dev);
+	pm_runtime_disable(&dev->dev);
+
 	if (pdata == &ehci_platform_defaults)
 		dev->dev.platform_data = NULL;
 
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 59fd523c5..55e517248 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -218,7 +218,13 @@ struct ehci_hcd {			/* one per controller */
 	unsigned		frame_index_bug:1; /* MosChip (AKA NetMos) */
 	unsigned		need_oc_pp_cycle:1; /* MPC834X port power */
 	unsigned		imx28_write_fix:1; /* For Freescale i.MX28 */
+	/*
+	 * __GENKSYMS__ test is an abi workaround for commit
+	 * 7f2d73788d90 ("usb: ehci: handshake CMD_RUN * instead of STS_HALT")
+	 */
+#ifndef __GENKSYMS__
 	unsigned		is_aspeed:1;
+#endif
 
 	/* required for usb32 quirk */
 	#define OHCI_CTRL_HCFS          (3 << 6)
diff --git a/drivers/usb/host/fotg210.h b/drivers/usb/host/fotg210.h
index 67f59517e..6cee40ec6 100644
--- a/drivers/usb/host/fotg210.h
+++ b/drivers/usb/host/fotg210.h
@@ -686,6 +686,11 @@ static inline unsigned fotg210_read_frame_index(struct fotg210_hcd *fotg210)
 	return fotg210_readl(fotg210, &fotg210->regs->frame_index);
 }
 
+#define fotg210_itdlen(urb, desc, t) ({			\
+	usb_pipein((urb)->pipe) ?				\
+	(desc)->length - FOTG210_ITD_LENGTH(t) :			\
+	FOTG210_ITD_LENGTH(t);					\
+})
 /*-------------------------------------------------------------------------*/
 
 #endif /* __LINUX_FOTG210_H */
diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c
index 4a8456f12..492353678 100644
--- a/drivers/usb/host/ohci-platform.c
+++ b/drivers/usb/host/ohci-platform.c
@@ -32,7 +32,7 @@
 #include "ohci.h"
 
 #define DRIVER_DESC "OHCI generic platform driver"
-#define OHCI_MAX_CLKS 3
+#define OHCI_MAX_CLKS 4
 #define hcd_to_ohci_priv(h) ((struct ohci_platform_priv *)hcd_to_ohci(h)->priv)
 
 struct ohci_platform_priv {
@@ -96,7 +96,7 @@ static int ohci_platform_probe(struct platform_device *dev)
 	struct ohci_hcd *ohci;
 	int err, irq, clk = 0;
 
-	if (usb_disabled())
+	if (usb_disabled() || of_machine_is_compatible("rockchip,rk3288"))
 		return -ENODEV;
 
 	/*
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 7bb306741..55e568b3d 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -449,8 +449,13 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
 	    cmd->status == COMP_COMMAND_RING_STOPPED) {
 		xhci_warn(xhci, "Timeout while waiting for stop endpoint command\n");
 		ret = -ETIME;
+		goto cmd_cleanup;
 	}
 
+	ret = xhci_vendor_sync_dev_ctx(xhci, slot_id);
+	if (ret)
+		xhci_warn(xhci, "Sync device context failed, ret=%d\n", ret);
+
 cmd_cleanup:
 	xhci_free_command(xhci, cmd);
 	return ret;
@@ -631,7 +636,6 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci,
 			continue;
 
 		retval = xhci_disable_slot(xhci, i);
-		xhci_free_virt_device(xhci, i);
 		if (retval)
 			xhci_err(xhci, "Failed to disable slot %d, %d. Enter test mode anyway\n",
 				 i, retval);
@@ -1735,6 +1739,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(xhci_bus_suspend);
 
 /*
  * Workaround for missing Cold Attach Status (CAS) if device re-plugged in S3.
@@ -1879,6 +1884,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
 	spin_unlock_irqrestore(&xhci->lock, flags);
 	return 0;
 }
+EXPORT_SYMBOL_GPL(xhci_bus_resume);
 
 unsigned long xhci_get_resuming_ports(struct usb_hcd *hcd)
 {
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index d1a42300a..92218383b 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -65,7 +65,7 @@ static struct xhci_segment *xhci_segment_alloc(struct xhci_hcd *xhci,
 	return seg;
 }
 
-static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg)
+void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg)
 {
 	if (seg->trbs) {
 		dma_pool_free(xhci->segment_pool, seg->trbs, seg->dma);
@@ -74,8 +74,9 @@ static void xhci_segment_free(struct xhci_hcd *xhci, struct xhci_segment *seg)
 	kfree(seg->bounce_buf);
 	kfree(seg);
 }
+EXPORT_SYMBOL_GPL(xhci_segment_free);
 
-static void xhci_free_segments_for_ring(struct xhci_hcd *xhci,
+void xhci_free_segments_for_ring(struct xhci_hcd *xhci,
 				struct xhci_segment *first)
 {
 	struct xhci_segment *seg;
@@ -96,9 +97,9 @@ static void xhci_free_segments_for_ring(struct xhci_hcd *xhci,
  * DMA address of the next segment.  The caller needs to set any Link TRB
  * related flags, such as End TRB, Toggle Cycle, and no snoop.
  */
-static void xhci_link_segments(struct xhci_segment *prev,
-			       struct xhci_segment *next,
-			       enum xhci_ring_type type, bool chain_links)
+void xhci_link_segments(struct xhci_segment *prev,
+			struct xhci_segment *next,
+			enum xhci_ring_type type, bool chain_links)
 {
 	u32 val;
 
@@ -118,6 +119,7 @@ static void xhci_link_segments(struct xhci_segment *prev,
 		prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val);
 	}
 }
+EXPORT_SYMBOL_GPL(xhci_link_segments);
 
 /*
  * Link the ring to the new segments.
@@ -292,6 +294,7 @@ void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring)
 
 	kfree(ring);
 }
+EXPORT_SYMBOL_GPL(xhci_ring_free);
 
 void xhci_initialize_ring_info(struct xhci_ring *ring,
 			       unsigned int cycle_state)
@@ -316,6 +319,7 @@ void xhci_initialize_ring_info(struct xhci_ring *ring,
 	 */
 	ring->num_trbs_free = ring->num_segs * (TRBS_PER_SEGMENT - 1) - 1;
 }
+EXPORT_SYMBOL_GPL(xhci_initialize_ring_info);
 
 /* Allocate segments and link them for a ring */
 static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci,
@@ -361,6 +365,54 @@ static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci,
 	return 0;
 }
 
+static void xhci_vendor_free_container_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx)
+{
+	struct xhci_vendor_ops *ops = xhci_vendor_get_ops(xhci);
+
+	if (ops && ops->free_container_ctx)
+		ops->free_container_ctx(xhci, ctx);
+}
+
+static void xhci_vendor_alloc_container_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx,
+					    int type, gfp_t flags)
+{
+	struct xhci_vendor_ops *ops = xhci_vendor_get_ops(xhci);
+
+	if (ops && ops->alloc_container_ctx)
+		ops->alloc_container_ctx(xhci, ctx, type, flags);
+}
+
+static struct xhci_ring *xhci_vendor_alloc_transfer_ring(struct xhci_hcd *xhci,
+		u32 endpoint_type, enum xhci_ring_type ring_type,
+		unsigned int max_packet, gfp_t mem_flags)
+{
+	struct xhci_vendor_ops *ops = xhci_vendor_get_ops(xhci);
+
+	if (ops && ops->alloc_transfer_ring)
+		return ops->alloc_transfer_ring(xhci, endpoint_type, ring_type,
+				max_packet, mem_flags);
+	return 0;
+}
+
+void xhci_vendor_free_transfer_ring(struct xhci_hcd *xhci,
+		struct xhci_virt_device *virt_dev, unsigned int ep_index)
+{
+	struct xhci_vendor_ops *ops = xhci_vendor_get_ops(xhci);
+
+	if (ops && ops->free_transfer_ring)
+		ops->free_transfer_ring(xhci, virt_dev, ep_index);
+}
+
+bool xhci_vendor_is_usb_offload_enabled(struct xhci_hcd *xhci,
+		struct xhci_virt_device *virt_dev, unsigned int ep_index)
+{
+	struct xhci_vendor_ops *ops = xhci_vendor_get_ops(xhci);
+
+	if (ops && ops->is_usb_offload_enabled)
+		return ops->is_usb_offload_enabled(xhci, virt_dev, ep_index);
+	return false;
+}
+
 /*
  * Create a new ring with zero or more segments.
  *
@@ -407,12 +459,17 @@ struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
 	kfree(ring);
 	return NULL;
 }
+EXPORT_SYMBOL_GPL(xhci_ring_alloc);
 
 void xhci_free_endpoint_ring(struct xhci_hcd *xhci,
 		struct xhci_virt_device *virt_dev,
 		unsigned int ep_index)
 {
-	xhci_ring_free(xhci, virt_dev->eps[ep_index].ring);
+	if (xhci_vendor_is_usb_offload_enabled(xhci, virt_dev, ep_index))
+		xhci_vendor_free_transfer_ring(xhci, virt_dev, ep_index);
+	else
+		xhci_ring_free(xhci, virt_dev->eps[ep_index].ring);
+
 	virt_dev->eps[ep_index].ring = NULL;
 }
 
@@ -471,6 +528,7 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
 {
 	struct xhci_container_ctx *ctx;
 	struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
+	struct xhci_vendor_ops *ops = xhci_vendor_get_ops(xhci);
 
 	if ((type != XHCI_CTX_TYPE_DEVICE) && (type != XHCI_CTX_TYPE_INPUT))
 		return NULL;
@@ -484,7 +542,12 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
 	if (type == XHCI_CTX_TYPE_INPUT)
 		ctx->size += CTX_SIZE(xhci->hcc_params);
 
-	ctx->bytes = dma_pool_zalloc(xhci->device_pool, flags, &ctx->dma);
+	if (xhci_vendor_is_usb_offload_enabled(xhci, NULL, 0) &&
+	    (ops && ops->alloc_container_ctx))
+		xhci_vendor_alloc_container_ctx(xhci, ctx, type, flags);
+	else
+		ctx->bytes = dma_pool_zalloc(xhci->device_pool, flags, &ctx->dma);
+
 	if (!ctx->bytes) {
 		kfree(ctx);
 		return NULL;
@@ -495,9 +558,16 @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
 void xhci_free_container_ctx(struct xhci_hcd *xhci,
 			     struct xhci_container_ctx *ctx)
 {
+	struct xhci_vendor_ops *ops = xhci_vendor_get_ops(xhci);
+
 	if (!ctx)
 		return;
-	dma_pool_free(xhci->device_pool, ctx->bytes, ctx->dma);
+	if (xhci_vendor_is_usb_offload_enabled(xhci, NULL, 0) &&
+	    (ops && ops->free_container_ctx))
+		xhci_vendor_free_container_ctx(xhci, ctx);
+	else
+		dma_pool_free(xhci->device_pool, ctx->bytes, ctx->dma);
+
 	kfree(ctx);
 }
 
@@ -519,6 +589,7 @@ struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_hcd *xhci,
 	return (struct xhci_slot_ctx *)
 		(ctx->bytes + CTX_SIZE(xhci->hcc_params));
 }
+EXPORT_SYMBOL_GPL(xhci_get_slot_ctx);
 
 struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci,
 				    struct xhci_container_ctx *ctx,
@@ -532,6 +603,7 @@ struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci,
 	return (struct xhci_ep_ctx *)
 		(ctx->bytes + (ep_index * CTX_SIZE(xhci->hcc_params)));
 }
+EXPORT_SYMBOL_GPL(xhci_get_ep_ctx);
 
 
 /***************** Streams structures manipulation *************************/
@@ -592,23 +664,6 @@ struct xhci_ring *xhci_dma_to_transfer_ring(
 	return ep->ring;
 }
 
-struct xhci_ring *xhci_stream_id_to_ring(
-		struct xhci_virt_device *dev,
-		unsigned int ep_index,
-		unsigned int stream_id)
-{
-	struct xhci_virt_ep *ep = &dev->eps[ep_index];
-
-	if (stream_id == 0)
-		return ep->ring;
-	if (!ep->stream_info)
-		return NULL;
-
-	if (stream_id >= ep->stream_info->num_streams)
-		return NULL;
-	return ep->stream_info->stream_rings[stream_id];
-}
-
 /*
  * Change an endpoint's internal structure so it supports stream IDs.  The
  * number of requested streams includes stream 0, which cannot be used by device
@@ -911,7 +966,7 @@ void xhci_free_virt_device(struct xhci_hcd *xhci, int slot_id)
 
 	for (i = 0; i < 31; i++) {
 		if (dev->eps[i].ring)
-			xhci_ring_free(xhci, dev->eps[i].ring);
+			xhci_free_endpoint_ring(xhci, dev, i);
 		if (dev->eps[i].stream_info)
 			xhci_free_stream_info(xhci,
 					dev->eps[i].stream_info);
@@ -1003,6 +1058,8 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
 	if (!dev)
 		return 0;
 
+	dev->slot_id = slot_id;
+
 	/* Allocate the (output) device context that will be used in the HC. */
 	dev->out_ctx = xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_DEVICE, flags);
 	if (!dev->out_ctx)
@@ -1021,6 +1078,8 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
 
 	/* Initialize the cancellation list and watchdog timers for each ep */
 	for (i = 0; i < 31; i++) {
+		dev->eps[i].ep_index = i;
+		dev->eps[i].vdev = dev;
 		xhci_init_endpoint_timer(xhci, &dev->eps[i]);
 		INIT_LIST_HEAD(&dev->eps[i].cancelled_td_list);
 		INIT_LIST_HEAD(&dev->eps[i].bw_endpoint_list);
@@ -1510,8 +1569,16 @@ int xhci_endpoint_init(struct xhci_hcd *xhci,
 		mult = 0;
 
 	/* Set up the endpoint ring */
-	virt_dev->eps[ep_index].new_ring =
-		xhci_ring_alloc(xhci, 2, 1, ring_type, max_packet, mem_flags);
+	if (xhci_vendor_is_usb_offload_enabled(xhci, virt_dev, ep_index) &&
+	    usb_endpoint_xfer_isoc(&ep->desc)) {
+		virt_dev->eps[ep_index].new_ring =
+			xhci_vendor_alloc_transfer_ring(xhci, endpoint_type, ring_type,
+							max_packet, mem_flags);
+	} else {
+		virt_dev->eps[ep_index].new_ring =
+			xhci_ring_alloc(xhci, 2, 1, ring_type, max_packet, mem_flags);
+	}
+
 	if (!virt_dev->eps[ep_index].new_ring)
 		return -ENOMEM;
 
@@ -1778,6 +1845,7 @@ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
 	INIT_LIST_HEAD(&command->cmd_list);
 	return command;
 }
+EXPORT_SYMBOL_GPL(xhci_alloc_command);
 
 struct xhci_command *xhci_alloc_command_with_ctx(struct xhci_hcd *xhci,
 		bool allocate_completion, gfp_t mem_flags)
@@ -1811,6 +1879,7 @@ void xhci_free_command(struct xhci_hcd *xhci,
 	kfree(command->completion);
 	kfree(command);
 }
+EXPORT_SYMBOL_GPL(xhci_free_command);
 
 int xhci_alloc_erst(struct xhci_hcd *xhci,
 		    struct xhci_ring *evt_ring,
@@ -1841,6 +1910,7 @@ int xhci_alloc_erst(struct xhci_hcd *xhci,
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(xhci_alloc_erst);
 
 void xhci_free_erst(struct xhci_hcd *xhci, struct xhci_erst *erst)
 {
@@ -1854,6 +1924,25 @@ void xhci_free_erst(struct xhci_hcd *xhci, struct xhci_erst *erst)
 				erst->erst_dma_addr);
 	erst->entries = NULL;
 }
+EXPORT_SYMBOL_GPL(xhci_free_erst);
+
+static struct xhci_device_context_array *xhci_vendor_alloc_dcbaa(
+		struct xhci_hcd *xhci, gfp_t flags)
+{
+	struct xhci_vendor_ops *ops = xhci_vendor_get_ops(xhci);
+
+	if (ops && ops->alloc_dcbaa)
+		return ops->alloc_dcbaa(xhci, flags);
+	return 0;
+}
+
+static void xhci_vendor_free_dcbaa(struct xhci_hcd *xhci)
+{
+	struct xhci_vendor_ops *ops = xhci_vendor_get_ops(xhci);
+
+	if (ops && ops->free_dcbaa)
+		ops->free_dcbaa(xhci);
+}
 
 void xhci_mem_cleanup(struct xhci_hcd *xhci)
 {
@@ -1909,9 +1998,13 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
 	xhci_dbg_trace(xhci, trace_xhci_dbg_init,
 			"Freed medium stream array pool");
 
-	if (xhci->dcbaa)
-		dma_free_coherent(dev, sizeof(*xhci->dcbaa),
-				xhci->dcbaa, xhci->dcbaa->dma);
+	if (xhci_vendor_is_usb_offload_enabled(xhci, NULL, 0)) {
+		xhci_vendor_free_dcbaa(xhci);
+	} else {
+		if (xhci->dcbaa)
+			dma_free_coherent(dev, sizeof(*xhci->dcbaa),
+					xhci->dcbaa, xhci->dcbaa->dma);
+	}
 	xhci->dcbaa = NULL;
 
 	scratchpad_free(xhci);
@@ -1992,7 +2085,7 @@ static int xhci_test_trb_in_td(struct xhci_hcd *xhci,
 }
 
 /* TRB math checks for xhci_trb_in_td(), using the command and event rings. */
-static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci)
+int xhci_check_trb_in_td_math(struct xhci_hcd *xhci)
 {
 	struct {
 		dma_addr_t		input_dma;
@@ -2112,6 +2205,7 @@ static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci)
 	xhci_dbg(xhci, "TRB math tests passed.\n");
 	return 0;
 }
+EXPORT_SYMBOL_GPL(xhci_check_trb_in_td_math);
 
 static void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
 {
@@ -2451,15 +2545,21 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
 	 * xHCI section 5.4.6 - doorbell array must be
 	 * "physically contiguous and 64-byte (cache line) aligned".
 	 */
-	xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma,
-			flags);
-	if (!xhci->dcbaa)
-		goto fail;
-	xhci->dcbaa->dma = dma;
+	if (xhci_vendor_is_usb_offload_enabled(xhci, NULL, 0)) {
+		xhci->dcbaa = xhci_vendor_alloc_dcbaa(xhci, flags);
+		if (!xhci->dcbaa)
+			goto fail;
+	} else {
+		xhci->dcbaa = dma_alloc_coherent(dev, sizeof(*xhci->dcbaa), &dma,
+				flags);
+		if (!xhci->dcbaa)
+			goto fail;
+		xhci->dcbaa->dma = dma;
+	}
 	xhci_dbg_trace(xhci, trace_xhci_dbg_init,
 			"// Device context base array address = 0x%llx (DMA), %p (virt)",
 			(unsigned long long)xhci->dcbaa->dma, xhci->dcbaa);
-	xhci_write_64(xhci, dma, &xhci->op_regs->dcbaa_ptr);
+	xhci_write_64(xhci, xhci->dcbaa->dma, &xhci->op_regs->dcbaa_ptr);
 
 	/*
 	 * Initialize the ring segment pool.  The ring must be a contiguous
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 9168b492c..0ee11a937 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -59,7 +59,6 @@
 #define PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI		0x9a13
 #define PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_XHCI		0x1138
 #define PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI		0x51ed
-#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_N_PCH_XHCI	0x54ed
 
 #define PCI_DEVICE_ID_AMD_PROMONTORYA_4			0x43b9
 #define PCI_DEVICE_ID_AMD_PROMONTORYA_3			0x43ba
@@ -243,8 +242,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
 		xhci->quirks |= XHCI_MISSING_CAS;
 
 	if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
-	    (pdev->device == PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI ||
-	     pdev->device == PCI_DEVICE_ID_INTEL_ALDER_LAKE_N_PCH_XHCI))
+	    pdev->device == PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI)
 		xhci->quirks |= XHCI_RESET_TO_DEFAULT;
 
 	if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 972a44b2a..6384c895d 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -134,7 +134,7 @@ static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = {
 };
 
 static const struct xhci_plat_priv xhci_plat_brcm = {
-	.quirks = XHCI_RESET_ON_RESUME | XHCI_SUSPEND_RESUME_CLKS,
+	.quirks = XHCI_RESET_ON_RESUME,
 };
 
 static const struct of_device_id usb_xhci_of_match[] = {
@@ -184,6 +184,41 @@ static const struct of_device_id usb_xhci_of_match[] = {
 MODULE_DEVICE_TABLE(of, usb_xhci_of_match);
 #endif
 
+static struct xhci_plat_priv_overwrite xhci_plat_vendor_overwrite;
+
+int xhci_plat_register_vendor_ops(struct xhci_vendor_ops *vendor_ops)
+{
+	if (vendor_ops == NULL)
+		return -EINVAL;
+
+	xhci_plat_vendor_overwrite.vendor_ops = vendor_ops;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(xhci_plat_register_vendor_ops);
+
+static int xhci_vendor_init(struct xhci_hcd *xhci)
+{
+	struct xhci_vendor_ops *ops = NULL;
+
+	if (xhci_plat_vendor_overwrite.vendor_ops)
+		ops = xhci->vendor_ops = xhci_plat_vendor_overwrite.vendor_ops;
+
+	if (ops && ops->vendor_init)
+		return ops->vendor_init(xhci);
+	return 0;
+}
+
+static void xhci_vendor_cleanup(struct xhci_hcd *xhci)
+{
+	struct xhci_vendor_ops *ops = xhci_vendor_get_ops(xhci);
+
+	if (ops && ops->vendor_cleanup)
+		ops->vendor_cleanup(xhci);
+
+	xhci->vendor_ops = NULL;
+}
+
 static int xhci_plat_probe(struct platform_device *pdev)
 {
 	const struct xhci_plat_priv *priv_match;
@@ -323,6 +358,9 @@ static int xhci_plat_probe(struct platform_device *pdev)
 		if (device_property_read_bool(tmpdev, "quirk-broken-port-ped"))
 			xhci->quirks |= XHCI_BROKEN_PORT_PED;
 
+		if (device_property_read_bool(tmpdev, "quirk-skip-phy-init"))
+			xhci->quirks |= XHCI_SKIP_PHY_INIT;
+
 		device_property_read_u32(tmpdev, "imod-interval-ns",
 					 &xhci->imod_interval);
 	}
@@ -339,6 +377,10 @@ static int xhci_plat_probe(struct platform_device *pdev)
 			goto put_usb3_hcd;
 	}
 
+	ret = xhci_vendor_init(xhci);
+	if (ret)
+		goto disable_usb_phy;
+
 	hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node);
 	xhci->shared_hcd->tpl_support = hcd->tpl_support;
 
@@ -418,8 +460,10 @@ static int xhci_plat_remove(struct platform_device *dev)
 	usb_phy_shutdown(hcd->usb_phy);
 
 	usb_remove_hcd(hcd);
-	usb_put_hcd(shared_hcd);
 
+	xhci_vendor_cleanup(xhci);
+
+	usb_put_hcd(shared_hcd);
 	clk_disable_unprepare(clk);
 	clk_disable_unprepare(reg_clk);
 	usb_put_hcd(hcd);
@@ -437,9 +481,6 @@ static int __maybe_unused xhci_plat_suspend(struct device *dev)
 	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
 	int ret;
 
-	if (pm_runtime_suspended(dev))
-		pm_runtime_resume(dev);
-
 	ret = xhci_priv_suspend_quirk(hcd);
 	if (ret)
 		return ret;
@@ -447,16 +488,7 @@ static int __maybe_unused xhci_plat_suspend(struct device *dev)
 	 * xhci_suspend() needs `do_wakeup` to know whether host is allowed
 	 * to do wakeup during suspend.
 	 */
-	ret = xhci_suspend(xhci, device_may_wakeup(dev));
-	if (ret)
-		return ret;
-
-	if (!device_may_wakeup(dev) && (xhci->quirks & XHCI_SUSPEND_RESUME_CLKS)) {
-		clk_disable_unprepare(xhci->clk);
-		clk_disable_unprepare(xhci->reg_clk);
-	}
-
-	return 0;
+	return xhci_suspend(xhci, device_may_wakeup(dev));
 }
 
 static int __maybe_unused xhci_plat_resume(struct device *dev)
@@ -465,11 +497,6 @@ static int __maybe_unused xhci_plat_resume(struct device *dev)
 	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
 	int ret;
 
-	if (!device_may_wakeup(dev) && (xhci->quirks & XHCI_SUSPEND_RESUME_CLKS)) {
-		clk_prepare_enable(xhci->clk);
-		clk_prepare_enable(xhci->reg_clk);
-	}
-
 	ret = xhci_priv_resume_quirk(hcd);
 	if (ret)
 		return ret;
diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h
index 561d0b7bc..e726a5723 100644
--- a/drivers/usb/host/xhci-plat.h
+++ b/drivers/usb/host/xhci-plat.h
@@ -13,6 +13,7 @@
 struct xhci_plat_priv {
 	const char *firmware_name;
 	unsigned long long quirks;
+	struct xhci_vendor_data *vendor_data;
 	int (*plat_setup)(struct usb_hcd *);
 	void (*plat_start)(struct usb_hcd *);
 	int (*init_quirk)(struct usb_hcd *);
@@ -22,4 +23,11 @@ struct xhci_plat_priv {
 
 #define hcd_to_xhci_priv(h) ((struct xhci_plat_priv *)hcd_to_xhci(h)->priv)
 #define xhci_to_priv(x) ((struct xhci_plat_priv *)(x)->priv)
+
+struct xhci_plat_priv_overwrite {
+	struct xhci_vendor_ops *vendor_ops;
+};
+
+int xhci_plat_register_vendor_ops(struct xhci_vendor_ops *vendor_ops);
+
 #endif	/* _XHCI_PLAT_H */
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index fa3a7ac15..99d95d516 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -57,7 +57,10 @@
 #include <linux/dma-mapping.h>
 #include "xhci.h"
 #include "xhci-trace.h"
-#include "xhci-mtk.h"
+
+static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd,
+			 u32 field1, u32 field2,
+			 u32 field3, u32 field4, bool command_must_succeed);
 
 /*
  * Returns zero if the TRB isn't in this segment, otherwise it returns the DMA
@@ -76,6 +79,7 @@ dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg,
 		return 0;
 	return seg->dma + (segment_offset * sizeof(*trb));
 }
+EXPORT_SYMBOL_GPL(xhci_trb_virt_to_dma);
 
 static bool trb_is_noop(union xhci_trb *trb)
 {
@@ -151,10 +155,11 @@ static void next_trb(struct xhci_hcd *xhci,
 
 /*
  * See Cycle bit rules. SW is the consumer for the event ring only.
- * Don't make a ring full of link TRBs.  That would be dumb and this would loop.
  */
 void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
 {
+	unsigned int link_trb_count = 0;
+
 	/* event ring doesn't have link trbs, check for last trb */
 	if (ring->type == TYPE_EVENT) {
 		if (!last_trb_on_seg(ring->deq_seg, ring->dequeue)) {
@@ -170,14 +175,23 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
 
 	/* All other rings have link trbs */
 	if (!trb_is_link(ring->dequeue)) {
-		ring->dequeue++;
-		ring->num_trbs_free++;
+		if (last_trb_on_seg(ring->deq_seg, ring->dequeue)) {
+			xhci_warn(xhci, "Missing link TRB at end of segment\n");
+		} else {
+			ring->dequeue++;
+			ring->num_trbs_free++;
+		}
 	}
+
 	while (trb_is_link(ring->dequeue)) {
 		ring->deq_seg = ring->deq_seg->next;
 		ring->dequeue = ring->deq_seg->trbs;
-	}
 
+		if (link_trb_count++ > ring->num_segs) {
+			xhci_warn(xhci, "Ring is an endless link TRB loop\n");
+			break;
+		}
+	}
 out:
 	trace_xhci_inc_deq(ring);
 
@@ -186,7 +200,6 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
 
 /*
  * See Cycle bit rules. SW is the consumer for the event ring only.
- * Don't make a ring full of link TRBs.  That would be dumb and this would loop.
  *
  * If we've just enqueued a TRB that is in the middle of a TD (meaning the
  * chain bit is set), then set the chain bit in all the following link TRBs.
@@ -206,11 +219,18 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
 {
 	u32 chain;
 	union xhci_trb *next;
+	unsigned int link_trb_count = 0;
 
 	chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN;
 	/* If this is not event ring, there is one less usable TRB */
 	if (!trb_is_link(ring->enqueue))
 		ring->num_trbs_free--;
+
+	if (last_trb_on_seg(ring->enq_seg, ring->enqueue)) {
+		xhci_err(xhci, "Tried to move enqueue past ring segment\n");
+		return;
+	}
+
 	next = ++(ring->enqueue);
 
 	/* Update the dequeue pointer further if that was a link TRB */
@@ -247,6 +267,11 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
 		ring->enq_seg = ring->enq_seg->next;
 		ring->enqueue = ring->enq_seg->trbs;
 		next = ring->enqueue;
+
+		if (link_trb_count++ > ring->num_segs) {
+			xhci_warn(xhci, "%s: Ring link TRB loop\n", __func__);
+			break;
+		}
 	}
 
 	trace_xhci_inc_enq(ring);
@@ -287,6 +312,7 @@ void xhci_ring_cmd_db(struct xhci_hcd *xhci)
 	/* Flush PCI posted writes */
 	readl(&xhci->dba->doorbell[0]);
 }
+EXPORT_SYMBOL_GPL(xhci_ring_cmd_db);
 
 static bool xhci_mod_cmd_timer(struct xhci_hcd *xhci, unsigned long delay)
 {
@@ -342,9 +368,7 @@ static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci,
 /* Must be called with xhci->lock held, releases and aquires lock back */
 static int xhci_abort_cmd_ring(struct xhci_hcd *xhci, unsigned long flags)
 {
-	struct xhci_segment *new_seg	= xhci->cmd_ring->deq_seg;
-	union xhci_trb *new_deq		= xhci->cmd_ring->dequeue;
-	u64 crcr;
+	u32 temp_32;
 	int ret;
 
 	xhci_dbg(xhci, "Abort command ring\n");
@@ -353,18 +377,13 @@ static int xhci_abort_cmd_ring(struct xhci_hcd *xhci, unsigned long flags)
 
 	/*
 	 * The control bits like command stop, abort are located in lower
-	 * dword of the command ring control register.
-	 * Some controllers require all 64 bits to be written to abort the ring.
-	 * Make sure the upper dword is valid, pointing to the next command,
-	 * avoiding corrupting the command ring pointer in case the command ring
-	 * is stopped by the time the upper dword is written.
+	 * dword of the command ring control register. Limit the write
+	 * to the lower dword to avoid corrupting the command ring pointer
+	 * in case if the command ring is stopped by the time upper dword
+	 * is written.
 	 */
-	next_trb(xhci, NULL, &new_seg, &new_deq);
-	if (trb_is_link(new_deq))
-		next_trb(xhci, NULL, &new_seg, &new_deq);
-
-	crcr = xhci_trb_virt_to_dma(new_seg, new_deq);
-	xhci_write_64(xhci, crcr | CMD_RING_ABORT, &xhci->op_regs->cmd_ring);
+	temp_32 = readl(&xhci->op_regs->cmd_ring);
+	writel(temp_32 | CMD_RING_ABORT, &xhci->op_regs->cmd_ring);
 
 	/* Section 4.6.1.2 of xHCI 1.0 spec says software should also time the
 	 * completion of the Command Abort operation. If CRR is not negated in 5
@@ -421,9 +440,8 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci,
 	trace_xhci_ring_ep_doorbell(slot_id, DB_VALUE(ep_index, stream_id));
 
 	writel(DB_VALUE(ep_index, stream_id), db_addr);
-	/* The CPU has better things to do at this point than wait for a
-	 * write-posting flush.  It'll get there soon enough.
-	 */
+	/* flush the write */
+	readl(db_addr);
 }
 
 /* Ring the doorbell for any rings with pending URBs */
@@ -479,6 +497,26 @@ static struct xhci_virt_ep *xhci_get_virt_ep(struct xhci_hcd *xhci,
 	return &xhci->devs[slot_id]->eps[ep_index];
 }
 
+static struct xhci_ring *xhci_virt_ep_to_ring(struct xhci_hcd *xhci,
+					      struct xhci_virt_ep *ep,
+					      unsigned int stream_id)
+{
+	/* common case, no streams */
+	if (!(ep->ep_state & EP_HAS_STREAMS))
+		return ep->ring;
+
+	if (!ep->stream_info)
+		return NULL;
+
+	if (stream_id == 0 || stream_id >= ep->stream_info->num_streams) {
+		xhci_warn(xhci, "Invalid stream_id %u request for slot_id %u ep_index %u\n",
+			  stream_id, ep->vdev->slot_id, ep->ep_index);
+		return NULL;
+	}
+
+	return ep->stream_info->stream_rings[stream_id];
+}
+
 /* Get the right ring for the given slot_id, ep_index and stream_id.
  * If the endpoint supports streams, boundary check the URB's stream ID.
  * If the endpoint doesn't support streams, return the singular endpoint ring.
@@ -493,29 +531,7 @@ struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci,
 	if (!ep)
 		return NULL;
 
-	/* Common case: no streams */
-	if (!(ep->ep_state & EP_HAS_STREAMS))
-		return ep->ring;
-
-	if (stream_id == 0) {
-		xhci_warn(xhci,
-				"WARN: Slot ID %u, ep index %u has streams, "
-				"but URB has no stream ID.\n",
-				slot_id, ep_index);
-		return NULL;
-	}
-
-	if (stream_id < ep->stream_info->num_streams)
-		return ep->stream_info->stream_rings[stream_id];
-
-	xhci_warn(xhci,
-			"WARN: Slot ID %u, ep index %u has "
-			"stream IDs 1 to %u allocated, "
-			"but stream ID %u is requested.\n",
-			slot_id, ep_index,
-			ep->stream_info->num_streams - 1,
-			stream_id);
-	return NULL;
+	return xhci_virt_ep_to_ring(xhci, ep, stream_id);
 }
 
 
@@ -542,97 +558,55 @@ static u64 xhci_get_hw_deq(struct xhci_hcd *xhci, struct xhci_virt_device *vdev,
 	return le64_to_cpu(ep_ctx->deq);
 }
 
-/*
- * Move the xHC's endpoint ring dequeue pointer past cur_td.
- * Record the new state of the xHC's endpoint ring dequeue segment,
- * dequeue pointer, stream id, and new consumer cycle state in state.
- * Update our internal representation of the ring's dequeue pointer.
- *
- * We do this in three jumps:
- *  - First we update our new ring state to be the same as when the xHC stopped.
- *  - Then we traverse the ring to find the segment that contains
- *    the last TRB in the TD.  We toggle the xHC's new cycle state when we pass
- *    any link TRBs with the toggle cycle bit set.
- *  - Finally we move the dequeue state one TRB further, toggling the cycle bit
- *    if we've moved it past a link TRB with the toggle cycle bit set.
- *
- * Some of the uses of xhci_generic_trb are grotty, but if they're done
- * with correct __le32 accesses they should work fine.  Only users of this are
- * in here.
- */
-void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
-		unsigned int slot_id, unsigned int ep_index,
-		unsigned int stream_id, struct xhci_td *cur_td,
-		struct xhci_dequeue_state *state)
+static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
+				unsigned int slot_id, unsigned int ep_index,
+				unsigned int stream_id, struct xhci_td *td)
 {
 	struct xhci_virt_device *dev = xhci->devs[slot_id];
 	struct xhci_virt_ep *ep = &dev->eps[ep_index];
 	struct xhci_ring *ep_ring;
+	struct xhci_command *cmd;
 	struct xhci_segment *new_seg;
-	struct xhci_segment *halted_seg = NULL;
 	union xhci_trb *new_deq;
-	union xhci_trb *halted_trb;
-	int index = 0;
+	int new_cycle;
 	dma_addr_t addr;
 	u64 hw_dequeue;
 	bool cycle_found = false;
 	bool td_last_trb_found = false;
+	u32 trb_sct = 0;
+	int ret;
 
 	ep_ring = xhci_triad_to_transfer_ring(xhci, slot_id,
 			ep_index, stream_id);
 	if (!ep_ring) {
-		xhci_warn(xhci, "WARN can't find new dequeue state "
-				"for invalid stream ID %u.\n",
-				stream_id);
-		return;
+		xhci_warn(xhci, "WARN can't find new dequeue, invalid stream ID %u\n",
+			  stream_id);
+		return -ENODEV;
 	}
 	/*
 	 * A cancelled TD can complete with a stall if HW cached the trb.
-	 * In this case driver can't find cur_td, but if the ring is empty we
+	 * In this case driver can't find td, but if the ring is empty we
 	 * can move the dequeue pointer to the current enqueue position.
+	 * We shouldn't hit this anymore as cached cancelled TRBs are given back
+	 * after clearing the cache, but be on the safe side and keep it anyway
 	 */
-	if (!cur_td) {
+	if (!td) {
 		if (list_empty(&ep_ring->td_list)) {
-			state->new_deq_seg = ep_ring->enq_seg;
-			state->new_deq_ptr = ep_ring->enqueue;
-			state->new_cycle_state = ep_ring->cycle_state;
-			goto done;
+			new_seg = ep_ring->enq_seg;
+			new_deq = ep_ring->enqueue;
+			new_cycle = ep_ring->cycle_state;
+			xhci_dbg(xhci, "ep ring empty, Set new dequeue = enqueue");
+			goto deq_found;
 		} else {
-			xhci_warn(xhci, "Can't find new dequeue state, missing cur_td\n");
-			return;
+			xhci_warn(xhci, "Can't find new dequeue state, missing td\n");
+			return -EINVAL;
 		}
 	}
 
-	/* Dig out the cycle state saved by the xHC during the stop ep cmd */
-	xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
-			"Finding endpoint context");
-
 	hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id);
 	new_seg = ep_ring->deq_seg;
 	new_deq = ep_ring->dequeue;
-
-	/*
-	 * Quirk: xHC write-back of the DCS field in the hardware dequeue
-	 * pointer is wrong - use the cycle state of the TRB pointed to by
-	 * the dequeue pointer.
-	 */
-	if (xhci->quirks & XHCI_EP_CTX_BROKEN_DCS &&
-	    !(ep->ep_state & EP_HAS_STREAMS))
-		halted_seg = trb_in_td(xhci, cur_td->start_seg,
-				       cur_td->first_trb, cur_td->last_trb,
-				       hw_dequeue & ~0xf, false);
-	if (halted_seg) {
-		index = ((dma_addr_t)(hw_dequeue & ~0xf) - halted_seg->dma) /
-			 sizeof(*halted_trb);
-		halted_trb = &halted_seg->trbs[index];
-		state->new_cycle_state = halted_trb->generic.field[3] & 0x1;
-		xhci_dbg(xhci, "Endpoint DCS = %d TRB index = %d cycle = %d\n",
-			 (u8)(hw_dequeue & 0x1), index,
-			 state->new_cycle_state);
-	} else {
-		state->new_cycle_state = hw_dequeue & 0x1;
-	}
-	state->stream_id = stream_id;
+	new_cycle = hw_dequeue & 0x1;
 
 	/*
 	 * We want to find the pointer, segment and cycle state of the new trb
@@ -647,40 +621,71 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
 			if (td_last_trb_found)
 				break;
 		}
-		if (new_deq == cur_td->last_trb)
+		if (new_deq == td->last_trb)
 			td_last_trb_found = true;
 
 		if (cycle_found && trb_is_link(new_deq) &&
 		    link_trb_toggles_cycle(new_deq))
-			state->new_cycle_state ^= 0x1;
+			new_cycle ^= 0x1;
 
 		next_trb(xhci, ep_ring, &new_seg, &new_deq);
 
 		/* Search wrapped around, bail out */
 		if (new_deq == ep->ring->dequeue) {
 			xhci_err(xhci, "Error: Failed finding new dequeue state\n");
-			state->new_deq_seg = NULL;
-			state->new_deq_ptr = NULL;
-			return;
+			return -EINVAL;
 		}
 
 	} while (!cycle_found || !td_last_trb_found);
 
-	state->new_deq_seg = new_seg;
-	state->new_deq_ptr = new_deq;
+deq_found:
 
-done:
 	/* Don't update the ring cycle state for the producer (us). */
-	xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
-			"Cycle state = 0x%x", state->new_cycle_state);
+	addr = xhci_trb_virt_to_dma(new_seg, new_deq);
+	if (addr == 0) {
+		xhci_warn(xhci, "Can't find dma of new dequeue ptr\n");
+		xhci_warn(xhci, "deq seg = %p, deq ptr = %p\n", new_seg, new_deq);
+		return -EINVAL;
+	}
+
+	if ((ep->ep_state & SET_DEQ_PENDING)) {
+		xhci_warn(xhci, "Set TR Deq already pending, don't submit for 0x%pad\n",
+			  &addr);
+		return -EBUSY;
+	}
+
+	/* This function gets called from contexts where it cannot sleep */
+	cmd = xhci_alloc_command(xhci, false, GFP_ATOMIC);
+	if (!cmd) {
+		xhci_warn(xhci, "Can't alloc Set TR Deq cmd 0x%pad\n", &addr);
+		return -ENOMEM;
+	}
+
+	if (stream_id)
+		trb_sct = SCT_FOR_TRB(SCT_PRI_TR);
+	ret = queue_command(xhci, cmd,
+		lower_32_bits(addr) | trb_sct | new_cycle,
+		upper_32_bits(addr),
+		STREAM_ID_FOR_TRB(stream_id), SLOT_ID_FOR_TRB(slot_id) |
+		EP_ID_FOR_TRB(ep_index) | TRB_TYPE(TRB_SET_DEQ), false);
+	if (ret < 0) {
+		xhci_free_command(xhci, cmd);
+		return ret;
+	}
+	ep->queued_deq_seg = new_seg;
+	ep->queued_deq_ptr = new_deq;
 
 	xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
-			"New dequeue segment = %p (virtual)",
-			state->new_deq_seg);
-	addr = xhci_trb_virt_to_dma(state->new_deq_seg, state->new_deq_ptr);
-	xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
-			"New dequeue pointer = 0x%llx (DMA)",
-			(unsigned long long) addr);
+		       "Set TR Deq ptr 0x%llx, cycle %u\n", addr, new_cycle);
+
+	/* Stop the TD queueing code from ringing the doorbell until
+	 * this command completes.  The HC won't set the dequeue pointer
+	 * if the ring is running, and ringing the doorbell starts the
+	 * ring running.
+	 */
+	ep->ep_state |= SET_DEQ_PENDING;
+	xhci_ring_cmd_db(xhci);
+	return 0;
 }
 
 /* flip_cycle means flip the cycle bit of all but the first and last TRB.
@@ -773,153 +778,326 @@ static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci,
 	seg->bounce_offs = 0;
 }
 
-/*
- * When we get a command completion for a Stop Endpoint Command, we need to
- * unlink any cancelled TDs from the ring.  There are two ways to do that:
- *
- *  1. If the HW was in the middle of processing the TD that needs to be
- *     cancelled, then we must move the ring's dequeue pointer past the last TRB
- *     in the TD with a Set Dequeue Pointer Command.
- *  2. Otherwise, we turn all the TRBs in the TD into No-op TRBs (with the chain
- *     bit cleared) so that the HW will skip over them.
- */
-static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
-		union xhci_trb *trb, struct xhci_event_cmd *event)
+static int xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td,
+			   struct xhci_ring *ep_ring, int status)
 {
-	unsigned int ep_index;
-	struct xhci_ring *ep_ring;
-	struct xhci_virt_ep *ep;
-	struct xhci_td *cur_td = NULL;
-	struct xhci_td *last_unlinked_td;
-	struct xhci_ep_ctx *ep_ctx;
-	struct xhci_virt_device *vdev;
-	u64 hw_deq;
-	struct xhci_dequeue_state deq_state;
+	struct urb *urb = NULL;
 
-	if (unlikely(TRB_TO_SUSPEND_PORT(le32_to_cpu(trb->generic.field[3])))) {
-		if (!xhci->devs[slot_id])
-			xhci_warn(xhci, "Stop endpoint command "
-				"completion for disabled slot %u\n",
-				slot_id);
-		return;
+	/* Clean up the endpoint's TD list */
+	urb = td->urb;
+
+	/* if a bounce buffer was used to align this td then unmap it */
+	xhci_unmap_td_bounce_buffer(xhci, ep_ring, td);
+
+	/* Do one last check of the actual transfer length.
+	 * If the host controller said we transferred more data than the buffer
+	 * length, urb->actual_length will be a very big number (since it's
+	 * unsigned).  Play it safe and say we didn't transfer anything.
+	 */
+	if (urb->actual_length > urb->transfer_buffer_length) {
+		xhci_warn(xhci, "URB req %u and actual %u transfer length mismatch\n",
+			  urb->transfer_buffer_length, urb->actual_length);
+		urb->actual_length = 0;
+		status = 0;
 	}
+	/* TD might be removed from td_list if we are giving back a cancelled URB */
+	if (!list_empty(&td->td_list))
+		list_del_init(&td->td_list);
+	/* Giving back a cancelled URB, or if a slated TD completed anyway */
+	if (!list_empty(&td->cancelled_td_list))
+		list_del_init(&td->cancelled_td_list);
 
-	memset(&deq_state, 0, sizeof(deq_state));
-	ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
+	inc_td_cnt(urb);
+	/* Giveback the urb when all the tds are completed */
+	if (last_td_in_urb(td)) {
+		if ((urb->actual_length != urb->transfer_buffer_length &&
+		     (urb->transfer_flags & URB_SHORT_NOT_OK)) ||
+		    (status != 0 && !usb_endpoint_xfer_isoc(&urb->ep->desc)))
+			xhci_dbg(xhci, "Giveback URB %p, len = %d, expected = %d, status = %d\n",
+				 urb, urb->actual_length,
+				 urb->transfer_buffer_length, status);
 
-	ep = xhci_get_virt_ep(xhci, slot_id, ep_index);
-	if (!ep)
-		return;
+		/* set isoc urb status to 0 just as EHCI, UHCI, and OHCI */
+		if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
+			status = 0;
+		xhci_giveback_urb_in_irq(xhci, td, status);
+	}
 
-	vdev = xhci->devs[slot_id];
-	ep_ctx = xhci_get_ep_ctx(xhci, vdev->out_ctx, ep_index);
-	trace_xhci_handle_cmd_stop_ep(ep_ctx);
+	return 0;
+}
 
-	last_unlinked_td = list_last_entry(&ep->cancelled_td_list,
-			struct xhci_td, cancelled_td_list);
 
-	if (list_empty(&ep->cancelled_td_list)) {
-		xhci_stop_watchdog_timer_in_irq(xhci, ep);
-		ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
-		return;
+/* Complete the cancelled URBs we unlinked from td_list. */
+static void xhci_giveback_invalidated_tds(struct xhci_virt_ep *ep)
+{
+	struct xhci_ring *ring;
+	struct xhci_td *td, *tmp_td;
+
+	list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list,
+				 cancelled_td_list) {
+
+		ring = xhci_urb_to_transfer_ring(ep->xhci, td->urb);
+
+		if (td->cancel_status == TD_CLEARED)
+			xhci_td_cleanup(ep->xhci, td, ring, td->status);
+
+		if (ep->xhci->xhc_state & XHCI_STATE_DYING)
+			return;
 	}
+}
+
+static int xhci_reset_halted_ep(struct xhci_hcd *xhci, unsigned int slot_id,
+				unsigned int ep_index, enum xhci_ep_reset_type reset_type)
+{
+	struct xhci_command *command;
+	int ret = 0;
 
-	/* Fix up the ep ring first, so HW stops executing cancelled TDs.
-	 * We have the xHCI lock, so nothing can modify this list until we drop
-	 * it.  We're also in the event handler, so we can't get re-interrupted
-	 * if another Stop Endpoint command completes
+	command = xhci_alloc_command(xhci, false, GFP_ATOMIC);
+	if (!command) {
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	ret = xhci_queue_reset_ep(xhci, command, slot_id, ep_index, reset_type);
+done:
+	if (ret)
+		xhci_err(xhci, "ERROR queuing reset endpoint for slot %d ep_index %d, %d\n",
+			 slot_id, ep_index, ret);
+	return ret;
+}
+
+static int xhci_handle_halted_endpoint(struct xhci_hcd *xhci,
+				struct xhci_virt_ep *ep, unsigned int stream_id,
+				struct xhci_td *td,
+				enum xhci_ep_reset_type reset_type)
+{
+	unsigned int slot_id = ep->vdev->slot_id;
+	int err;
+
+	/*
+	 * Avoid resetting endpoint if link is inactive. Can cause host hang.
+	 * Device will be reset soon to recover the link so don't do anything
 	 */
-	list_for_each_entry(cur_td, &ep->cancelled_td_list, cancelled_td_list) {
+	if (ep->vdev->flags & VDEV_PORT_ERROR)
+		return -ENODEV;
+
+	/* add td to cancelled list and let reset ep handler take care of it */
+	if (reset_type == EP_HARD_RESET) {
+		ep->ep_state |= EP_HARD_CLEAR_TOGGLE;
+		if (td && list_empty(&td->cancelled_td_list)) {
+			list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list);
+			td->cancel_status = TD_HALTED;
+		}
+	}
+
+	if (ep->ep_state & EP_HALTED) {
+		xhci_dbg(xhci, "Reset ep command already pending\n");
+		return 0;
+	}
+
+	err = xhci_reset_halted_ep(xhci, slot_id, ep->ep_index, reset_type);
+	if (err)
+		return err;
+
+	ep->ep_state |= EP_HALTED;
+
+	xhci_ring_cmd_db(xhci);
+
+	return 0;
+}
+
+/*
+ * Fix up the ep ring first, so HW stops executing cancelled TDs.
+ * We have the xHCI lock, so nothing can modify this list until we drop it.
+ * We're also in the event handler, so we can't get re-interrupted if another
+ * Stop Endpoint command completes.
+ *
+ * only call this when ring is not in a running state
+ */
+
+static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
+{
+	struct xhci_hcd		*xhci;
+	struct xhci_td		*td = NULL;
+	struct xhci_td		*tmp_td = NULL;
+	struct xhci_td		*cached_td = NULL;
+	struct xhci_ring	*ring;
+	u64			hw_deq;
+	unsigned int		slot_id = ep->vdev->slot_id;
+	int			err;
+
+	xhci = ep->xhci;
+
+	list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) {
 		xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
 				"Removing canceled TD starting at 0x%llx (dma).",
 				(unsigned long long)xhci_trb_virt_to_dma(
-					cur_td->start_seg, cur_td->first_trb));
-		ep_ring = xhci_urb_to_transfer_ring(xhci, cur_td->urb);
-		if (!ep_ring) {
-			/* This shouldn't happen unless a driver is mucking
-			 * with the stream ID after submission.  This will
-			 * leave the TD on the hardware ring, and the hardware
-			 * will try to execute it, and may access a buffer
-			 * that has already been freed.  In the best case, the
-			 * hardware will execute it, and the event handler will
-			 * ignore the completion event for that TD, since it was
-			 * removed from the td_list for that endpoint.  In
-			 * short, don't muck with the stream ID after
-			 * submission.
-			 */
-			xhci_warn(xhci, "WARN Cancelled URB %p "
-					"has invalid stream ID %u.\n",
-					cur_td->urb,
-					cur_td->urb->stream_id);
-			goto remove_finished_td;
+					td->start_seg, td->first_trb));
+		list_del_init(&td->td_list);
+		ring = xhci_urb_to_transfer_ring(xhci, td->urb);
+		if (!ring) {
+			xhci_warn(xhci, "WARN Cancelled URB %p has invalid stream ID %u.\n",
+				  td->urb, td->urb->stream_id);
+			continue;
 		}
 		/*
-		 * If we stopped on the TD we need to cancel, then we have to
+		 * If a ring stopped on the TD we need to cancel then we have to
 		 * move the xHC endpoint ring dequeue pointer past this TD.
+		 * Rings halted due to STALL may show hw_deq is past the stalled
+		 * TD, but still require a set TR Deq command to flush xHC cache.
 		 */
-		hw_deq = xhci_get_hw_deq(xhci, vdev, ep_index,
-					 cur_td->urb->stream_id);
+		hw_deq = xhci_get_hw_deq(xhci, ep->vdev, ep->ep_index,
+					 td->urb->stream_id);
 		hw_deq &= ~0xf;
 
-		if (trb_in_td(xhci, cur_td->start_seg, cur_td->first_trb,
-			      cur_td->last_trb, hw_deq, false)) {
-			xhci_find_new_dequeue_state(xhci, slot_id, ep_index,
-						    cur_td->urb->stream_id,
-						    cur_td, &deq_state);
+		if (td->cancel_status == TD_HALTED) {
+			cached_td = td;
+		} else if (trb_in_td(xhci, td->start_seg, td->first_trb,
+			      td->last_trb, hw_deq, false)) {
+			switch (td->cancel_status) {
+			case TD_CLEARED: /* TD is already no-op */
+			case TD_CLEARING_CACHE: /* set TR deq command already queued */
+				break;
+			case TD_DIRTY: /* TD is cached, clear it */
+			case TD_HALTED:
+				/* FIXME  stream case, several stopped rings */
+				cached_td = td;
+				break;
+			}
 		} else {
-			td_to_noop(xhci, ep_ring, cur_td, false);
+			td_to_noop(xhci, ring, td, false);
+			td->cancel_status = TD_CLEARED;
 		}
+	}
+	if (cached_td) {
+		cached_td->cancel_status = TD_CLEARING_CACHE;
+
+		err = xhci_move_dequeue_past_td(xhci, slot_id, ep->ep_index,
+						cached_td->urb->stream_id,
+						cached_td);
+		/* Failed to move past cached td, try just setting it noop */
+		if (err) {
+			td_to_noop(xhci, ring, cached_td, false);
+			cached_td->cancel_status = TD_CLEARED;
+		}
+		cached_td = NULL;
+	}
+	return 0;
+}
 
-remove_finished_td:
-		/*
-		 * The event handler won't see a completion for this TD anymore,
-		 * so remove it from the endpoint ring's TD list.  Keep it in
-		 * the cancelled TD list for URB completion later.
-		 */
-		list_del_init(&cur_td->td_list);
+/*
+ * Returns the TD the endpoint ring halted on.
+ * Only call for non-running rings without streams.
+ */
+static struct xhci_td *find_halted_td(struct xhci_virt_ep *ep)
+{
+	struct xhci_td	*td;
+	u64		hw_deq;
+
+	if (!list_empty(&ep->ring->td_list)) { /* Not streams compatible */
+		hw_deq = xhci_get_hw_deq(ep->xhci, ep->vdev, ep->ep_index, 0);
+		hw_deq &= ~0xf;
+		td = list_first_entry(&ep->ring->td_list, struct xhci_td, td_list);
+		if (trb_in_td(ep->xhci, td->start_seg, td->first_trb,
+				td->last_trb, hw_deq, false))
+			return td;
 	}
+	return NULL;
+}
 
-	xhci_stop_watchdog_timer_in_irq(xhci, ep);
+/*
+ * When we get a command completion for a Stop Endpoint Command, we need to
+ * unlink any cancelled TDs from the ring.  There are two ways to do that:
+ *
+ *  1. If the HW was in the middle of processing the TD that needs to be
+ *     cancelled, then we must move the ring's dequeue pointer past the last TRB
+ *     in the TD with a Set Dequeue Pointer Command.
+ *  2. Otherwise, we turn all the TRBs in the TD into No-op TRBs (with the chain
+ *     bit cleared) so that the HW will skip over them.
+ */
+static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
+				    union xhci_trb *trb, u32 comp_code)
+{
+	unsigned int ep_index;
+	struct xhci_virt_ep *ep;
+	struct xhci_ep_ctx *ep_ctx;
+	struct xhci_td *td = NULL;
+	enum xhci_ep_reset_type reset_type;
+	struct xhci_command *command;
+	int err;
 
-	/* If necessary, queue a Set Transfer Ring Dequeue Pointer command */
-	if (deq_state.new_deq_ptr && deq_state.new_deq_seg) {
-		xhci_queue_new_dequeue_state(xhci, slot_id, ep_index,
-					     &deq_state);
-		xhci_ring_cmd_db(xhci);
-	} else {
-		/* Otherwise ring the doorbell(s) to restart queued transfers */
-		ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
+	if (unlikely(TRB_TO_SUSPEND_PORT(le32_to_cpu(trb->generic.field[3])))) {
+		if (!xhci->devs[slot_id])
+			xhci_warn(xhci, "Stop endpoint command completion for disabled slot %u\n",
+				  slot_id);
+		return;
 	}
 
+	ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
+	ep = xhci_get_virt_ep(xhci, slot_id, ep_index);
+	if (!ep)
+		return;
+
+	ep_ctx = xhci_get_ep_ctx(xhci, ep->vdev->out_ctx, ep_index);
+
+	trace_xhci_handle_cmd_stop_ep(ep_ctx);
+
+	if (comp_code == COMP_CONTEXT_STATE_ERROR) {
 	/*
-	 * Drop the lock and complete the URBs in the cancelled TD list.
-	 * New TDs to be cancelled might be added to the end of the list before
-	 * we can complete all the URBs for the TDs we already unlinked.
-	 * So stop when we've completed the URB for the last TD we unlinked.
+	 * If stop endpoint command raced with a halting endpoint we need to
+	 * reset the host side endpoint first.
+	 * If the TD we halted on isn't cancelled the TD should be given back
+	 * with a proper error code, and the ring dequeue moved past the TD.
+	 * If streams case we can't find hw_deq, or the TD we halted on so do a
+	 * soft reset.
+	 *
+	 * Proper error code is unknown here, it would be -EPIPE if device side
+	 * of enadpoit halted (aka STALL), and -EPROTO if not (transaction error)
+	 * We use -EPROTO, if device is stalled it should return a stall error on
+	 * next transfer, which then will return -EPIPE, and device side stall is
+	 * noted and cleared by class driver.
 	 */
-	do {
-		cur_td = list_first_entry(&ep->cancelled_td_list,
-				struct xhci_td, cancelled_td_list);
-		list_del_init(&cur_td->cancelled_td_list);
+		switch (GET_EP_CTX_STATE(ep_ctx)) {
+		case EP_STATE_HALTED:
+			xhci_dbg(xhci, "Stop ep completion raced with stall, reset ep\n");
+			if (ep->ep_state & EP_HAS_STREAMS) {
+				reset_type = EP_SOFT_RESET;
+			} else {
+				reset_type = EP_HARD_RESET;
+				td = find_halted_td(ep);
+				if (td)
+					td->status = -EPROTO;
+			}
+			/* reset ep, reset handler cleans up cancelled tds */
+			err = xhci_handle_halted_endpoint(xhci, ep, 0, td,
+							  reset_type);
+			if (err)
+				break;
+			xhci_stop_watchdog_timer_in_irq(xhci, ep);
+			return;
+		case EP_STATE_RUNNING:
+			/* Race, HW handled stop ep cmd before ep was running */
+			command = xhci_alloc_command(xhci, false, GFP_ATOMIC);
+			if (!command)
+				xhci_stop_watchdog_timer_in_irq(xhci, ep);
 
-		/* Clean up the cancelled URB */
-		/* Doesn't matter what we pass for status, since the core will
-		 * just overwrite it (because the URB has been unlinked).
-		 */
-		ep_ring = xhci_urb_to_transfer_ring(xhci, cur_td->urb);
-		xhci_unmap_td_bounce_buffer(xhci, ep_ring, cur_td);
-		inc_td_cnt(cur_td->urb);
-		if (last_td_in_urb(cur_td))
-			xhci_giveback_urb_in_irq(xhci, cur_td, 0);
+			mod_timer(&ep->stop_cmd_timer,
+				  jiffies + XHCI_STOP_EP_CMD_TIMEOUT * HZ);
+			xhci_queue_stop_endpoint(xhci, command, slot_id, ep_index, 0);
+			xhci_ring_cmd_db(xhci);
 
-		/* Stop processing the cancelled list if the watchdog timer is
-		 * running.
-		 */
-		if (xhci->xhc_state & XHCI_STATE_DYING)
 			return;
-	} while (cur_td != last_unlinked_td);
+		default:
+			break;
+		}
+	}
+	/* will queue a set TR deq if stopped on a cancelled, uncleared TD */
+	xhci_invalidate_cancelled_tds(ep);
+	xhci_stop_watchdog_timer_in_irq(xhci, ep);
 
-	/* Return to the event handler with xhci->lock re-acquired */
+	/* Otherwise ring the doorbell(s) to restart queued transfers */
+	xhci_giveback_invalidated_tds(ep);
+	ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
 }
 
 static void xhci_kill_ring_urbs(struct xhci_hcd *xhci, struct xhci_ring *ring)
@@ -1133,10 +1311,10 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
 	unsigned int ep_index;
 	unsigned int stream_id;
 	struct xhci_ring *ep_ring;
-	struct xhci_virt_device *dev;
 	struct xhci_virt_ep *ep;
 	struct xhci_ep_ctx *ep_ctx;
 	struct xhci_slot_ctx *slot_ctx;
+	struct xhci_td *td, *tmp_td;
 
 	ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
 	stream_id = TRB_TO_STREAM_ID(le32_to_cpu(trb->generic.field[2]));
@@ -1144,8 +1322,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
 	if (!ep)
 		return;
 
-	dev = xhci->devs[slot_id];
-	ep_ring = xhci_stream_id_to_ring(dev, ep_index, stream_id);
+	ep_ring = xhci_virt_ep_to_ring(xhci, ep, stream_id);
 	if (!ep_ring) {
 		xhci_warn(xhci, "WARN Set TR deq ptr command for freed stream ID %u\n",
 				stream_id);
@@ -1153,8 +1330,8 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
 		goto cleanup;
 	}
 
-	ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index);
-	slot_ctx = xhci_get_slot_ctx(xhci, dev->out_ctx);
+	ep_ctx = xhci_get_ep_ctx(xhci, ep->vdev->out_ctx, ep_index);
+	slot_ctx = xhci_get_slot_ctx(xhci, ep->vdev->out_ctx);
 	trace_xhci_handle_cmd_set_deq(slot_ctx);
 	trace_xhci_handle_cmd_set_deq_ep(ep_ctx);
 
@@ -1207,7 +1384,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
 			/* Update the ring's dequeue segment and dequeue pointer
 			 * to reflect the new position.
 			 */
-			update_ring_for_set_deq_completion(xhci, dev,
+			update_ring_for_set_deq_completion(xhci, ep->vdev,
 				ep_ring, ep_index);
 		} else {
 			xhci_warn(xhci, "Mismatch between completed Set TR Deq Ptr command & xHCI internal state.\n");
@@ -1215,7 +1392,15 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
 				  ep->queued_deq_seg, ep->queued_deq_ptr);
 		}
 	}
-
+	/* HW cached TDs cleared from cache, give them back */
+	list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list,
+				 cancelled_td_list) {
+		ep_ring = xhci_urb_to_transfer_ring(ep->xhci, td->urb);
+		if (td->cancel_status == TD_CLEARING_CACHE) {
+			td->cancel_status = TD_CLEARED;
+			xhci_td_cleanup(ep->xhci, td, ep_ring, td->status);
+		}
+	}
 cleanup:
 	ep->ep_state &= ~SET_DEQ_PENDING;
 	ep->queued_deq_seg = NULL;
@@ -1227,7 +1412,6 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
 static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
 		union xhci_trb *trb, u32 cmd_comp_code)
 {
-	struct xhci_virt_device *vdev;
 	struct xhci_virt_ep *ep;
 	struct xhci_ep_ctx *ep_ctx;
 	unsigned int ep_index;
@@ -1237,8 +1421,7 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
 	if (!ep)
 		return;
 
-	vdev = xhci->devs[slot_id];
-	ep_ctx = xhci_get_ep_ctx(xhci, vdev->out_ctx, ep_index);
+	ep_ctx = xhci_get_ep_ctx(xhci, ep->vdev->out_ctx, ep_index);
 	trace_xhci_handle_cmd_reset_ep(ep_ctx);
 
 	/* This command will only fail if the endpoint wasn't halted,
@@ -1247,27 +1430,15 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
 	xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep,
 		"Ignoring reset ep completion code of %u", cmd_comp_code);
 
-	/* HW with the reset endpoint quirk needs to have a configure endpoint
-	 * command complete before the endpoint can be used.  Queue that here
-	 * because the HW can't handle two commands being queued in a row.
-	 */
-	if (xhci->quirks & XHCI_RESET_EP_QUIRK) {
-		struct xhci_command *command;
+	/* Cleanup cancelled TDs as ep is stopped. May queue a Set TR Deq cmd */
+	xhci_invalidate_cancelled_tds(ep);
 
-		command = xhci_alloc_command(xhci, false, GFP_ATOMIC);
-		if (!command)
-			return;
+	if (xhci->quirks & XHCI_RESET_EP_QUIRK)
+		xhci_dbg(xhci, "Note: Removed workaround to queue config ep for this hw");
+	/* Clear our internal halted state */
+	ep->ep_state &= ~EP_HALTED;
 
-		xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
-				"Queueing configure endpoint command");
-		xhci_queue_configure_endpoint(xhci, command,
-				xhci->devs[slot_id]->in_ctx->dma, slot_id,
-				false);
-		xhci_ring_cmd_db(xhci);
-	} else {
-		/* Clear our internal halted state */
-		ep->ep_state &= ~EP_HALTED;
-	}
+	xhci_giveback_invalidated_tds(ep);
 
 	/* if this was a soft reset, then restart */
 	if ((le32_to_cpu(trb->generic.field[3])) & TRB_TSP)
@@ -1298,10 +1469,11 @@ static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id)
 	if (xhci->quirks & XHCI_EP_LIMIT_QUIRK)
 		/* Delete default control endpoint resources */
 		xhci_free_device_endpoint_resources(xhci, virt_dev, true);
+	xhci_free_virt_device(xhci, slot_id);
 }
 
 static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id,
-		struct xhci_event_cmd *event, u32 cmd_comp_code)
+		u32 cmd_comp_code)
 {
 	struct xhci_virt_device *virt_dev;
 	struct xhci_input_control_ctx *ctrl_ctx;
@@ -1319,6 +1491,8 @@ static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id,
 	 * is not waiting on the configure endpoint command.
 	 */
 	virt_dev = xhci->devs[slot_id];
+	if (!virt_dev)
+		return;
 	ctrl_ctx = xhci_get_input_control_ctx(virt_dev->in_ctx);
 	if (!ctrl_ctx) {
 		xhci_warn(xhci, "Could not get input context, bad type.\n");
@@ -1363,24 +1537,27 @@ static void xhci_handle_cmd_addr_dev(struct xhci_hcd *xhci, int slot_id)
 	struct xhci_slot_ctx *slot_ctx;
 
 	vdev = xhci->devs[slot_id];
+	if (!vdev)
+		return;
 	slot_ctx = xhci_get_slot_ctx(xhci, vdev->out_ctx);
 	trace_xhci_handle_cmd_addr_dev(slot_ctx);
 }
 
-static void xhci_handle_cmd_reset_dev(struct xhci_hcd *xhci, int slot_id,
-		struct xhci_event_cmd *event)
+static void xhci_handle_cmd_reset_dev(struct xhci_hcd *xhci, int slot_id)
 {
 	struct xhci_virt_device *vdev;
 	struct xhci_slot_ctx *slot_ctx;
 
 	vdev = xhci->devs[slot_id];
+	if (!vdev) {
+		xhci_warn(xhci, "Reset device command completion for disabled slot %u\n",
+			  slot_id);
+		return;
+	}
 	slot_ctx = xhci_get_slot_ctx(xhci, vdev->out_ctx);
 	trace_xhci_handle_cmd_reset_dev(slot_ctx);
 
 	xhci_dbg(xhci, "Completed reset device command.\n");
-	if (!xhci->devs[slot_id])
-		xhci_warn(xhci, "Reset device command completion "
-				"for disabled slot %u\n", slot_id);
 }
 
 static void xhci_handle_cmd_nec_get_fw(struct xhci_hcd *xhci,
@@ -1473,7 +1650,7 @@ void xhci_handle_command_timeout(struct work_struct *work)
 static void handle_cmd_completion(struct xhci_hcd *xhci,
 		struct xhci_event_cmd *event)
 {
-	int slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
+	unsigned int slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
 	u64 cmd_dma;
 	dma_addr_t cmd_dequeue_dma;
 	u32 cmd_comp_code;
@@ -1481,6 +1658,11 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
 	struct xhci_command *cmd;
 	u32 cmd_type;
 
+	if (slot_id >= MAX_HC_SLOTS) {
+		xhci_warn(xhci, "Invalid slot_id %u\n", slot_id);
+		return;
+	}
+
 	cmd_dma = le64_to_cpu(event->cmd_trb);
 	cmd_trb = xhci->cmd_ring->dequeue;
 
@@ -1541,8 +1723,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
 		break;
 	case TRB_CONFIG_EP:
 		if (!cmd->completion)
-			xhci_handle_cmd_config_ep(xhci, slot_id, event,
-						  cmd_comp_code);
+			xhci_handle_cmd_config_ep(xhci, slot_id, cmd_comp_code);
 		break;
 	case TRB_EVAL_CONTEXT:
 		break;
@@ -1553,7 +1734,8 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
 		WARN_ON(slot_id != TRB_TO_SLOT_ID(
 				le32_to_cpu(cmd_trb->generic.field[3])));
 		if (!cmd->completion)
-			xhci_handle_cmd_stop_ep(xhci, slot_id, cmd_trb, event);
+			xhci_handle_cmd_stop_ep(xhci, slot_id, cmd_trb,
+						cmd_comp_code);
 		break;
 	case TRB_SET_DEQ:
 		WARN_ON(slot_id != TRB_TO_SLOT_ID(
@@ -1576,7 +1758,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
 		 */
 		slot_id = TRB_TO_SLOT_ID(
 				le32_to_cpu(cmd_trb->generic.field[3]));
-		xhci_handle_cmd_reset_dev(xhci, slot_id, event);
+		xhci_handle_cmd_reset_dev(xhci, slot_id);
 		break;
 	case TRB_NEC_GET_FW:
 		xhci_handle_cmd_nec_get_fw(xhci, event);
@@ -1603,11 +1785,8 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
 }
 
 static void handle_vendor_event(struct xhci_hcd *xhci,
-		union xhci_trb *event)
+				union xhci_trb *event, u32 trb_type)
 {
-	u32 trb_type;
-
-	trb_type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->generic.field[3]));
 	xhci_dbg(xhci, "Vendor specific event TRB type = %u\n", trb_type);
 	if (trb_type == TRB_NEC_CMD_COMP && (xhci->quirks & XHCI_NEC_HOST))
 		handle_cmd_completion(xhci, &event->event_cmd);
@@ -1924,37 +2103,6 @@ static void xhci_clear_hub_tt_buffer(struct xhci_hcd *xhci, struct xhci_td *td,
 	}
 }
 
-static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci,
-		unsigned int slot_id, unsigned int ep_index,
-		unsigned int stream_id, struct xhci_td *td,
-		enum xhci_ep_reset_type reset_type)
-{
-	struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index];
-	struct xhci_command *command;
-
-	/*
-	 * Avoid resetting endpoint if link is inactive. Can cause host hang.
-	 * Device will be reset soon to recover the link so don't do anything
-	 */
-	if (xhci->devs[slot_id]->flags & VDEV_PORT_ERROR)
-		return;
-
-	command = xhci_alloc_command(xhci, false, GFP_ATOMIC);
-	if (!command)
-		return;
-
-	ep->ep_state |= EP_HALTED;
-
-	xhci_queue_reset_ep(xhci, command, slot_id, ep_index, reset_type);
-
-	if (reset_type == EP_HARD_RESET) {
-		ep->ep_state |= EP_HARD_CLEAR_TOGGLE;
-		xhci_cleanup_stalled_ring(xhci, slot_id, ep_index, stream_id,
-					  td);
-	}
-	xhci_ring_cmd_db(xhci);
-}
-
 /* Check if an error has halted the endpoint ring.  The class driver will
  * cleanup the halt for a non-default control endpoint if we indicate a stall.
  * However, a babble and other errors also halt the endpoint ring, and the class
@@ -1995,82 +2143,60 @@ int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code)
 	return 0;
 }
 
-static int xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td,
-		struct xhci_ring *ep_ring, int *status)
+static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
+		     struct xhci_ring *ep_ring, struct xhci_td *td,
+		     u32 trb_comp_code)
 {
-	struct urb *urb = NULL;
-
-	/* Clean up the endpoint's TD list */
-	urb = td->urb;
-
-	/* if a bounce buffer was used to align this td then unmap it */
-	xhci_unmap_td_bounce_buffer(xhci, ep_ring, td);
-
-	/* Do one last check of the actual transfer length.
-	 * If the host controller said we transferred more data than the buffer
-	 * length, urb->actual_length will be a very big number (since it's
-	 * unsigned).  Play it safe and say we didn't transfer anything.
-	 */
-	if (urb->actual_length > urb->transfer_buffer_length) {
-		xhci_warn(xhci, "URB req %u and actual %u transfer length mismatch\n",
-			  urb->transfer_buffer_length, urb->actual_length);
-		urb->actual_length = 0;
-		*status = 0;
-	}
-	list_del_init(&td->td_list);
-	/* Was this TD slated to be cancelled but completed anyway? */
-	if (!list_empty(&td->cancelled_td_list))
-		list_del_init(&td->cancelled_td_list);
-
-	inc_td_cnt(urb);
-	/* Giveback the urb when all the tds are completed */
-	if (last_td_in_urb(td)) {
-		if ((urb->actual_length != urb->transfer_buffer_length &&
-		     (urb->transfer_flags & URB_SHORT_NOT_OK)) ||
-		    (*status != 0 && !usb_endpoint_xfer_isoc(&urb->ep->desc)))
-			xhci_dbg(xhci, "Giveback URB %p, len = %d, expected = %d, status = %d\n",
-				 urb, urb->actual_length,
-				 urb->transfer_buffer_length, *status);
-
-		/* set isoc urb status to 0 just as EHCI, UHCI, and OHCI */
-		if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
-			*status = 0;
-		xhci_giveback_urb_in_irq(xhci, td, *status);
-	}
-
-	return 0;
-}
-
-static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
-	struct xhci_transfer_event *event,
-	struct xhci_virt_ep *ep, int *status)
-{
-	struct xhci_virt_device *xdev;
 	struct xhci_ep_ctx *ep_ctx;
-	struct xhci_ring *ep_ring;
-	unsigned int slot_id;
-	u32 trb_comp_code;
-	int ep_index;
 
-	slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
-	xdev = xhci->devs[slot_id];
-	ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
-	ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
-	ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
-	trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
+	ep_ctx = xhci_get_ep_ctx(xhci, ep->vdev->out_ctx, ep->ep_index);
 
-	if (trb_comp_code == COMP_STOPPED_LENGTH_INVALID ||
-			trb_comp_code == COMP_STOPPED ||
-			trb_comp_code == COMP_STOPPED_SHORT_PACKET) {
-		/* The Endpoint Stop Command completion will take care of any
-		 * stopped TDs.  A stopped TD may be restarted, so don't update
+	switch (trb_comp_code) {
+	case COMP_STOPPED_LENGTH_INVALID:
+	case COMP_STOPPED_SHORT_PACKET:
+	case COMP_STOPPED:
+		/*
+		 * The "Stop Endpoint" completion will take care of any
+		 * stopped TDs. A stopped TD may be restarted, so don't update
 		 * the ring dequeue pointer or take this TD off any lists yet.
 		 */
 		return 0;
-	}
-	if (trb_comp_code == COMP_STALL_ERROR ||
-		xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
-						trb_comp_code)) {
+	case COMP_USB_TRANSACTION_ERROR:
+	case COMP_BABBLE_DETECTED_ERROR:
+	case COMP_SPLIT_TRANSACTION_ERROR:
+		/*
+		 * If endpoint context state is not halted we might be
+		 * racing with a reset endpoint command issued by a unsuccessful
+		 * stop endpoint completion (context error). In that case the
+		 * td should be on the cancelled list, and EP_HALTED flag set.
+		 *
+		 * Or then it's not halted due to the 0.95 spec stating that a
+		 * babbling control endpoint should not halt. The 0.96 spec
+		 * again says it should.  Some HW claims to be 0.95 compliant,
+		 * but it halts the control endpoint anyway.
+		 */
+		if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_HALTED) {
+			/*
+			 * If EP_HALTED is set and TD is on the cancelled list
+			 * the TD and dequeue pointer will be handled by reset
+			 * ep command completion
+			 */
+			if ((ep->ep_state & EP_HALTED) &&
+			    !list_empty(&td->cancelled_td_list)) {
+				xhci_dbg(xhci, "Already resolving halted ep for 0x%llx\n",
+					 (unsigned long long)xhci_trb_virt_to_dma(
+						 td->start_seg, td->first_trb));
+				return 0;
+			}
+			/* endpoint not halted, don't reset it */
+			break;
+		}
+		/* Almost same procedure as for STALL_ERROR below */
+		xhci_clear_hub_tt_buffer(xhci, td, ep);
+		xhci_handle_halted_endpoint(xhci, ep, ep_ring->stream_id, td,
+					    EP_HARD_RESET);
+		return 0;
+	case COMP_STALL_ERROR:
 		/*
 		 * xhci internal endpoint state will go to a "halt" state for
 		 * any stall, including default control pipe protocol stall.
@@ -2081,18 +2207,24 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
 		 * stall later. Hub TT buffer should only be cleared for FS/LS
 		 * devices behind HS hubs for functional stalls.
 		 */
-		if ((ep_index != 0) || (trb_comp_code != COMP_STALL_ERROR))
+		if (ep->ep_index != 0)
 			xhci_clear_hub_tt_buffer(xhci, td, ep);
-		xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index,
-					ep_ring->stream_id, td, EP_HARD_RESET);
-	} else {
-		/* Update ring dequeue pointer */
-		while (ep_ring->dequeue != td->last_trb)
-			inc_deq(xhci, ep_ring);
-		inc_deq(xhci, ep_ring);
+
+		xhci_handle_halted_endpoint(xhci, ep, ep_ring->stream_id, td,
+					    EP_HARD_RESET);
+
+		return 0; /* xhci_handle_halted_endpoint marked td cancelled */
+	default:
+		break;
 	}
 
-	return xhci_td_cleanup(xhci, td, ep_ring, status);
+	/* Update ring dequeue pointer */
+	ep_ring->dequeue = td->last_trb;
+	ep_ring->deq_seg = td->last_trb_seg;
+	ep_ring->num_trbs_free += td->num_trbs - 1;
+	inc_deq(xhci, ep_ring);
+
+	return xhci_td_cleanup(xhci, td, ep_ring, td->status);
 }
 
 /* sum trb lengths from ring dequeue up to stop_trb, _excluding_ stop_trb */
@@ -2113,23 +2245,17 @@ static int sum_trb_lengths(struct xhci_hcd *xhci, struct xhci_ring *ring,
 /*
  * Process control tds, update urb status and actual_length.
  */
-static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
-	union xhci_trb *ep_trb, struct xhci_transfer_event *event,
-	struct xhci_virt_ep *ep, int *status)
+static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
+		struct xhci_ring *ep_ring,  struct xhci_td *td,
+			   union xhci_trb *ep_trb, struct xhci_transfer_event *event)
 {
-	struct xhci_virt_device *xdev;
-	unsigned int slot_id;
-	int ep_index;
 	struct xhci_ep_ctx *ep_ctx;
 	u32 trb_comp_code;
 	u32 remaining, requested;
 	u32 trb_type;
 
 	trb_type = TRB_FIELD_TO_TYPE(le32_to_cpu(ep_trb->generic.field[3]));
-	slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
-	xdev = xhci->devs[slot_id];
-	ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
-	ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
+	ep_ctx = xhci_get_ep_ctx(xhci, ep->vdev->out_ctx, ep->ep_index);
 	trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
 	requested = td->urb->transfer_buffer_length;
 	remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
@@ -2139,13 +2265,13 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
 		if (trb_type != TRB_STATUS) {
 			xhci_warn(xhci, "WARN: Success on ctrl %s TRB without IOC set?\n",
 				  (trb_type == TRB_DATA) ? "data" : "setup");
-			*status = -ESHUTDOWN;
+			td->status = -ESHUTDOWN;
 			break;
 		}
-		*status = 0;
+		td->status = 0;
 		break;
 	case COMP_SHORT_PACKET:
-		*status = 0;
+		td->status = 0;
 		break;
 	case COMP_STOPPED_SHORT_PACKET:
 		if (trb_type == TRB_DATA || trb_type == TRB_NORMAL)
@@ -2177,7 +2303,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
 						       ep_ctx, trb_comp_code))
 			break;
 		xhci_dbg(xhci, "TRB error %u, halted endpoint index = %u\n",
-			 trb_comp_code, ep_index);
+			 trb_comp_code, ep->ep_index);
 		fallthrough;
 	case COMP_STALL_ERROR:
 		/* Did we transfer part of the data (middle) phase? */
@@ -2209,17 +2335,16 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
 		td->urb->actual_length = requested;
 
 finish_td:
-	return finish_td(xhci, td, event, ep, status);
+	return finish_td(xhci, ep, ep_ring, td, trb_comp_code);
 }
 
 /*
  * Process isochronous tds, update urb packet status and actual_length.
  */
-static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
-	union xhci_trb *ep_trb, struct xhci_transfer_event *event,
-	struct xhci_virt_ep *ep, int *status)
+static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
+		struct xhci_ring *ep_ring, struct xhci_td *td,
+		union xhci_trb *ep_trb, struct xhci_transfer_event *event)
 {
-	struct xhci_ring *ep_ring;
 	struct urb_priv *urb_priv;
 	int idx;
 	struct usb_iso_packet_descriptor *frame;
@@ -2228,7 +2353,6 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
 	u32 remaining, requested, ep_trb_len;
 	int short_framestatus;
 
-	ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
 	trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
 	urb_priv = td->urb->hcpriv;
 	idx = urb_priv->num_tds_done;
@@ -2289,26 +2413,23 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
 	}
 
 	if (sum_trbs_for_length)
-		frame->actual_length = sum_trb_lengths(xhci, ep_ring, ep_trb) +
+		frame->actual_length = sum_trb_lengths(xhci, ep->ring, ep_trb) +
 			ep_trb_len - remaining;
 	else
 		frame->actual_length = requested;
 
 	td->urb->actual_length += frame->actual_length;
 
-	return finish_td(xhci, td, event, ep, status);
+	return finish_td(xhci, ep, ep_ring, td, trb_comp_code);
 }
 
 static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
-			struct xhci_transfer_event *event,
-			struct xhci_virt_ep *ep, int *status)
+			struct xhci_virt_ep *ep, int status)
 {
-	struct xhci_ring *ep_ring;
 	struct urb_priv *urb_priv;
 	struct usb_iso_packet_descriptor *frame;
 	int idx;
 
-	ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
 	urb_priv = td->urb->hcpriv;
 	idx = urb_priv->num_tds_done;
 	frame = &td->urb->iso_frame_desc[idx];
@@ -2320,31 +2441,26 @@ static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
 	frame->actual_length = 0;
 
 	/* Update ring dequeue pointer */
-	while (ep_ring->dequeue != td->last_trb)
-		inc_deq(xhci, ep_ring);
-	inc_deq(xhci, ep_ring);
+	ep->ring->dequeue = td->last_trb;
+	ep->ring->deq_seg = td->last_trb_seg;
+	ep->ring->num_trbs_free += td->num_trbs - 1;
+	inc_deq(xhci, ep->ring);
 
-	return xhci_td_cleanup(xhci, td, ep_ring, status);
+	return xhci_td_cleanup(xhci, td, ep->ring, status);
 }
 
 /*
  * Process bulk and interrupt tds, update urb status and actual_length.
  */
-static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
-	union xhci_trb *ep_trb, struct xhci_transfer_event *event,
-	struct xhci_virt_ep *ep, int *status)
+static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
+		struct xhci_ring *ep_ring, struct xhci_td *td,
+		union xhci_trb *ep_trb, struct xhci_transfer_event *event)
 {
 	struct xhci_slot_ctx *slot_ctx;
-	struct xhci_ring *ep_ring;
 	u32 trb_comp_code;
 	u32 remaining, requested, ep_trb_len;
-	unsigned int slot_id;
-	int ep_index;
 
-	slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
-	slot_ctx = xhci_get_slot_ctx(xhci, xhci->devs[slot_id]->out_ctx);
-	ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
-	ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer));
+	slot_ctx = xhci_get_slot_ctx(xhci, ep->vdev->out_ctx);
 	trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len));
 	remaining = EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
 	ep_trb_len = TRB_LEN(le32_to_cpu(ep_trb->generic.field[2]));
@@ -2360,13 +2476,13 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
 				 td->urb->ep->desc.bEndpointAddress,
 				 requested, remaining);
 		}
-		*status = 0;
+		td->status = 0;
 		break;
 	case COMP_SHORT_PACKET:
 		xhci_dbg(xhci, "ep %#x - asked for %d bytes, %d bytes untransferred\n",
 			 td->urb->ep->desc.bEndpointAddress,
 			 requested, remaining);
-		*status = 0;
+		td->status = 0;
 		break;
 	case COMP_STOPPED_SHORT_PACKET:
 		td->urb->actual_length = remaining;
@@ -2381,9 +2497,11 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
 		    (ep_ring->err_count++ > MAX_SOFT_RETRY) ||
 		    le32_to_cpu(slot_ctx->tt_info) & TT_SLOT)
 			break;
-		*status = 0;
-		xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index,
-					ep_ring->stream_id, td, EP_SOFT_RESET);
+
+		td->status = 0;
+
+		xhci_handle_halted_endpoint(xhci, ep, ep_ring->stream_id, td,
+					    EP_SOFT_RESET);
 		return 0;
 	default:
 		/* do nothing */
@@ -2402,7 +2520,8 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
 			  remaining);
 		td->urb->actual_length = 0;
 	}
-	return finish_td(xhci, td, event, ep, status);
+
+	return finish_td(xhci, ep, ep_ring, td, trb_comp_code);
 }
 
 /*
@@ -2413,7 +2532,6 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
 static int handle_tx_event(struct xhci_hcd *xhci,
 		struct xhci_transfer_event *event)
 {
-	struct xhci_virt_device *xdev;
 	struct xhci_virt_ep *ep;
 	struct xhci_ring *ep_ring;
 	unsigned int slot_id;
@@ -2440,9 +2558,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
 		goto err_out;
 	}
 
-	xdev = xhci->devs[slot_id];
 	ep_ring = xhci_dma_to_transfer_ring(ep, ep_trb_dma);
-	ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
+	ep_ctx = xhci_get_ep_ctx(xhci, ep->vdev->out_ctx, ep_index);
 
 	if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_DISABLED) {
 		xhci_err(xhci,
@@ -2458,8 +2575,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
 		case COMP_USB_TRANSACTION_ERROR:
 		case COMP_INVALID_STREAM_TYPE_ERROR:
 		case COMP_INVALID_STREAM_ID_ERROR:
-			xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index, 0,
-						     NULL, EP_SOFT_RESET);
+			xhci_handle_halted_endpoint(xhci, ep, 0, NULL,
+						    EP_SOFT_RESET);
 			goto cleanup;
 		case COMP_RING_UNDERRUN:
 		case COMP_RING_OVERRUN:
@@ -2514,7 +2631,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
 	case COMP_STALL_ERROR:
 		xhci_dbg(xhci, "Stalled endpoint for slot %u ep %u\n", slot_id,
 			 ep_index);
-		ep->ep_state |= EP_HALTED;
 		status = -EPIPE;
 		break;
 	case COMP_SPLIT_TRANSACTION_ERROR:
@@ -2642,11 +2758,10 @@ static int handle_tx_event(struct xhci_hcd *xhci,
 			if (trb_comp_code == COMP_STALL_ERROR ||
 			    xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
 							      trb_comp_code)) {
-				xhci_cleanup_halted_endpoint(xhci, slot_id,
-							     ep_index,
-							     ep_ring->stream_id,
-							     NULL,
-							     EP_HARD_RESET);
+				xhci_handle_halted_endpoint(xhci, ep,
+							    ep_ring->stream_id,
+							    NULL,
+							    EP_HARD_RESET);
 			}
 			goto cleanup;
 		}
@@ -2705,7 +2820,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
 				return -ESHUTDOWN;
 			}
 
-			skip_isoc_td(xhci, td, event, ep, &status);
+			skip_isoc_td(xhci, td, ep, status);
 			goto cleanup;
 		}
 		if (trb_comp_code == COMP_SHORT_PACKET)
@@ -2733,25 +2848,26 @@ static int handle_tx_event(struct xhci_hcd *xhci,
 		 * endpoint. Otherwise, the endpoint remains stalled
 		 * indefinitely.
 		 */
+
 		if (trb_is_noop(ep_trb)) {
 			if (trb_comp_code == COMP_STALL_ERROR ||
 			    xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
 							      trb_comp_code))
-				xhci_cleanup_halted_endpoint(xhci, slot_id,
-							     ep_index,
-							     ep_ring->stream_id,
-							     td, EP_HARD_RESET);
+				xhci_handle_halted_endpoint(xhci, ep,
+							    ep_ring->stream_id,
+							    td, EP_HARD_RESET);
 			goto cleanup;
 		}
 
+		td->status = status;
+
 		/* update the urb's actual_length and give back to the core */
 		if (usb_endpoint_xfer_control(&td->urb->ep->desc))
-			process_ctrl_td(xhci, td, ep_trb, event, ep, &status);
+			process_ctrl_td(xhci, ep, ep_ring, td, ep_trb, event);
 		else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc))
-			process_isoc_td(xhci, td, ep_trb, event, ep, &status);
+			process_isoc_td(xhci, ep, ep_ring, td, ep_trb, event);
 		else
-			process_bulk_intr_td(xhci, td, ep_trb, event, ep,
-					     &status);
+			process_bulk_intr_td(xhci, ep, ep_ring, td, ep_trb, event);
 cleanup:
 		handling_skipped_tds = ep->skip &&
 			trb_comp_code != COMP_MISSED_SERVICE_ERROR &&
@@ -2792,10 +2908,11 @@ static int handle_tx_event(struct xhci_hcd *xhci,
  * Returns >0 for "possibly more events to process" (caller should call again),
  * otherwise 0 if done.  In future, <0 returns should indicate error code.
  */
-static int xhci_handle_event(struct xhci_hcd *xhci)
+int xhci_handle_event(struct xhci_hcd *xhci)
 {
 	union xhci_trb *event;
 	int update_ptrs = 1;
+	u32 trb_type;
 	int ret;
 
 	/* Event ring hasn't been allocated yet. */
@@ -2817,31 +2934,30 @@ static int xhci_handle_event(struct xhci_hcd *xhci)
 	 * speculative reads of the event's flags/data below.
 	 */
 	rmb();
+	trb_type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->event_cmd.flags));
 	/* FIXME: Handle more event types. */
-	switch (le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK) {
-	case TRB_TYPE(TRB_COMPLETION):
+
+	switch (trb_type) {
+	case TRB_COMPLETION:
 		handle_cmd_completion(xhci, &event->event_cmd);
 		break;
-	case TRB_TYPE(TRB_PORT_STATUS):
+	case TRB_PORT_STATUS:
 		handle_port_status(xhci, event);
 		update_ptrs = 0;
 		break;
-	case TRB_TYPE(TRB_TRANSFER):
+	case TRB_TRANSFER:
 		ret = handle_tx_event(xhci, &event->trans_event);
 		if (ret >= 0)
 			update_ptrs = 0;
 		break;
-	case TRB_TYPE(TRB_DEV_NOTE):
+	case TRB_DEV_NOTE:
 		handle_device_notification(xhci, event);
 		break;
 	default:
-		if ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK) >=
-		    TRB_TYPE(48))
-			handle_vendor_event(xhci, event);
+		if (trb_type >= TRB_VENDOR_DEFINED_LOW)
+			handle_vendor_event(xhci, event, trb_type);
 		else
-			xhci_warn(xhci, "ERROR unknown event type %d\n",
-				  TRB_FIELD_TO_TYPE(
-				  le32_to_cpu(event->event_cmd.flags)));
+			xhci_warn(xhci, "ERROR unknown event type %d\n", trb_type);
 	}
 	/* Any of the above functions may drop and re-acquire the lock, so check
 	 * to make sure a watchdog timer didn't mark the host as non-responsive.
@@ -2861,13 +2977,14 @@ static int xhci_handle_event(struct xhci_hcd *xhci)
 	 */
 	return 1;
 }
+EXPORT_SYMBOL_GPL(xhci_handle_event);
 
 /*
  * Update Event Ring Dequeue Pointer:
  * - When all events have finished
  * - To avoid "Event Ring Full Error" condition
  */
-static void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
+void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
 		union xhci_trb *event_ring_deq)
 {
 	u64 temp_64;
@@ -2897,6 +3014,16 @@ static void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
 	temp_64 |= ERST_EHB;
 	xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue);
 }
+EXPORT_SYMBOL_GPL(xhci_update_erst_dequeue);
+
+static irqreturn_t xhci_vendor_queue_irq_work(struct xhci_hcd *xhci)
+{
+	struct xhci_vendor_ops *ops = xhci_vendor_get_ops(xhci);
+
+	if (ops && ops->queue_irq_work)
+		return ops->queue_irq_work(xhci);
+	return IRQ_NONE;
+}
 
 /*
  * xHCI spec says we can get an interrupt, and if the HC has an error condition,
@@ -2932,6 +3059,10 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
 		goto out;
 	}
 
+	ret = xhci_vendor_queue_irq_work(xhci);
+	if (ret == IRQ_HANDLED)
+		goto out;
+
 	/*
 	 * Clear the op reg interrupt status first,
 	 * so we can receive interrupts from other MSI-X interrupters.
@@ -3024,6 +3155,7 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
 		u32 ep_state, unsigned int num_trbs, gfp_t mem_flags)
 {
 	unsigned int num_trbs_needed;
+	unsigned int link_trb_count = 0;
 
 	/* Make sure the endpoint has been added to xHC schedule */
 	switch (ep_state) {
@@ -3094,7 +3226,19 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
 
 		ep_ring->enq_seg = ep_ring->enq_seg->next;
 		ep_ring->enqueue = ep_ring->enq_seg->trbs;
+
+		/* prevent infinite loop if all first trbs are link trbs */
+		if (link_trb_count++ > ep_ring->num_segs) {
+			xhci_warn(xhci, "Ring is an endless link TRB loop\n");
+			return -EINVAL;
+		}
 	}
+
+	if (last_trb_on_seg(ep_ring->enq_seg, ep_ring->enqueue)) {
+		xhci_warn(xhci, "Missing link TRB at end of ring segment\n");
+		return -EINVAL;
+	}
+
 	return 0;
 }
 
@@ -3113,7 +3257,8 @@ static int prepare_transfer(struct xhci_hcd *xhci,
 	struct xhci_ring *ep_ring;
 	struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index);
 
-	ep_ring = xhci_stream_id_to_ring(xdev, ep_index, stream_id);
+	ep_ring = xhci_triad_to_transfer_ring(xhci, xdev->slot_id, ep_index,
+					      stream_id);
 	if (!ep_ring) {
 		xhci_dbg(xhci, "Can't prepare ring for bad stream ID %u\n",
 				stream_id);
@@ -3482,7 +3627,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 			field |= TRB_IOC;
 			more_trbs_coming = false;
 			td->last_trb = ring->enqueue;
-
+			td->last_trb_seg = ring->enq_seg;
 			if (xhci_urb_suitable_for_idt(urb)) {
 				memcpy(&send_addr, urb->transfer_buffer,
 				       trb_buff_len);
@@ -3508,7 +3653,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 				upper_32_bits(send_addr),
 				length_field,
 				field);
-
+		td->num_trbs++;
 		addr += trb_buff_len;
 		sent_len = trb_buff_len;
 
@@ -3532,8 +3677,10 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 				       ep_index, urb->stream_id,
 				       1, urb, 1, mem_flags);
 		urb_priv->td[1].last_trb = ring->enqueue;
+		urb_priv->td[1].last_trb_seg = ring->enq_seg;
 		field = TRB_TYPE(TRB_NORMAL) | ring->cycle_state | TRB_IOC;
 		queue_trb(xhci, ring, 0, 0, 0, TRB_INTR_TARGET(0), field);
+		urb_priv->td[1].num_trbs++;
 	}
 
 	check_trb_math(urb, enqd_len);
@@ -3584,6 +3731,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 
 	urb_priv = urb->hcpriv;
 	td = &urb_priv->td[0];
+	td->num_trbs = num_trbs;
 
 	/*
 	 * Don't give the first TRB to the hardware (by toggling the cycle bit)
@@ -3656,6 +3804,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 
 	/* Save the DMA address of the last TRB in the TD */
 	td->last_trb = ep_ring->enqueue;
+	td->last_trb_seg = ep_ring->enq_seg;
 
 	/* Queue status TRB - see Table 7 and sections 4.11.2.2 and 6.4.1.2.3 */
 	/* If the device sent data, the status stage is an OUT transfer */
@@ -3900,7 +4049,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 			goto cleanup;
 		}
 		td = &urb_priv->td[i];
-
+		td->num_trbs = trbs_per_td;
 		/* use SIA as default, if frame id is used overwrite it */
 		sia_frame_id = TRB_SIA;
 		if (!(urb->transfer_flags & URB_ISO_ASAP) &&
@@ -3943,6 +4092,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
 			} else {
 				more_trbs_coming = false;
 				td->last_trb = ep_ring->enqueue;
+				td->last_trb_seg = ep_ring->enq_seg;
 				field |= TRB_IOC;
 				if (trb_block_event_intr(xhci, num_tds, i))
 					field |= TRB_BEI;
@@ -4225,71 +4375,7 @@ int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, struct xhci_command *cmd,
 	return queue_command(xhci, cmd, 0, 0, 0,
 			trb_slot_id | trb_ep_index | type | trb_suspend, false);
 }
-
-/* Set Transfer Ring Dequeue Pointer command */
-void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci,
-		unsigned int slot_id, unsigned int ep_index,
-		struct xhci_dequeue_state *deq_state)
-{
-	dma_addr_t addr;
-	u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id);
-	u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
-	u32 trb_stream_id = STREAM_ID_FOR_TRB(deq_state->stream_id);
-	u32 trb_sct = 0;
-	u32 type = TRB_TYPE(TRB_SET_DEQ);
-	struct xhci_virt_ep *ep;
-	struct xhci_command *cmd;
-	int ret;
-
-	xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
-		"Set TR Deq Ptr cmd, new deq seg = %p (0x%llx dma), new deq ptr = %p (0x%llx dma), new cycle = %u",
-		deq_state->new_deq_seg,
-		(unsigned long long)deq_state->new_deq_seg->dma,
-		deq_state->new_deq_ptr,
-		(unsigned long long)xhci_trb_virt_to_dma(
-			deq_state->new_deq_seg, deq_state->new_deq_ptr),
-		deq_state->new_cycle_state);
-
-	addr = xhci_trb_virt_to_dma(deq_state->new_deq_seg,
-				    deq_state->new_deq_ptr);
-	if (addr == 0) {
-		xhci_warn(xhci, "WARN Cannot submit Set TR Deq Ptr\n");
-		xhci_warn(xhci, "WARN deq seg = %p, deq pt = %p\n",
-			  deq_state->new_deq_seg, deq_state->new_deq_ptr);
-		return;
-	}
-	ep = &xhci->devs[slot_id]->eps[ep_index];
-	if ((ep->ep_state & SET_DEQ_PENDING)) {
-		xhci_warn(xhci, "WARN Cannot submit Set TR Deq Ptr\n");
-		xhci_warn(xhci, "A Set TR Deq Ptr command is pending.\n");
-		return;
-	}
-
-	/* This function gets called from contexts where it cannot sleep */
-	cmd = xhci_alloc_command(xhci, false, GFP_ATOMIC);
-	if (!cmd)
-		return;
-
-	ep->queued_deq_seg = deq_state->new_deq_seg;
-	ep->queued_deq_ptr = deq_state->new_deq_ptr;
-	if (deq_state->stream_id)
-		trb_sct = SCT_FOR_TRB(SCT_PRI_TR);
-	ret = queue_command(xhci, cmd,
-		lower_32_bits(addr) | trb_sct | deq_state->new_cycle_state,
-		upper_32_bits(addr), trb_stream_id,
-		trb_slot_id | trb_ep_index | type, false);
-	if (ret < 0) {
-		xhci_free_command(xhci, cmd);
-		return;
-	}
-
-	/* Stop the TD queueing code from ringing the doorbell until
-	 * this command completes.  The HC won't set the dequeue pointer
-	 * if the ring is running, and ringing the doorbell starts the
-	 * ring running.
-	 */
-	ep->ep_state |= SET_DEQ_PENDING;
-}
+EXPORT_SYMBOL_GPL(xhci_queue_stop_endpoint);
 
 int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd,
 			int slot_id, unsigned int ep_index,
diff --git a/drivers/usb/host/xhci-trace.c b/drivers/usb/host/xhci-trace.c
index d0070814d..c4178357b 100644
--- a/drivers/usb/host/xhci-trace.c
+++ b/drivers/usb/host/xhci-trace.c
@@ -12,3 +12,6 @@
 #include "xhci-trace.h"
 
 EXPORT_TRACEPOINT_SYMBOL_GPL(xhci_dbg_quirks);
+EXPORT_TRACEPOINT_SYMBOL_GPL(xhci_urb_enqueue);
+EXPORT_TRACEPOINT_SYMBOL_GPL(xhci_handle_transfer);
+EXPORT_TRACEPOINT_SYMBOL_GPL(xhci_urb_giveback);
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index c968dd865..44dd99608 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -20,7 +20,6 @@
 
 #include "xhci.h"
 #include "xhci-trace.h"
-#include "xhci-mtk.h"
 #include "xhci-debugfs.h"
 #include "xhci-dbgcap.h"
 
@@ -1169,8 +1168,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
 	/* re-initialize the HC on Restore Error, or Host Controller Error */
 	if (temp & (STS_SRE | STS_HCE)) {
 		reinit_xhc = true;
-		if (!xhci->broken_suspend)
-			xhci_warn(xhci, "xHC error in resume, USBSTS 0x%x, Reinit\n", temp);
+		xhci_warn(xhci, "xHC error in resume, USBSTS 0x%x, Reinit\n", temp);
 	}
 
 	if (reinit_xhc) {
@@ -1331,6 +1329,7 @@ unsigned int xhci_get_endpoint_index(struct usb_endpoint_descriptor *desc)
 			(usb_endpoint_dir_in(desc) ? 1 : 0) - 1;
 	return index;
 }
+EXPORT_SYMBOL_GPL(xhci_get_endpoint_index);
 
 /* The reverse operation to xhci_get_endpoint_index. Calculate the USB endpoint
  * address from the XHCI endpoint index.
@@ -1341,6 +1340,7 @@ unsigned int xhci_get_endpoint_address(unsigned int ep_index)
 	unsigned int direction = ep_index % 2 ? USB_DIR_OUT : USB_DIR_IN;
 	return direction | number;
 }
+EXPORT_SYMBOL_GPL(xhci_get_endpoint_address);
 
 /* Find the flag for this endpoint (for use in the control context).  Use the
  * endpoint index to create a bitmask.  The slot context is bit 0, endpoint 0 is
@@ -1351,15 +1351,6 @@ static unsigned int xhci_get_endpoint_flag(struct usb_endpoint_descriptor *desc)
 	return 1 << (xhci_get_endpoint_index(desc) + 1);
 }
 
-/* Find the flag for this endpoint (for use in the control context).  Use the
- * endpoint index to create a bitmask.  The slot context is bit 0, endpoint 0 is
- * bit 1, etc.
- */
-static unsigned int xhci_get_endpoint_flag_from_index(unsigned int ep_index)
-{
-	return 1 << (ep_index + 1);
-}
-
 /* Compute the last valid endpoint context index.  Basically, this is the
  * endpoint index plus one.  For slot contexts with more than valid endpoint,
  * we find the most significant bit set in the added contexts flags.
@@ -1526,6 +1517,11 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
 		return -ENODEV;
 	}
 
+	if (xhci_vendor_usb_offload_skip_urb(xhci, urb)) {
+		xhci_dbg(xhci, "skip urb for usb offload\n");
+		return -EOPNOTSUPP;
+	}
+
 	if (usb_endpoint_xfer_isoc(&urb->ep->desc))
 		num_tds = urb->number_of_packets;
 	else if (usb_endpoint_is_bulk_out(&urb->ep->desc) &&
@@ -1726,7 +1722,12 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
 
 	for (; i < urb_priv->num_tds; i++) {
 		td = &urb_priv->td[i];
-		list_add_tail(&td->cancelled_td_list, &ep->cancelled_td_list);
+		/* TD can already be on cancelled list if ep halted on it */
+		if (list_empty(&td->cancelled_td_list)) {
+			td->cancel_status = TD_DIRTY;
+			list_add_tail(&td->cancelled_td_list,
+				      &ep->cancelled_td_list);
+		}
 	}
 
 	/* Queue a stop endpoint command, but only if this is
@@ -1772,8 +1773,8 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
  * disabled, so there's no need for mutual exclusion to protect
  * the xhci->devs[slot_id] structure.
  */
-static int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
-		struct usb_host_endpoint *ep)
+int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
+		       struct usb_host_endpoint *ep)
 {
 	struct xhci_hcd *xhci;
 	struct xhci_container_ctx *in_ctx, *out_ctx;
@@ -1833,9 +1834,6 @@ static int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
 
 	xhci_endpoint_zero(xhci, xhci->devs[udev->slot_id], ep);
 
-	if (xhci->quirks & XHCI_MTK_HOST)
-		xhci_mtk_drop_ep_quirk(hcd, udev, ep);
-
 	xhci_dbg(xhci, "drop ep 0x%x, slot id %d, new drop flags = %#x, new add flags = %#x\n",
 			(unsigned int) ep->desc.bEndpointAddress,
 			udev->slot_id,
@@ -1843,6 +1841,7 @@ static int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
 			(unsigned int) new_add_flags);
 	return 0;
 }
+EXPORT_SYMBOL_GPL(xhci_drop_endpoint);
 
 /* Add an endpoint to a new possible bandwidth configuration for this device.
  * Only one call to this function is allowed per endpoint before
@@ -1857,8 +1856,8 @@ static int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
  * configuration or alt setting is installed in the device, so there's no need
  * for mutual exclusion to protect the xhci->devs[slot_id] structure.
  */
-static int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
-		struct usb_host_endpoint *ep)
+int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
+		      struct usb_host_endpoint *ep)
 {
 	struct xhci_hcd *xhci;
 	struct xhci_container_ctx *in_ctx;
@@ -1932,15 +1931,6 @@ static int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
 		return -ENOMEM;
 	}
 
-	if (xhci->quirks & XHCI_MTK_HOST) {
-		ret = xhci_mtk_add_ep_quirk(hcd, udev, ep);
-		if (ret < 0) {
-			xhci_ring_free(xhci, virt_dev->eps[ep_index].new_ring);
-			virt_dev->eps[ep_index].new_ring = NULL;
-			return ret;
-		}
-	}
-
 	ctrl_ctx->add_flags |= cpu_to_le32(added_ctxs);
 	new_add_flags = le32_to_cpu(ctrl_ctx->add_flags);
 
@@ -1965,6 +1955,7 @@ static int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
 			(unsigned int) new_add_flags);
 	return 0;
 }
+EXPORT_SYMBOL_GPL(xhci_add_endpoint);
 
 static void xhci_zero_in_ctx(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev)
 {
@@ -2874,6 +2865,14 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
 			xhci_finish_resource_reservation(xhci, ctrl_ctx);
 		spin_unlock_irqrestore(&xhci->lock, flags);
 	}
+	if (ret)
+		goto failed;
+
+	ret = xhci_vendor_sync_dev_ctx(xhci, udev->slot_id);
+	if (ret)
+		xhci_warn(xhci, "sync device context failed, ret=%d", ret);
+
+failed:
 	return ret;
 }
 
@@ -2998,6 +2997,7 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
 
 	return ret;
 }
+EXPORT_SYMBOL_GPL(xhci_check_bandwidth);
 
 void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
 {
@@ -3016,12 +3016,17 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
 	for (i = 0; i < 31; i++) {
 		if (virt_dev->eps[i].new_ring) {
 			xhci_debugfs_remove_endpoint(xhci, virt_dev, i);
-			xhci_ring_free(xhci, virt_dev->eps[i].new_ring);
+			if (xhci_vendor_is_usb_offload_enabled(xhci, virt_dev, i))
+				xhci_vendor_free_transfer_ring(xhci, virt_dev, i);
+			else
+				xhci_ring_free(xhci, virt_dev->eps[i].new_ring);
+
 			virt_dev->eps[i].new_ring = NULL;
 		}
 	}
 	xhci_zero_in_ctx(xhci, virt_dev);
 }
+EXPORT_SYMBOL_GPL(xhci_reset_bandwidth);
 
 static void xhci_setup_input_ctx_for_config_ep(struct xhci_hcd *xhci,
 		struct xhci_container_ctx *in_ctx,
@@ -3035,84 +3040,6 @@ static void xhci_setup_input_ctx_for_config_ep(struct xhci_hcd *xhci,
 	ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
 }
 
-static void xhci_setup_input_ctx_for_quirk(struct xhci_hcd *xhci,
-		unsigned int slot_id, unsigned int ep_index,
-		struct xhci_dequeue_state *deq_state)
-{
-	struct xhci_input_control_ctx *ctrl_ctx;
-	struct xhci_container_ctx *in_ctx;
-	struct xhci_ep_ctx *ep_ctx;
-	u32 added_ctxs;
-	dma_addr_t addr;
-
-	in_ctx = xhci->devs[slot_id]->in_ctx;
-	ctrl_ctx = xhci_get_input_control_ctx(in_ctx);
-	if (!ctrl_ctx) {
-		xhci_warn(xhci, "%s: Could not get input context, bad type.\n",
-				__func__);
-		return;
-	}
-
-	xhci_endpoint_copy(xhci, xhci->devs[slot_id]->in_ctx,
-			xhci->devs[slot_id]->out_ctx, ep_index);
-	ep_ctx = xhci_get_ep_ctx(xhci, in_ctx, ep_index);
-	addr = xhci_trb_virt_to_dma(deq_state->new_deq_seg,
-			deq_state->new_deq_ptr);
-	if (addr == 0) {
-		xhci_warn(xhci, "WARN Cannot submit config ep after "
-				"reset ep command\n");
-		xhci_warn(xhci, "WARN deq seg = %p, deq ptr = %p\n",
-				deq_state->new_deq_seg,
-				deq_state->new_deq_ptr);
-		return;
-	}
-	ep_ctx->deq = cpu_to_le64(addr | deq_state->new_cycle_state);
-
-	added_ctxs = xhci_get_endpoint_flag_from_index(ep_index);
-	xhci_setup_input_ctx_for_config_ep(xhci, xhci->devs[slot_id]->in_ctx,
-			xhci->devs[slot_id]->out_ctx, ctrl_ctx,
-			added_ctxs, added_ctxs);
-}
-
-void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int slot_id,
-			       unsigned int ep_index, unsigned int stream_id,
-			       struct xhci_td *td)
-{
-	struct xhci_dequeue_state deq_state;
-
-	xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep,
-			"Cleaning up stalled endpoint ring");
-	/* We need to move the HW's dequeue pointer past this TD,
-	 * or it will attempt to resend it on the next doorbell ring.
-	 */
-	xhci_find_new_dequeue_state(xhci, slot_id, ep_index, stream_id, td,
-				    &deq_state);
-
-	if (!deq_state.new_deq_ptr || !deq_state.new_deq_seg)
-		return;
-
-	/* HW with the reset endpoint quirk will use the saved dequeue state to
-	 * issue a configure endpoint command later.
-	 */
-	if (!(xhci->quirks & XHCI_RESET_EP_QUIRK)) {
-		xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep,
-				"Queueing new dequeue state");
-		xhci_queue_new_dequeue_state(xhci, slot_id,
-				ep_index, &deq_state);
-	} else {
-		/* Better hope no one uses the input context between now and the
-		 * reset endpoint completion!
-		 * XXX: No idea how this hardware will react when stream rings
-		 * are enabled.
-		 */
-		xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
-				"Setting up input context for "
-				"configure endpoint command");
-		xhci_setup_input_ctx_for_quirk(xhci, slot_id,
-				ep_index, &deq_state);
-	}
-}
-
 static void xhci_endpoint_disable(struct usb_hcd *hcd,
 				  struct usb_host_endpoint *host_ep)
 {
@@ -3200,13 +3127,10 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd,
 		return;
 
 	/* Bail out if toggle is already being cleared by a endpoint reset */
-	spin_lock_irqsave(&xhci->lock, flags);
 	if (ep->ep_state & EP_HARD_CLEAR_TOGGLE) {
 		ep->ep_state &= ~EP_HARD_CLEAR_TOGGLE;
-		spin_unlock_irqrestore(&xhci->lock, flags);
 		return;
 	}
-	spin_unlock_irqrestore(&xhci->lock, flags);
 	/* Only interrupt and bulk ep's use data toggle, USB2 spec 5.5.4-> */
 	if (usb_endpoint_xfer_control(&host_ep->desc) ||
 	    usb_endpoint_xfer_isoc(&host_ep->desc))
@@ -3258,6 +3182,13 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd,
 
 	wait_for_completion(stop_cmd->completion);
 
+	err = xhci_vendor_sync_dev_ctx(xhci, udev->slot_id);
+	if (err) {
+		xhci_warn(xhci, "%s: Failed to sync device context failed, err=%d",
+			  __func__, err);
+		goto cleanup;
+	}
+
 	spin_lock_irqsave(&xhci->lock, flags);
 
 	/* config ep command clears toggle if add and drop ep flags are set */
@@ -3289,13 +3220,16 @@ static void xhci_endpoint_reset(struct usb_hcd *hcd,
 
 	wait_for_completion(cfg_cmd->completion);
 
+	err = xhci_vendor_sync_dev_ctx(xhci, udev->slot_id);
+	if (err)
+		xhci_warn(xhci, "%s: Failed to sync device context failed, err=%d",
+			  __func__, err);
+
 	xhci_free_command(xhci, cfg_cmd);
 cleanup:
 	xhci_free_command(xhci, stop_cmd);
-	spin_lock_irqsave(&xhci->lock, flags);
 	if (ep->ep_state & EP_SOFT_CLEAR_TOGGLE)
 		ep->ep_state &= ~EP_SOFT_CLEAR_TOGGLE;
-	spin_unlock_irqrestore(&xhci->lock, flags);
 }
 
 static int xhci_check_streams_endpoint(struct xhci_hcd *xhci,
@@ -3834,6 +3768,13 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
 	/* Wait for the Reset Device command to finish */
 	wait_for_completion(reset_device_cmd->completion);
 
+	ret = xhci_vendor_sync_dev_ctx(xhci, slot_id);
+	if (ret) {
+		xhci_warn(xhci, "%s: Failed to sync device context failed, err=%d",
+			  __func__, ret);
+		goto command_cleanup;
+	}
+
 	/* The Reset Device command can't fail, according to the 0.95/0.96 spec,
 	 * unless we tried to reset a slot ID that wasn't enabled,
 	 * or the device wasn't in the addressed or configured state.
@@ -3921,6 +3862,7 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
 	struct xhci_slot_ctx *slot_ctx;
 	int i, ret;
 
+#ifndef CONFIG_USB_DEFAULT_PERSIST
 	/*
 	 * We called pm_runtime_get_noresume when the device was attached.
 	 * Decrement the counter here to allow controller to runtime suspend
@@ -3928,6 +3870,7 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
 	 */
 	if (xhci->quirks & XHCI_RESET_ON_RESUME)
 		pm_runtime_put_noidle(hcd->self.controller);
+#endif
 
 	ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__);
 	/* If the host is halted due to driver unload, we still need to free the
@@ -3946,8 +3889,9 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
 		del_timer_sync(&virt_dev->eps[i].stop_cmd_timer);
 	}
 	virt_dev->udev = NULL;
-	xhci_disable_slot(xhci, udev->slot_id);
-	xhci_free_virt_device(xhci, udev->slot_id);
+	ret = xhci_disable_slot(xhci, udev->slot_id);
+	if (ret)
+		xhci_free_virt_device(xhci, udev->slot_id);
 }
 
 int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id)
@@ -3957,7 +3901,7 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id)
 	u32 state;
 	int ret = 0;
 
-	command = xhci_alloc_command(xhci, true, GFP_KERNEL);
+	command = xhci_alloc_command(xhci, false, GFP_KERNEL);
 	if (!command)
 		return -ENOMEM;
 
@@ -3982,15 +3926,6 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id)
 	}
 	xhci_ring_cmd_db(xhci);
 	spin_unlock_irqrestore(&xhci->lock, flags);
-
-	wait_for_completion(command->completion);
-
-	if (command->status != COMP_SUCCESS)
-		xhci_warn(xhci, "Unsuccessful disable slot %u command, status %d\n",
-			  slot_id, command->status);
-
-	xhci_free_command(xhci, command);
-
 	return ret;
 }
 
@@ -4079,6 +4014,14 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
 		xhci_warn(xhci, "Could not allocate xHCI USB device data structures\n");
 		goto disable_slot;
 	}
+
+	ret = xhci_vendor_sync_dev_ctx(xhci, slot_id);
+	if (ret) {
+		xhci_warn(xhci, "%s: Failed to sync device context failed, err=%d",
+			  __func__, ret);
+		goto disable_slot;
+	}
+
 	vdev = xhci->devs[slot_id];
 	slot_ctx = xhci_get_slot_ctx(xhci, vdev->out_ctx);
 	trace_xhci_alloc_dev(slot_ctx);
@@ -4087,20 +4030,23 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
 
 	xhci_debugfs_create_slot(xhci, slot_id);
 
+#ifndef CONFIG_USB_DEFAULT_PERSIST
 	/*
 	 * If resetting upon resume, we can't put the controller into runtime
 	 * suspend if there is a device attached.
 	 */
 	if (xhci->quirks & XHCI_RESET_ON_RESUME)
 		pm_runtime_get_noresume(hcd->self.controller);
+#endif
 
 	/* Is this a LS or FS device under a HS hub? */
 	/* Hub or peripherial? */
 	return 1;
 
 disable_slot:
-	xhci_disable_slot(xhci, udev->slot_id);
-	xhci_free_virt_device(xhci, udev->slot_id);
+	ret = xhci_disable_slot(xhci, udev->slot_id);
+	if (ret)
+		xhci_free_virt_device(xhci, udev->slot_id);
 
 	return 0;
 }
@@ -4209,6 +4155,13 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
 	/* ctrl tx can take up to 5 sec; XXX: need more time for xHC? */
 	wait_for_completion(command->completion);
 
+	ret = xhci_vendor_sync_dev_ctx(xhci, udev->slot_id);
+	if (ret) {
+		xhci_warn(xhci, "%s: Failed to sync device context failed, err=%d",
+			  __func__, ret);
+		goto out;
+	}
+
 	/* FIXME: From section 4.3.4: "Software shall be responsible for timing
 	 * the SetAddress() "recovery interval" required by USB and aborting the
 	 * command on a timeout.
@@ -4230,7 +4183,6 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
 
 		mutex_unlock(&xhci->mutex);
 		ret = xhci_disable_slot(xhci, udev->slot_id);
-		xhci_free_virt_device(xhci, udev->slot_id);
 		if (!ret)
 			xhci_alloc_dev(hcd, udev);
 		kfree(command->completion);
@@ -4293,10 +4245,11 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
 	return ret;
 }
 
-static int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
+int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
 {
 	return xhci_setup_device(hcd, udev, SETUP_CONTEXT_ADDRESS);
 }
+EXPORT_SYMBOL_GPL(xhci_address_device);
 
 static int xhci_enable_device(struct usb_hcd *hcd, struct usb_device *udev)
 {
@@ -4356,6 +4309,14 @@ static int __maybe_unused xhci_change_max_exit_latency(struct xhci_hcd *xhci,
 		return -ENOMEM;
 	}
 
+	ret = xhci_vendor_sync_dev_ctx(xhci, udev->slot_id);
+	if (ret) {
+		spin_unlock_irqrestore(&xhci->lock, flags);
+		xhci_warn(xhci, "%s: Failed to sync device context failed, err=%d",
+			  __func__, ret);
+		return ret;
+	}
+
 	xhci_slot_copy(xhci, command->in_ctx, virt_dev->out_ctx);
 	spin_unlock_irqrestore(&xhci->lock, flags);
 
@@ -4380,6 +4341,30 @@ static int __maybe_unused xhci_change_max_exit_latency(struct xhci_hcd *xhci,
 	return ret;
 }
 
+struct xhci_vendor_ops *xhci_vendor_get_ops(struct xhci_hcd *xhci)
+{
+	return xhci->vendor_ops;
+}
+EXPORT_SYMBOL_GPL(xhci_vendor_get_ops);
+
+int xhci_vendor_sync_dev_ctx(struct xhci_hcd *xhci, unsigned int slot_id)
+{
+	struct xhci_vendor_ops *ops = xhci_vendor_get_ops(xhci);
+
+	if (ops && ops->sync_dev_ctx)
+		return ops->sync_dev_ctx(xhci, slot_id);
+	return 0;
+}
+
+bool xhci_vendor_usb_offload_skip_urb(struct xhci_hcd *xhci, struct urb *urb)
+{
+	struct xhci_vendor_ops *ops = xhci_vendor_get_ops(xhci);
+
+	if (ops && ops->usb_offload_skip_urb)
+		return ops->usb_offload_skip_urb(xhci, urb);
+	return false;
+}
+
 #ifdef CONFIG_PM
 
 /* BESL to HIRD Encoding array for USB2 LPM */
@@ -4703,19 +4688,19 @@ static u16 xhci_calculate_u1_timeout(struct xhci_hcd *xhci,
 {
 	unsigned long long timeout_ns;
 
+	if (xhci->quirks & XHCI_INTEL_HOST)
+		timeout_ns = xhci_calculate_intel_u1_timeout(udev, desc);
+	else
+		timeout_ns = udev->u1_params.sel;
+
 	/* Prevent U1 if service interval is shorter than U1 exit latency */
 	if (usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc)) {
-		if (xhci_service_interval_to_ns(desc) <= udev->u1_params.mel) {
+		if (xhci_service_interval_to_ns(desc) <= timeout_ns) {
 			dev_dbg(&udev->dev, "Disable U1, ESIT shorter than exit latency\n");
 			return USB3_LPM_DISABLED;
 		}
 	}
 
-	if (xhci->quirks & XHCI_INTEL_HOST)
-		timeout_ns = xhci_calculate_intel_u1_timeout(udev, desc);
-	else
-		timeout_ns = udev->u1_params.sel;
-
 	/* The U1 timeout is encoded in 1us intervals.
 	 * Don't return a timeout of zero, because that's USB3_LPM_DISABLED.
 	 */
@@ -4767,19 +4752,19 @@ static u16 xhci_calculate_u2_timeout(struct xhci_hcd *xhci,
 {
 	unsigned long long timeout_ns;
 
+	if (xhci->quirks & XHCI_INTEL_HOST)
+		timeout_ns = xhci_calculate_intel_u2_timeout(udev, desc);
+	else
+		timeout_ns = udev->u2_params.sel;
+
 	/* Prevent U2 if service interval is shorter than U2 exit latency */
 	if (usb_endpoint_xfer_int(desc) || usb_endpoint_xfer_isoc(desc)) {
-		if (xhci_service_interval_to_ns(desc) <= udev->u2_params.mel) {
+		if (xhci_service_interval_to_ns(desc) <= timeout_ns) {
 			dev_dbg(&udev->dev, "Disable U2, ESIT shorter than exit latency\n");
 			return USB3_LPM_DISABLED;
 		}
 	}
 
-	if (xhci->quirks & XHCI_INTEL_HOST)
-		timeout_ns = xhci_calculate_intel_u2_timeout(udev, desc);
-	else
-		timeout_ns = udev->u2_params.sel;
-
 	/* The U2 timeout is encoded in 256us intervals */
 	timeout_ns = DIV_ROUND_UP_ULL(timeout_ns, 256 * 1000);
 	/* If the necessary timeout value is bigger than what we can set in the
@@ -5119,6 +5104,15 @@ static int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
 		return -ENOMEM;
 	}
 
+	ret = xhci_vendor_sync_dev_ctx(xhci, hdev->slot_id);
+	if (ret) {
+		xhci_warn(xhci, "%s: Failed to sync device context failed, err=%d",
+			  __func__, ret);
+		xhci_free_command(xhci, config_cmd);
+		spin_unlock_irqrestore(&xhci->lock, flags);
+		return ret;
+	}
+
 	xhci_slot_copy(xhci, config_cmd->in_ctx, vdev->out_ctx);
 	ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG);
 	slot_ctx = xhci_get_slot_ctx(xhci, config_cmd->in_ctx);
@@ -5442,10 +5436,20 @@ void xhci_init_driver(struct hc_driver *drv,
 			drv->reset = over->reset;
 		if (over->start)
 			drv->start = over->start;
+		if (over->add_endpoint)
+			drv->add_endpoint = over->add_endpoint;
+		if (over->drop_endpoint)
+			drv->drop_endpoint = over->drop_endpoint;
 		if (over->check_bandwidth)
 			drv->check_bandwidth = over->check_bandwidth;
 		if (over->reset_bandwidth)
 			drv->reset_bandwidth = over->reset_bandwidth;
+		if (over->address_device)
+			drv->address_device = over->address_device;
+		if (over->bus_suspend)
+			drv->bus_suspend = over->bus_suspend;
+		if (over->bus_resume)
+			drv->bus_resume = over->bus_resume;
 	}
 }
 EXPORT_SYMBOL_GPL(xhci_init_driver);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 059050f13..551132b79 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -17,6 +17,7 @@
 #include <linux/kernel.h>
 #include <linux/usb/hcd.h>
 #include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/android_kabi.h>
 
 /* Code sharing between pci-quirks and xhci hcd */
 #include	"xhci-ext-caps.h"
@@ -815,6 +816,9 @@ struct xhci_command {
 	struct completion		*completion;
 	union xhci_trb			*command_trb;
 	struct list_head		cmd_list;
+
+	ANDROID_KABI_RESERVE(1);
+	ANDROID_KABI_RESERVE(2);
 };
 
 /* drop context bitmasks */
@@ -924,6 +928,8 @@ struct xhci_bw_info {
 #define SS_BW_RESERVED		10
 
 struct xhci_virt_ep {
+	struct xhci_virt_device		*vdev;	/* parent */
+	unsigned int			ep_index;
 	struct xhci_ring		*ring;
 	/* Related to endpoints that are configured to use stream IDs only */
 	struct xhci_stream_info		*stream_info;
@@ -1002,6 +1008,7 @@ struct xhci_interval_bw_table {
 #define EP_CTX_PER_DEV		31
 
 struct xhci_virt_device {
+	int				slot_id;
 	struct usb_device		*udev;
 	/*
 	 * Commands to the hardware are passed an "input context" that
@@ -1418,7 +1425,7 @@ union xhci_trb {
 /* MFINDEX Wrap Event - microframe counter wrapped */
 #define TRB_MFINDEX_WRAP	39
 /* TRB IDs 40-47 reserved, 48-63 is vendor-defined */
-
+#define TRB_VENDOR_DEFINED_LOW	48
 /* Nec vendor-specific command completion event. */
 #define	TRB_NEC_CMD_COMP	48
 /* Get NEC firmware revision. */
@@ -1536,18 +1543,31 @@ struct xhci_segment {
 	void			*bounce_buf;
 	unsigned int		bounce_offs;
 	unsigned int		bounce_len;
+
+	ANDROID_KABI_RESERVE(1);
+};
+
+enum xhci_cancelled_td_status {
+	TD_DIRTY = 0,
+	TD_HALTED,
+	TD_CLEARING_CACHE,
+	TD_CLEARED,
 };
 
 struct xhci_td {
 	struct list_head	td_list;
 	struct list_head	cancelled_td_list;
+	int			status;
+	enum xhci_cancelled_td_status	cancel_status;
 	struct urb		*urb;
 	struct xhci_segment	*start_seg;
 	union xhci_trb		*first_trb;
 	union xhci_trb		*last_trb;
+	struct xhci_segment	*last_trb_seg;
 	struct xhci_segment	*bounce_seg;
 	/* actual_length of the URB has already been set */
 	bool			urb_length_set;
+	unsigned int		num_trbs;
 };
 
 /* xHCI command default timeout value */
@@ -1559,13 +1579,6 @@ struct xhci_cd {
 	union xhci_trb		*cmd_trb;
 };
 
-struct xhci_dequeue_state {
-	struct xhci_segment *new_deq_seg;
-	union xhci_trb *new_deq_ptr;
-	int new_cycle_state;
-	unsigned int stream_id;
-};
-
 enum xhci_ring_type {
 	TYPE_CTRL = 0,
 	TYPE_ISOC,
@@ -1621,6 +1634,9 @@ struct xhci_ring {
 	enum xhci_ring_type	type;
 	bool			last_td_was_short;
 	struct radix_tree_root	*trb_address_map;
+
+	ANDROID_KABI_RESERVE(1);
+	ANDROID_KABI_RESERVE(2);
 };
 
 struct xhci_erst_entry {
@@ -1638,6 +1654,8 @@ struct xhci_erst {
 	dma_addr_t		erst_dma_addr;
 	/* Num entries the ERST can contain */
 	unsigned int		erst_size;
+
+	ANDROID_KABI_RESERVE(1);
 };
 
 struct xhci_scratchpad {
@@ -1918,6 +1936,14 @@ struct xhci_hcd {
 	struct list_head	regset_list;
 
 	void			*dbc;
+
+	/* Used for bug 194461020 */
+	ANDROID_KABI_USE(1, struct xhci_vendor_ops *vendor_ops);
+
+	ANDROID_KABI_RESERVE(2);
+	ANDROID_KABI_RESERVE(3);
+	ANDROID_KABI_RESERVE(4);
+
 	/* platform-specific data -- must come last */
 	unsigned long		priv[] __aligned(sizeof(s64));
 };
@@ -1927,8 +1953,15 @@ struct xhci_driver_overrides {
 	size_t extra_priv_size;
 	int (*reset)(struct usb_hcd *hcd);
 	int (*start)(struct usb_hcd *hcd);
+	int (*add_endpoint)(struct usb_hcd *hcd, struct usb_device *udev,
+			    struct usb_host_endpoint *ep);
+	int (*drop_endpoint)(struct usb_hcd *hcd, struct usb_device *udev,
+			     struct usb_host_endpoint *ep);
 	int (*check_bandwidth)(struct usb_hcd *, struct usb_device *);
 	void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *);
+	int (*address_device)(struct usb_hcd *hcd, struct usb_device *udev);
+	int (*bus_suspend)(struct usb_hcd *hcd);
+	int (*bus_resume)(struct usb_hcd *hcd);
 };
 
 #define	XHCI_CFC_DELAY		10
@@ -2055,10 +2088,6 @@ void xhci_free_device_endpoint_resources(struct xhci_hcd *xhci,
 struct xhci_ring *xhci_dma_to_transfer_ring(
 		struct xhci_virt_ep *ep,
 		u64 address);
-struct xhci_ring *xhci_stream_id_to_ring(
-		struct xhci_virt_device *dev,
-		unsigned int ep_index,
-		unsigned int stream_id);
 struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
 		bool allocate_completion, gfp_t mem_flags);
 struct xhci_command *xhci_alloc_command_with_ctx(struct xhci_hcd *xhci,
@@ -2083,8 +2112,13 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks);
 void xhci_shutdown(struct usb_hcd *hcd);
 void xhci_init_driver(struct hc_driver *drv,
 		      const struct xhci_driver_overrides *over);
+int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
+		      struct usb_host_endpoint *ep);
+int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
+		       struct usb_host_endpoint *ep);
 int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev);
 void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev);
+int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev);
 int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id);
 int xhci_ext_cap_init(struct xhci_hcd *xhci);
 
@@ -2132,13 +2166,6 @@ int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd,
 		enum xhci_ep_reset_type reset_type);
 int xhci_queue_reset_device(struct xhci_hcd *xhci, struct xhci_command *cmd,
 		u32 slot_id);
-void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
-		unsigned int slot_id, unsigned int ep_index,
-		unsigned int stream_id, struct xhci_td *cur_td,
-		struct xhci_dequeue_state *state);
-void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci,
-		unsigned int slot_id, unsigned int ep_index,
-		struct xhci_dequeue_state *deq_state);
 void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int slot_id,
 			       unsigned int ep_index, unsigned int stream_id,
 			       struct xhci_td *td);
@@ -2199,6 +2226,53 @@ static inline struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci,
 					urb->stream_id);
 }
 
+/**
+ * struct xhci_vendor_ops - function callbacks for vendor specific operations
+ * @vendor_init: called for vendor init process
+ * @vendor_cleanup: called for vendor cleanup process
+ * @is_usb_offload_enabled: called to check if usb offload enabled
+ * @queue_irq_work: called to queue vendor specific irq work
+ * @alloc_dcbaa: called when allocating vendor specific dcbaa
+ * @free_dcbaa: called to free vendor specific dcbaa
+ * @alloc_transfer_ring: called when remote transfer ring allocation is required
+ * @free_transfer_ring: called to free vendor specific transfer ring
+ * @sync_dev_ctx: called when synchronization for device context is required
+ * @alloc_container_ctx: called when allocating vendor specific container context
+ * @free_container_ctx: called to free vendor specific container context
+ */
+struct xhci_vendor_ops {
+	int (*vendor_init)(struct xhci_hcd *xhci);
+	void (*vendor_cleanup)(struct xhci_hcd *xhci);
+	bool (*is_usb_offload_enabled)(struct xhci_hcd *xhci,
+				       struct xhci_virt_device *vdev,
+				       unsigned int ep_index);
+	irqreturn_t (*queue_irq_work)(struct xhci_hcd *xhci);
+
+	struct xhci_device_context_array *(*alloc_dcbaa)(struct xhci_hcd *xhci,
+							 gfp_t flags);
+	void (*free_dcbaa)(struct xhci_hcd *xhci);
+
+	struct xhci_ring *(*alloc_transfer_ring)(struct xhci_hcd *xhci,
+			u32 endpoint_type, enum xhci_ring_type ring_type,
+			unsigned int max_packet, gfp_t mem_flags);
+	void (*free_transfer_ring)(struct xhci_hcd *xhci,
+			struct xhci_virt_device *virt_dev, unsigned int ep_index);
+	int (*sync_dev_ctx)(struct xhci_hcd *xhci, unsigned int slot_id);
+	bool (*usb_offload_skip_urb)(struct xhci_hcd *xhci, struct urb *urb);
+	void (*alloc_container_ctx)(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx,
+				    int type, gfp_t flags);
+	void (*free_container_ctx)(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx);
+};
+
+struct xhci_vendor_ops *xhci_vendor_get_ops(struct xhci_hcd *xhci);
+
+int xhci_vendor_sync_dev_ctx(struct xhci_hcd *xhci, unsigned int slot_id);
+bool xhci_vendor_usb_offload_skip_urb(struct xhci_hcd *xhci, struct urb *urb);
+void xhci_vendor_free_transfer_ring(struct xhci_hcd *xhci,
+		struct xhci_virt_device *virt_dev, unsigned int ep_index);
+bool xhci_vendor_is_usb_offload_enabled(struct xhci_hcd *xhci,
+		struct xhci_virt_device *virt_dev, unsigned int ep_index);
+
 /*
  * TODO: As per spec Isochronous IDT transmissions are supported. We bypass
  * them anyways as we where unable to find a device that matches the
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 8a4a0d4db..d92eece5c 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -244,7 +244,6 @@ static const struct usb_device_id id_table[] = {
 	{ USB_DEVICE(0x1FB9, 0x0602) }, /* Lake Shore Model 648 Magnet Power Supply */
 	{ USB_DEVICE(0x1FB9, 0x0700) }, /* Lake Shore Model 737 VSM Controller */
 	{ USB_DEVICE(0x1FB9, 0x0701) }, /* Lake Shore Model 776 Hall Matrix */
-	{ USB_DEVICE(0x2184, 0x0030) }, /* GW Instek GDM-834x Digital Multimeter */
 	{ USB_DEVICE(0x2626, 0xEA60) }, /* Aruba Networks 7xxx USB Serial Console */
 	{ USB_DEVICE(0x3195, 0xF190) }, /* Link Instruments MSO-19 */
 	{ USB_DEVICE(0x3195, 0xF280) }, /* Link Instruments MSO-28 */
@@ -268,7 +267,6 @@ struct cp210x_serial_private {
 	speed_t			min_speed;
 	speed_t			max_speed;
 	bool			use_actual_rate;
-	bool			no_event_mode;
 };
 
 enum cp210x_event_state {
@@ -1340,16 +1338,12 @@ static void cp210x_change_speed(struct tty_struct *tty,
 
 static void cp210x_enable_event_mode(struct usb_serial_port *port)
 {
-	struct cp210x_serial_private *priv = usb_get_serial_data(port->serial);
 	struct cp210x_port_private *port_priv = usb_get_serial_port_data(port);
 	int ret;
 
 	if (port_priv->event_mode)
 		return;
 
-	if (priv->no_event_mode)
-		return;
-
 	port_priv->event_state = ES_DATA;
 	port_priv->event_mode = true;
 
@@ -1757,8 +1751,6 @@ static int cp2105_gpioconf_init(struct usb_serial *serial)
 
 	/*  2 banks of GPIO - One for the pins taken from each serial port */
 	if (intf_num == 0) {
-		priv->gc.ngpio = 2;
-
 		if (mode.eci == CP210X_PIN_MODE_MODEM) {
 			/* mark all GPIOs of this interface as reserved */
 			priv->gpio_altfunc = 0xff;
@@ -1769,9 +1761,8 @@ static int cp2105_gpioconf_init(struct usb_serial *serial)
 		priv->gpio_pushpull = (u8)((le16_to_cpu(config.gpio_mode) &
 						CP210X_ECI_GPIO_MODE_MASK) >>
 						CP210X_ECI_GPIO_MODE_OFFSET);
+		priv->gc.ngpio = 2;
 	} else if (intf_num == 1) {
-		priv->gc.ngpio = 3;
-
 		if (mode.sci == CP210X_PIN_MODE_MODEM) {
 			/* mark all GPIOs of this interface as reserved */
 			priv->gpio_altfunc = 0xff;
@@ -1782,6 +1773,7 @@ static int cp2105_gpioconf_init(struct usb_serial *serial)
 		priv->gpio_pushpull = (u8)((le16_to_cpu(config.gpio_mode) &
 						CP210X_SCI_GPIO_MODE_MASK) >>
 						CP210X_SCI_GPIO_MODE_OFFSET);
+		priv->gc.ngpio = 3;
 	} else {
 		return -ENODEV;
 	}
@@ -2101,46 +2093,6 @@ static void cp210x_init_max_speed(struct usb_serial *serial)
 	priv->use_actual_rate = use_actual_rate;
 }
 
-static void cp2102_determine_quirks(struct usb_serial *serial)
-{
-	struct cp210x_serial_private *priv = usb_get_serial_data(serial);
-	u8 *buf;
-	int ret;
-
-	buf = kmalloc(2, GFP_KERNEL);
-	if (!buf)
-		return;
-	/*
-	 * Some (possibly counterfeit) CP2102 do not support event-insertion
-	 * mode and respond differently to malformed vendor requests.
-	 * Specifically, they return one instead of two bytes when sent a
-	 * two-byte part-number request.
-	 */
-	ret = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
-			CP210X_VENDOR_SPECIFIC, REQTYPE_DEVICE_TO_HOST,
-			CP210X_GET_PARTNUM, 0, buf, 2, USB_CTRL_GET_TIMEOUT);
-	if (ret == 1) {
-		dev_dbg(&serial->interface->dev,
-				"device does not support event-insertion mode\n");
-		priv->no_event_mode = true;
-	}
-
-	kfree(buf);
-}
-
-static void cp210x_determine_quirks(struct usb_serial *serial)
-{
-	struct cp210x_serial_private *priv = usb_get_serial_data(serial);
-
-	switch (priv->partnum) {
-	case CP210X_PARTNUM_CP2102:
-		cp2102_determine_quirks(serial);
-		break;
-	default:
-		break;
-	}
-}
-
 static int cp210x_attach(struct usb_serial *serial)
 {
 	int result;
@@ -2161,7 +2113,6 @@ static int cp210x_attach(struct usb_serial *serial)
 
 	usb_set_serial_data(serial, priv);
 
-	cp210x_determine_quirks(serial);
 	cp210x_init_max_speed(serial);
 
 	result = cp210x_gpio_init(serial);
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 5636b8f52..537ef276c 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -255,7 +255,6 @@ static void option_instat_callback(struct urb *urb);
 #define QUECTEL_PRODUCT_EP06			0x0306
 #define QUECTEL_PRODUCT_EM05G			0x030a
 #define QUECTEL_PRODUCT_EM060K			0x030b
-#define QUECTEL_PRODUCT_EM05G_SG		0x0311
 #define QUECTEL_PRODUCT_EM12			0x0512
 #define QUECTEL_PRODUCT_RM500Q			0x0800
 #define QUECTEL_PRODUCT_RM520N			0x0801
@@ -1161,8 +1160,6 @@ static const struct usb_device_id option_ids[] = {
 	{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0, 0) },
 	{ USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05G, 0xff),
 	  .driver_info = RSVD(6) | ZLP },
-	{ USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05G_SG, 0xff),
-	  .driver_info = RSVD(6) | ZLP },
 	{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K, 0xff, 0x00, 0x40) },
 	{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K, 0xff, 0xff, 0x30) },
 	{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K, 0xff, 0xff, 0x40) },
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index b1e844bf3..c81f70530 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -165,9 +165,6 @@ static const struct usb_device_id id_table[] = {
 	{DEVICE_SWI(0x1199, 0x907b)},	/* Sierra Wireless EM74xx */
 	{DEVICE_SWI(0x1199, 0x9090)},	/* Sierra Wireless EM7565 QDL */
 	{DEVICE_SWI(0x1199, 0x9091)},	/* Sierra Wireless EM7565 */
-	{DEVICE_SWI(0x1199, 0x90d2)},	/* Sierra Wireless EM9191 QDL */
-	{DEVICE_SWI(0x1199, 0xc080)},	/* Sierra Wireless EM7590 QDL */
-	{DEVICE_SWI(0x1199, 0xc081)},	/* Sierra Wireless EM7590 */
 	{DEVICE_SWI(0x413c, 0x81a2)},	/* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */
 	{DEVICE_SWI(0x413c, 0x81a3)},	/* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */
 	{DEVICE_SWI(0x413c, 0x81a4)},	/* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index e5a971b83..a345f2982 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -102,6 +102,10 @@ static int slave_configure(struct scsi_device *sdev)
 	if (us->fflags & (US_FL_MAX_SECTORS_64 | US_FL_MAX_SECTORS_MIN)) {
 		unsigned int max_sectors = 64;
 
+		if (le16_to_cpu(us->pusb_dev->descriptor.idVendor) == 0x05e3 &&
+		    le16_to_cpu(us->pusb_dev->descriptor.idProduct) == 0x0749)
+			max_sectors = 128;
+
 		if (us->fflags & US_FL_MAX_SECTORS_MIN)
 			max_sectors = PAGE_SIZE >> 9;
 		if (queue_max_hw_sectors(sdev->request_queue) > max_sectors)
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 20dcbccb2..8d3848322 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -406,16 +406,6 @@ UNUSUAL_DEV(  0x04b8, 0x0602, 0x0110, 0x0110,
 		"785EPX Storage",
 		USB_SC_SCSI, USB_PR_BULK, NULL, US_FL_SINGLE_LUN),
 
-/*
- * Reported by James Buren <braewoods+lkml@braewoods.net>
- * Virtual ISOs cannot be remounted if ejected while the device is locked
- * Disable locking to mimic Windows behavior that bypasses the issue
- */
-UNUSUAL_DEV(  0x04c5, 0x2028, 0x0001, 0x0001,
-		"iODD",
-		"2531/2541",
-		USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE),
-
 /*
  * Not sure who reported this originally but
  * Pavel Machek <pavel@ucw.cz> reported that the extra US_FL_SINGLE_LUN
@@ -426,16 +416,9 @@ UNUSUAL_DEV(  0x04cb, 0x0100, 0x0000, 0x2210,
 		USB_SC_UFI, USB_PR_DEVICE, NULL, US_FL_FIX_INQUIRY | US_FL_SINGLE_LUN),
 
 /*
- * Reported by Ondrej Zary <linux@zary.sk>
+ * Reported by Ondrej Zary <linux@rainbow-software.org>
  * The device reports one sector more and breaks when that sector is accessed
- * Firmwares older than 2.6c (the latest one and the only that claims Linux
- * support) have also broken tag handling
  */
-UNUSUAL_DEV(  0x04ce, 0x0002, 0x0000, 0x026b,
-		"ScanLogic",
-		"SL11R-IDE",
-		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
-		US_FL_FIX_CAPACITY | US_FL_BULK_IGNORE_TAG),
 UNUSUAL_DEV(  0x04ce, 0x0002, 0x026c, 0x026c,
 		"ScanLogic",
 		"SL11R-IDE",
@@ -927,6 +910,12 @@ UNUSUAL_DEV(  0x05e3, 0x0723, 0x9451, 0x9451,
 		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
 		US_FL_SANE_SENSE ),
 
+UNUSUAL_DEV(  0x05e3, 0x0749, 0x0000, 0xffff,
+		"Genesys Logic",
+		"USB Storage",
+		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+		US_FL_GO_SLOW | US_FL_MAX_SECTORS_64 | US_FL_IGNORE_RESIDUE ),
+
 /*
  * Reported by Hanno Boeck <hanno@gmx.de>
  * Taken from the Lycoris Kernel
@@ -2302,16 +2291,6 @@ UNUSUAL_DEV(  0x2027, 0xa001, 0x0000, 0x9999,
 		USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init,
 		US_FL_SCM_MULT_TARG ),
 
-/*
- * Reported by DocMAX <mail@vacharakis.de>
- * and Thomas Weißschuh <linux@weissschuh.net>
- */
-UNUSUAL_DEV( 0x2109, 0x0715, 0x9999, 0x9999,
-		"VIA Labs, Inc.",
-		"VL817 SATA Bridge",
-		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
-		US_FL_IGNORE_UAS),
-
 UNUSUAL_DEV( 0x2116, 0x0320, 0x0001, 0x0001,
 		"ST",
 		"2A",
diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h
index 251778d14..464b4ff77 100644
--- a/drivers/usb/storage/unusual_uas.h
+++ b/drivers/usb/storage/unusual_uas.h
@@ -50,7 +50,7 @@ UNUSUAL_DEV(0x059f, 0x1061, 0x0000, 0x9999,
 		"LaCie",
 		"Rugged USB3-FW",
 		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
-		US_FL_NO_REPORT_OPCODES | US_FL_NO_SAME),
+		US_FL_IGNORE_UAS),
 
 /* Reported-by: Hongling Zeng <zenghongling@kylinos.cn> */
 UNUSUAL_DEV(0x090c, 0x2000, 0x0000, 0x9999,
@@ -76,6 +76,12 @@ UNUSUAL_DEV(0x0b05, 0x1932, 0x0000, 0x9999,
 		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
 		US_FL_IGNORE_UAS),
 
+UNUSUAL_DEV(0x0bc2, 0x2321, 0x0000, 0x9999,
+		"Seagate",
+		"Expansion HDD",
+		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+		US_FL_IGNORE_UAS),
+
 /* Reported-by: David Webb <djw@noc.ac.uk> */
 UNUSUAL_DEV(0x0bc2, 0x331a, 0x0000, 0x9999,
 		"Seagate",
@@ -118,6 +124,12 @@ UNUSUAL_DEV(0x152d, 0x0578, 0x0000, 0x9999,
 		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
 		US_FL_BROKEN_FUA),
 
+UNUSUAL_DEV(0x152d, 0x0583, 0x0000, 0x9999,
+		"JMicron",
+		"JMS583",
+		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+		US_FL_BROKEN_FUA | US_FL_NO_REPORT_OPCODES),
+
 /* Reported-by: Thinh Nguyen <thinhn@synopsys.com> */
 UNUSUAL_DEV(0x154b, 0xf00b, 0x0000, 0x9999,
 		"PNY",
@@ -139,6 +151,12 @@ UNUSUAL_DEV(0x17ef, 0x3899, 0x0000, 0x9999,
 		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
 		US_FL_IGNORE_UAS),
 
+UNUSUAL_DEV(0x174c, 0x55aa, 0x0000, 0x9999,
+		"WINTOGO",
+		"CHIPFANCIER",
+		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+		US_FL_BROKEN_FUA | US_FL_NO_REPORT_OPCODES),
+
 /* Reported-by: Hans de Goede <hdegoede@redhat.com> */
 UNUSUAL_DEV(0x2109, 0x0711, 0x0000, 0x9999,
 		"VIA",
diff --git a/drivers/usb/typec/altmodes/Kconfig b/drivers/usb/typec/altmodes/Kconfig
index 60d375e9c..1a6b5e872 100644
--- a/drivers/usb/typec/altmodes/Kconfig
+++ b/drivers/usb/typec/altmodes/Kconfig
@@ -4,6 +4,7 @@ menu "USB Type-C Alternate Mode drivers"
 
 config TYPEC_DP_ALTMODE
 	tristate "DisplayPort Alternate Mode driver"
+	depends on DRM
 	help
 	  DisplayPort USB Type-C Alternate Mode allows DisplayPort
 	  displays and adapters to be attached to the USB Type-C
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
index 5e293ccf0..8769cab04 100644
--- a/drivers/usb/typec/altmodes/displayport.c
+++ b/drivers/usb/typec/altmodes/displayport.c
@@ -11,12 +11,14 @@
 #include <linux/delay.h>
 #include <linux/mutex.h>
 #include <linux/module.h>
+#include <linux/property.h>
 #include <linux/usb/pd_vdo.h>
 #include <linux/usb/typec_dp.h>
+#include <drm/drm_connector.h>
 #include "displayport.h"
 
-#define DP_HEADER(_dp, cmd)		(VDO((_dp)->alt->svid, 1, cmd) | \
-					 VDO_OPOS(USB_TYPEC_DP_MODE))
+#define DP_HEADER(_dp, ver, cmd)	(VDO((_dp)->alt->svid, 1, ver, cmd)	\
+					 | VDO_OPOS(USB_TYPEC_DP_MODE))
 
 enum {
 	DP_CONF_USB,
@@ -45,6 +47,17 @@ enum {
 					 BIT(DP_PIN_ASSIGN_D) | \
 					 BIT(DP_PIN_ASSIGN_F))
 
+/*
+ * A UFP_U that uses a USB Type-C plug describes the pin assignments supported
+ * for the corresponding receptacle. (i.e., a UFP_D will describe the DFP_D pin
+ * assignments to which it connects), whereas a UFP_U that uses a USB Type-C
+ * receptacle describes its pin assignments directly (i.e., a UFP_D will
+ * describe its own UFP_D pin assignments).
+ */
+#define DP_CAP_PIN_ASSIGN(_cap_)	(((_cap_) & DP_CAP_RECEPTACLE) ? \
+					 DP_CAP_UFP_D_PIN_ASSIGN(_cap_) : \
+					 DP_CAP_DFP_D_PIN_ASSIGN(_cap_))
+
 enum dp_state {
 	DP_STATE_IDLE,
 	DP_STATE_ENTER,
@@ -57,11 +70,13 @@ struct dp_altmode {
 	struct typec_displayport_data data;
 
 	enum dp_state state;
+	bool hpd;
 
 	struct mutex lock; /* device lock */
 	struct work_struct work;
 	struct typec_altmode *alt;
 	const struct typec_altmode *port;
+	struct fwnode_handle *connector_fwnode;
 };
 
 static int dp_altmode_notify(struct dp_altmode *dp)
@@ -82,14 +97,10 @@ static int dp_altmode_configure(struct dp_altmode *dp, u8 con)
 		return 0;
 	case DP_STATUS_CON_DFP_D:
 		conf |= DP_CONF_UFP_U_AS_DFP_D;
-		pin_assign = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo) &
-			     DP_CAP_DFP_D_PIN_ASSIGN(dp->port->vdo);
 		break;
 	case DP_STATUS_CON_UFP_D:
 	case DP_STATUS_CON_BOTH: /* NOTE: First acting as DP source */
 		conf |= DP_CONF_UFP_U_AS_UFP_D;
-		pin_assign = DP_CAP_PIN_ASSIGN_UFP_D(dp->alt->vdo) &
-				 DP_CAP_PIN_ASSIGN_DFP_D(dp->port->vdo);
 		break;
 	default:
 		break;
@@ -97,6 +108,8 @@ static int dp_altmode_configure(struct dp_altmode *dp, u8 con)
 
 	/* Determining the initial pin assignment. */
 	if (!DP_CONF_GET_PIN_ASSIGN(dp->data.conf)) {
+		pin_assign = DP_CAP_PIN_ASSIGN(dp->alt->vdo);
+
 		/* Is USB together with DP preferred */
 		if (dp->data.status & DP_STATUS_PREFER_MULTI_FUNC &&
 		    pin_assign & DP_PIN_ASSIGN_MULTI_FUNC_MASK)
@@ -104,6 +117,13 @@ static int dp_altmode_configure(struct dp_altmode *dp, u8 con)
 		else if (pin_assign & DP_PIN_ASSIGN_DP_ONLY_MASK)
 			pin_assign &= DP_PIN_ASSIGN_DP_ONLY_MASK;
 
+		/*
+		 * DFP_U never selects Pin Assignment E when Pin Assignment C
+		 * and possibly Pin Assignment D are offered by the UFP_U.
+		 */
+		if (pin_assign & (BIT(DP_PIN_ASSIGN_C) | BIT(DP_PIN_ASSIGN_D)))
+			pin_assign &= ~BIT(DP_PIN_ASSIGN_E);
+
 		if (!pin_assign)
 			return -EINVAL;
 
@@ -118,6 +138,7 @@ static int dp_altmode_configure(struct dp_altmode *dp, u8 con)
 static int dp_altmode_status_update(struct dp_altmode *dp)
 {
 	bool configured = !!DP_CONF_GET_PIN_ASSIGN(dp->data.conf);
+	bool hpd = !!(dp->data.status & DP_STATUS_HPD_STATE);
 	u8 con = DP_STATUS_CONNECTION(dp->data.status);
 	int ret = 0;
 
@@ -130,6 +151,11 @@ static int dp_altmode_status_update(struct dp_altmode *dp)
 		ret = dp_altmode_configure(dp, con);
 		if (!ret)
 			dp->state = DP_STATE_CONFIGURE;
+	} else {
+		if (dp->hpd != hpd) {
+			drm_connector_oob_hotplug_event(dp->connector_fwnode);
+			dp->hpd = hpd;
+		}
 	}
 
 	return ret;
@@ -156,9 +182,14 @@ static int dp_altmode_configured(struct dp_altmode *dp)
 
 static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf)
 {
-	u32 header = DP_HEADER(dp, DP_CMD_CONFIGURE);
+	int svdm_version = typec_altmode_get_svdm_version(dp->alt);
+	u32 header;
 	int ret;
 
+	if (svdm_version < 0)
+		return svdm_version;
+
+	header = DP_HEADER(dp, svdm_version, DP_CMD_CONFIGURE);
 	ret = typec_altmode_notify(dp->alt, TYPEC_STATE_SAFE, &dp->data);
 	if (ret) {
 		dev_err(&dp->alt->dev,
@@ -181,6 +212,7 @@ static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf)
 static void dp_altmode_work(struct work_struct *work)
 {
 	struct dp_altmode *dp = container_of(work, struct dp_altmode, work);
+	int svdm_version;
 	u32 header;
 	u32 vdo;
 	int ret;
@@ -194,7 +226,10 @@ static void dp_altmode_work(struct work_struct *work)
 			dev_err(&dp->alt->dev, "failed to enter mode\n");
 		break;
 	case DP_STATE_UPDATE:
-		header = DP_HEADER(dp, DP_CMD_STATUS_UPDATE);
+		svdm_version = typec_altmode_get_svdm_version(dp->alt);
+		if (svdm_version < 0)
+			break;
+		header = DP_HEADER(dp, svdm_version, DP_CMD_STATUS_UPDATE);
 		vdo = 1;
 		ret = typec_altmode_vdm(dp->alt, header, &vdo, 2);
 		if (ret)
@@ -435,10 +470,7 @@ pin_assignment_store(struct device *dev, struct device_attribute *attr,
 		goto out_unlock;
 	}
 
-	if (DP_CONF_CURRENTLY(dp->data.conf) == DP_CONF_DFP_D)
-		assignments = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo);
-	else
-		assignments = DP_CAP_DFP_D_PIN_ASSIGN(dp->alt->vdo);
+	assignments = DP_CAP_PIN_ASSIGN(dp->alt->vdo);
 
 	if (!(DP_CONF_GET_PIN_ASSIGN(conf) & assignments)) {
 		ret = -EINVAL;
@@ -475,10 +507,7 @@ static ssize_t pin_assignment_show(struct device *dev,
 
 	cur = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf));
 
-	if (DP_CONF_CURRENTLY(dp->data.conf) == DP_CONF_DFP_D)
-		assignments = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo);
-	else
-		assignments = DP_CAP_DFP_D_PIN_ASSIGN(dp->alt->vdo);
+	assignments = DP_CAP_PIN_ASSIGN(dp->alt->vdo);
 
 	for (i = 0; assignments; assignments >>= 1, i++) {
 		if (assignments & 1) {
@@ -512,6 +541,7 @@ static const struct attribute_group dp_altmode_group = {
 int dp_altmode_probe(struct typec_altmode *alt)
 {
 	const struct typec_altmode *port = typec_altmode_get_partner(alt);
+	struct fwnode_handle *fwnode;
 	struct dp_altmode *dp;
 	int ret;
 
@@ -540,6 +570,11 @@ int dp_altmode_probe(struct typec_altmode *alt)
 	alt->desc = "DisplayPort";
 	alt->ops = &dp_altmode_ops;
 
+	fwnode = dev_fwnode(alt->dev.parent->parent); /* typec_port fwnode */
+	dp->connector_fwnode = fwnode_find_reference(fwnode, "displayport", 0);
+	if (IS_ERR(dp->connector_fwnode))
+		dp->connector_fwnode = NULL;
+
 	typec_altmode_set_drvdata(alt, dp);
 
 	dp->state = DP_STATE_ENTER;
@@ -555,6 +590,13 @@ void dp_altmode_remove(struct typec_altmode *alt)
 
 	sysfs_remove_group(&alt->dev.kobj, &dp_altmode_group);
 	cancel_work_sync(&dp->work);
+
+	if (dp->connector_fwnode) {
+		if (dp->hpd)
+			drm_connector_oob_hotplug_event(dp->connector_fwnode);
+
+		fwnode_handle_put(dp->connector_fwnode);
+	}
 }
 EXPORT_SYMBOL_GPL(dp_altmode_remove);
 
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 9d3a35b20..cb32a838e 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -11,6 +11,8 @@
 #include <linux/mutex.h>
 #include <linux/property.h>
 #include <linux/slab.h>
+#include <linux/usb/pd_vdo.h>
+#include <linux/android_kabi.h>
 
 #include "bus.h"
 
@@ -18,6 +20,8 @@ struct typec_plug {
 	struct device			dev;
 	enum typec_plug_index		index;
 	struct ida			mode_ids;
+	int				num_altmodes;
+	ANDROID_KABI_RESERVE(1);
 };
 
 struct typec_cable {
@@ -25,6 +29,8 @@ struct typec_cable {
 	enum typec_plug_type		type;
 	struct usb_pd_identity		*identity;
 	unsigned int			active:1;
+	u16				pd_revision; /* 0300H = "3.0" */
+	ANDROID_KABI_RESERVE(1);
 };
 
 struct typec_partner {
@@ -33,6 +39,10 @@ struct typec_partner {
 	struct usb_pd_identity		*identity;
 	enum typec_accessory		accessory;
 	struct ida			mode_ids;
+	int				num_altmodes;
+	u16				pd_revision; /* 0300H = "3.0" */
+	enum usb_pd_svdm_ver		svdm_version;
+	ANDROID_KABI_RESERVE(1);
 };
 
 struct typec_port {
@@ -54,6 +64,7 @@ struct typec_port {
 
 	const struct typec_capability	*cap;
 	const struct typec_operations   *ops;
+	ANDROID_KABI_RESERVE(1);
 };
 
 #define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev)
@@ -81,6 +92,29 @@ static const char * const typec_accessory_modes[] = {
 	[TYPEC_ACCESSORY_DEBUG]		= "debug",
 };
 
+/* Product types defined in USB PD Specification R3.0 V2.0 */
+static const char * const product_type_ufp[8] = {
+	[IDH_PTYPE_NOT_UFP]		= "not_ufp",
+	[IDH_PTYPE_HUB]			= "hub",
+	[IDH_PTYPE_PERIPH]		= "peripheral",
+	[IDH_PTYPE_PSD]			= "psd",
+	[IDH_PTYPE_AMA]			= "ama",
+};
+
+static const char * const product_type_dfp[8] = {
+	[IDH_PTYPE_NOT_DFP]		= "not_dfp",
+	[IDH_PTYPE_DFP_HUB]		= "hub",
+	[IDH_PTYPE_DFP_HOST]		= "host",
+	[IDH_PTYPE_DFP_PB]		= "power_brick",
+};
+
+static const char * const product_type_cable[8] = {
+	[IDH_PTYPE_NOT_CABLE]		= "not_cable",
+	[IDH_PTYPE_PCABLE]		= "passive",
+	[IDH_PTYPE_ACABLE]		= "active",
+	[IDH_PTYPE_VPD]			= "vpd",
+};
+
 static struct usb_pd_identity *get_pd_identity(struct device *dev)
 {
 	if (is_typec_partner(dev)) {
@@ -95,6 +129,32 @@ static struct usb_pd_identity *get_pd_identity(struct device *dev)
 	return NULL;
 }
 
+static const char *get_pd_product_type(struct device *dev)
+{
+	struct typec_port *port = to_typec_port(dev->parent);
+	struct usb_pd_identity *id = get_pd_identity(dev);
+	const char *ptype = NULL;
+
+	if (is_typec_partner(dev)) {
+		if (!id)
+			return NULL;
+
+		if (port->data_role == TYPEC_HOST)
+			ptype = product_type_ufp[PD_IDH_PTYPE(id->id_header)];
+		else
+			ptype = product_type_dfp[PD_IDH_DFP_PTYPE(id->id_header)];
+	} else if (is_typec_cable(dev)) {
+		if (id)
+			ptype = product_type_cable[PD_IDH_PTYPE(id->id_header)];
+		else
+			ptype = to_typec_cable(dev)->active ?
+				product_type_cable[IDH_PTYPE_ACABLE] :
+				product_type_cable[IDH_PTYPE_PCABLE];
+	}
+
+	return ptype;
+}
+
 static ssize_t id_header_show(struct device *dev, struct device_attribute *attr,
 			      char *buf)
 {
@@ -122,10 +182,40 @@ static ssize_t product_show(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RO(product);
 
+static ssize_t product_type_vdo1_show(struct device *dev, struct device_attribute *attr,
+				      char *buf)
+{
+	struct usb_pd_identity *id = get_pd_identity(dev);
+
+	return sysfs_emit(buf, "0x%08x\n", id->vdo[0]);
+}
+static DEVICE_ATTR_RO(product_type_vdo1);
+
+static ssize_t product_type_vdo2_show(struct device *dev, struct device_attribute *attr,
+				      char *buf)
+{
+	struct usb_pd_identity *id = get_pd_identity(dev);
+
+	return sysfs_emit(buf, "0x%08x\n", id->vdo[1]);
+}
+static DEVICE_ATTR_RO(product_type_vdo2);
+
+static ssize_t product_type_vdo3_show(struct device *dev, struct device_attribute *attr,
+				      char *buf)
+{
+	struct usb_pd_identity *id = get_pd_identity(dev);
+
+	return sysfs_emit(buf, "0x%08x\n", id->vdo[2]);
+}
+static DEVICE_ATTR_RO(product_type_vdo3);
+
 static struct attribute *usb_pd_id_attrs[] = {
 	&dev_attr_id_header.attr,
 	&dev_attr_cert_stat.attr,
 	&dev_attr_product.attr,
+	&dev_attr_product_type_vdo1.attr,
+	&dev_attr_product_type_vdo2.attr,
+	&dev_attr_product_type_vdo3.attr,
 	NULL
 };
 
@@ -139,13 +229,54 @@ static const struct attribute_group *usb_pd_id_groups[] = {
 	NULL,
 };
 
+static void typec_product_type_notify(struct device *dev)
+{
+	char *envp[2] = { };
+	const char *ptype;
+
+	ptype = get_pd_product_type(dev);
+	if (!ptype)
+		return;
+
+	sysfs_notify(&dev->kobj, NULL, "type");
+
+	envp[0] = kasprintf(GFP_KERNEL, "PRODUCT_TYPE=%s", ptype);
+	if (!envp[0])
+		return;
+
+	kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
+	kfree(envp[0]);
+}
+
 static void typec_report_identity(struct device *dev)
 {
 	sysfs_notify(&dev->kobj, "identity", "id_header");
 	sysfs_notify(&dev->kobj, "identity", "cert_stat");
 	sysfs_notify(&dev->kobj, "identity", "product");
+	sysfs_notify(&dev->kobj, "identity", "product_type_vdo1");
+	sysfs_notify(&dev->kobj, "identity", "product_type_vdo2");
+	sysfs_notify(&dev->kobj, "identity", "product_type_vdo3");
+	typec_product_type_notify(dev);
 }
 
+static ssize_t
+type_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	const char *ptype;
+
+	ptype = get_pd_product_type(dev);
+	if (!ptype)
+		return 0;
+
+	return sysfs_emit(buf, "%s\n", ptype);
+}
+static DEVICE_ATTR_RO(type);
+
+static ssize_t usb_power_delivery_revision_show(struct device *dev,
+						struct device_attribute *attr,
+						char *buf);
+static DEVICE_ATTR_RO(usb_power_delivery_revision);
+
 /* ------------------------------------------------------------------------- */
 /* Alternate Modes */
 
@@ -382,7 +513,7 @@ static umode_t typec_altmode_attr_is_visible(struct kobject *kobj,
 	return attr->mode;
 }
 
-static struct attribute_group typec_altmode_group = {
+static const struct attribute_group typec_altmode_group = {
 	.is_visible = typec_altmode_attr_is_visible,
 	.attrs = typec_altmode_attrs,
 };
@@ -484,6 +615,10 @@ typec_register_altmode(struct device *parent,
 	if (is_typec_partner(parent))
 		alt->adev.dev.bus = &typec_bus;
 
+	/* Plug alt modes need a class to generate udev events. */
+	if (is_typec_plug(parent))
+		alt->adev.dev.class = typec_class;
+
 	ret = device_register(&alt->adev.dev);
 	if (ret) {
 		dev_err(parent, "failed to register alternate mode (%d)\n",
@@ -534,12 +669,61 @@ static ssize_t supports_usb_power_delivery_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(supports_usb_power_delivery);
 
+static ssize_t number_of_alternate_modes_show(struct device *dev, struct device_attribute *attr,
+					      char *buf)
+{
+	struct typec_partner *partner;
+	struct typec_plug *plug;
+	int num_altmodes;
+
+	if (is_typec_partner(dev)) {
+		partner = to_typec_partner(dev);
+		num_altmodes = partner->num_altmodes;
+	} else if (is_typec_plug(dev)) {
+		plug = to_typec_plug(dev);
+		num_altmodes = plug->num_altmodes;
+	} else {
+		return 0;
+	}
+
+	return sysfs_emit(buf, "%d\n", num_altmodes);
+}
+static DEVICE_ATTR_RO(number_of_alternate_modes);
+
 static struct attribute *typec_partner_attrs[] = {
 	&dev_attr_accessory_mode.attr,
 	&dev_attr_supports_usb_power_delivery.attr,
+	&dev_attr_number_of_alternate_modes.attr,
+	&dev_attr_type.attr,
+	&dev_attr_usb_power_delivery_revision.attr,
+	NULL
+};
+
+static umode_t typec_partner_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n)
+{
+	struct typec_partner *partner = to_typec_partner(kobj_to_dev(kobj));
+
+	if (attr == &dev_attr_number_of_alternate_modes.attr) {
+		if (partner->num_altmodes < 0)
+			return 0;
+	}
+
+	if (attr == &dev_attr_type.attr)
+		if (!get_pd_product_type(kobj_to_dev(kobj)))
+			return 0;
+
+	return attr->mode;
+}
+
+static const struct attribute_group typec_partner_group = {
+	.is_visible = typec_partner_attr_is_visible,
+	.attrs = typec_partner_attrs
+};
+
+static const struct attribute_group *typec_partner_groups[] = {
+	&typec_partner_group,
 	NULL
 };
-ATTRIBUTE_GROUPS(typec_partner);
 
 static void typec_partner_release(struct device *dev)
 {
@@ -572,6 +756,61 @@ int typec_partner_set_identity(struct typec_partner *partner)
 }
 EXPORT_SYMBOL_GPL(typec_partner_set_identity);
 
+/**
+ * typec_partner_set_pd_revision - Set the PD revision supported by the partner
+ * @partner: The partner to be updated.
+ * @pd_revision:  USB Power Delivery Specification Revision supported by partner
+ *
+ * This routine is used to report that the PD revision of the port partner has
+ * become available.
+ */
+void typec_partner_set_pd_revision(struct typec_partner *partner, u16 pd_revision)
+{
+	if (partner->pd_revision == pd_revision)
+		return;
+
+	partner->pd_revision = pd_revision;
+	sysfs_notify(&partner->dev.kobj, NULL, "usb_power_delivery_revision");
+	if (pd_revision != 0 && !partner->usb_pd) {
+		partner->usb_pd = 1;
+		sysfs_notify(&partner->dev.kobj, NULL,
+			     "supports_usb_power_delivery");
+	}
+	kobject_uevent(&partner->dev.kobj, KOBJ_CHANGE);
+}
+EXPORT_SYMBOL_GPL(typec_partner_set_pd_revision);
+
+/**
+ * typec_partner_set_num_altmodes - Set the number of available partner altmodes
+ * @partner: The partner to be updated.
+ * @num_altmodes: The number of altmodes we want to specify as available.
+ *
+ * This routine is used to report the number of alternate modes supported by the
+ * partner. This value is *not* enforced in alternate mode registration routines.
+ *
+ * @partner.num_altmodes is set to -1 on partner registration, denoting that
+ * a valid value has not been set for it yet.
+ *
+ * Returns 0 on success or negative error number on failure.
+ */
+int typec_partner_set_num_altmodes(struct typec_partner *partner, int num_altmodes)
+{
+	int ret;
+
+	if (num_altmodes < 0)
+		return -EINVAL;
+
+	partner->num_altmodes = num_altmodes;
+	ret = sysfs_update_group(&partner->dev.kobj, &typec_partner_group);
+	if (ret < 0)
+		return ret;
+
+	sysfs_notify(&partner->dev.kobj, NULL, "number_of_alternate_modes");
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(typec_partner_set_num_altmodes);
+
 /**
  * typec_partner_register_altmode - Register USB Type-C Partner Alternate Mode
  * @partner: USB Type-C Partner that supports the alternate mode
@@ -592,6 +831,20 @@ typec_partner_register_altmode(struct typec_partner *partner,
 }
 EXPORT_SYMBOL_GPL(typec_partner_register_altmode);
 
+/**
+ * typec_partner_set_svdm_version - Set negotiated Structured VDM (SVDM) Version
+ * @partner: USB Type-C Partner that supports SVDM
+ * @svdm_version: Negotiated SVDM Version
+ *
+ * This routine is used to save the negotiated SVDM Version.
+ */
+void typec_partner_set_svdm_version(struct typec_partner *partner,
+				   enum usb_pd_svdm_ver svdm_version)
+{
+	partner->svdm_version = svdm_version;
+}
+EXPORT_SYMBOL_GPL(typec_partner_set_svdm_version);
+
 /**
  * typec_register_partner - Register a USB Type-C Partner
  * @port: The USB Type-C Port the partner is connected to
@@ -614,6 +867,9 @@ struct typec_partner *typec_register_partner(struct typec_port *port,
 	ida_init(&partner->mode_ids);
 	partner->usb_pd = desc->usb_pd;
 	partner->accessory = desc->accessory;
+	partner->num_altmodes = -1;
+	partner->pd_revision = desc->pd_revision;
+	partner->svdm_version = port->cap->svdm_version;
 
 	if (desc->identity) {
 		/*
@@ -664,11 +920,70 @@ static void typec_plug_release(struct device *dev)
 	kfree(plug);
 }
 
+static struct attribute *typec_plug_attrs[] = {
+	&dev_attr_number_of_alternate_modes.attr,
+	NULL
+};
+
+static umode_t typec_plug_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n)
+{
+	struct typec_plug *plug = to_typec_plug(kobj_to_dev(kobj));
+
+	if (attr == &dev_attr_number_of_alternate_modes.attr) {
+		if (plug->num_altmodes < 0)
+			return 0;
+	}
+
+	return attr->mode;
+}
+
+static const struct attribute_group typec_plug_group = {
+	.is_visible = typec_plug_attr_is_visible,
+	.attrs = typec_plug_attrs
+};
+
+static const struct attribute_group *typec_plug_groups[] = {
+	&typec_plug_group,
+	NULL
+};
+
 static const struct device_type typec_plug_dev_type = {
 	.name = "typec_plug",
+	.groups = typec_plug_groups,
 	.release = typec_plug_release,
 };
 
+/**
+ * typec_plug_set_num_altmodes - Set the number of available plug altmodes
+ * @plug: The plug to be updated.
+ * @num_altmodes: The number of altmodes we want to specify as available.
+ *
+ * This routine is used to report the number of alternate modes supported by the
+ * plug. This value is *not* enforced in alternate mode registration routines.
+ *
+ * @plug.num_altmodes is set to -1 on plug registration, denoting that
+ * a valid value has not been set for it yet.
+ *
+ * Returns 0 on success or negative error number on failure.
+ */
+int typec_plug_set_num_altmodes(struct typec_plug *plug, int num_altmodes)
+{
+	int ret;
+
+	if (num_altmodes < 0)
+		return -EINVAL;
+
+	plug->num_altmodes = num_altmodes;
+	ret = sysfs_update_group(&plug->dev.kobj, &typec_plug_group);
+	if (ret < 0)
+		return ret;
+
+	sysfs_notify(&plug->dev.kobj, NULL, "number_of_alternate_modes");
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(typec_plug_set_num_altmodes);
+
 /**
  * typec_plug_register_altmode - Register USB Type-C Cable Plug Alternate Mode
  * @plug: USB Type-C Cable Plug that supports the alternate mode
@@ -714,6 +1029,7 @@ struct typec_plug *typec_register_plug(struct typec_cable *cable,
 	sprintf(name, "plug%d", desc->index);
 
 	ida_init(&plug->mode_ids);
+	plug->num_altmodes = -1;
 	plug->index = desc->index;
 	plug->dev.class = typec_class;
 	plug->dev.parent = &cable->dev;
@@ -746,15 +1062,6 @@ EXPORT_SYMBOL_GPL(typec_unregister_plug);
 
 /* Type-C Cables */
 
-static ssize_t
-type_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
-	struct typec_cable *cable = to_typec_cable(dev);
-
-	return sprintf(buf, "%s\n", cable->active ? "active" : "passive");
-}
-static DEVICE_ATTR_RO(type);
-
 static const char * const typec_plug_types[] = {
 	[USB_PLUG_NONE]		= "unknown",
 	[USB_PLUG_TYPE_A]	= "type-a",
@@ -775,6 +1082,7 @@ static DEVICE_ATTR_RO(plug_type);
 static struct attribute *typec_cable_attrs[] = {
 	&dev_attr_type.attr,
 	&dev_attr_plug_type.attr,
+	&dev_attr_usb_power_delivery_revision.attr,
 	NULL
 };
 ATTRIBUTE_GROUPS(typec_cable);
@@ -877,6 +1185,7 @@ struct typec_cable *typec_register_cable(struct typec_port *port,
 
 	cable->type = desc->type;
 	cable->active = desc->active;
+	cable->pd_revision = desc->pd_revision;
 
 	if (desc->identity) {
 		/*
@@ -1246,11 +1555,23 @@ static ssize_t usb_power_delivery_revision_show(struct device *dev,
 						struct device_attribute *attr,
 						char *buf)
 {
-	struct typec_port *p = to_typec_port(dev);
+	u16 rev = 0;
 
-	return sprintf(buf, "%d\n", (p->cap->pd_revision >> 8) & 0xff);
+	if (is_typec_partner(dev)) {
+		struct typec_partner *partner = to_typec_partner(dev);
+
+		rev = partner->pd_revision;
+	} else if (is_typec_cable(dev)) {
+		struct typec_cable *cable = to_typec_cable(dev);
+
+		rev = cable->pd_revision;
+	} else if (is_typec_port(dev)) {
+		struct typec_port *p = to_typec_port(dev);
+
+		rev = p->cap->pd_revision;
+	}
+	return sysfs_emit(buf, "%d.%d\n", (rev >> 8) & 0xff, (rev >> 4) & 0xf);
 }
-static DEVICE_ATTR_RO(usb_power_delivery_revision);
 
 static ssize_t orientation_show(struct device *dev,
 				   struct device_attribute *attr,
@@ -1311,7 +1632,7 @@ static umode_t typec_attr_is_visible(struct kobject *kobj,
 	return attr->mode;
 }
 
-static struct attribute_group typec_group = {
+static const struct attribute_group typec_group = {
 	.is_visible = typec_attr_is_visible,
 	.attrs = typec_attrs,
 };
@@ -1354,6 +1675,11 @@ const struct device_type typec_port_dev_type = {
 /* --------------------------------------- */
 /* Driver callbacks to report role updates */
 
+static int partner_match(struct device *dev, void *data)
+{
+	return is_typec_partner(dev);
+}
+
 /**
  * typec_set_data_role - Report data role change
  * @port: The USB Type-C Port where the role was changed
@@ -1363,12 +1689,23 @@ const struct device_type typec_port_dev_type = {
  */
 void typec_set_data_role(struct typec_port *port, enum typec_data_role role)
 {
+	struct device *partner_dev;
+
 	if (port->data_role == role)
 		return;
 
 	port->data_role = role;
 	sysfs_notify(&port->dev.kobj, NULL, "data_role");
 	kobject_uevent(&port->dev.kobj, KOBJ_CHANGE);
+
+	partner_dev = device_find_child(&port->dev, NULL, partner_match);
+	if (!partner_dev)
+		return;
+
+	if (to_typec_partner(partner_dev)->identity)
+		typec_product_type_notify(partner_dev);
+
+	put_device(partner_dev);
 }
 EXPORT_SYMBOL_GPL(typec_set_data_role);
 
@@ -1409,11 +1746,6 @@ void typec_set_vconn_role(struct typec_port *port, enum typec_role role)
 }
 EXPORT_SYMBOL_GPL(typec_set_vconn_role);
 
-static int partner_match(struct device *dev, void *data)
-{
-	return is_typec_partner(dev);
-}
-
 /**
  * typec_set_pwr_opmode - Report changed power operation mode
  * @port: The USB Type-C Port where the mode was changed
@@ -1584,6 +1916,33 @@ EXPORT_SYMBOL_GPL(typec_set_mode);
 
 /* --------------------------------------- */
 
+/**
+ * typec_get_negotiated_svdm_version - Get negotiated SVDM Version
+ * @port: USB Type-C Port.
+ *
+ * Get the negotiated SVDM Version. The Version is set to the port default
+ * value stored in typec_capability on partner registration, and updated after
+ * a successful Discover Identity if the negotiated value is less than the
+ * default value.
+ *
+ * Returns usb_pd_svdm_ver if the partner has been registered otherwise -ENODEV.
+ */
+int typec_get_negotiated_svdm_version(struct typec_port *port)
+{
+	enum usb_pd_svdm_ver svdm_version;
+	struct device *partner_dev;
+
+	partner_dev = device_find_child(&port->dev, NULL, partner_match);
+	if (!partner_dev)
+		return -ENODEV;
+
+	svdm_version = to_typec_partner(partner_dev)->svdm_version;
+	put_device(partner_dev);
+
+	return svdm_version;
+}
+EXPORT_SYMBOL_GPL(typec_get_negotiated_svdm_version);
+
 /**
  * typec_get_drvdata - Return private driver data pointer
  * @port: USB Type-C port
@@ -1625,6 +1984,62 @@ typec_port_register_altmode(struct typec_port *port,
 }
 EXPORT_SYMBOL_GPL(typec_port_register_altmode);
 
+#ifdef CONFIG_NO_GKI
+void typec_port_register_altmodes(struct typec_port *port,
+	const struct typec_altmode_ops *ops, void *drvdata,
+	struct typec_altmode **altmodes, size_t n)
+{
+	struct fwnode_handle *altmodes_node, *child;
+	struct typec_altmode_desc desc;
+	struct typec_altmode *alt;
+	size_t index = 0;
+	u32 svid, vdo;
+	int ret;
+
+	altmodes_node = device_get_named_child_node(&port->dev, "altmodes");
+	if (!altmodes_node)
+		return; /* No altmodes specified */
+
+	fwnode_for_each_child_node(altmodes_node, child) {
+		ret = fwnode_property_read_u32(child, "svid", &svid);
+		if (ret) {
+			dev_err(&port->dev, "Error reading svid for altmode %s\n",
+				fwnode_get_name(child));
+			continue;
+		}
+
+		ret = fwnode_property_read_u32(child, "vdo", &vdo);
+		if (ret) {
+			dev_err(&port->dev, "Error reading vdo for altmode %s\n",
+				fwnode_get_name(child));
+			continue;
+		}
+
+		if (index >= n) {
+			dev_err(&port->dev, "Error not enough space for altmode %s\n",
+				fwnode_get_name(child));
+			continue;
+		}
+
+		desc.svid = svid;
+		desc.vdo = vdo;
+		desc.mode = index + 1;
+		alt = typec_port_register_altmode(port, &desc);
+		if (IS_ERR(alt)) {
+			dev_err(&port->dev, "Error registering altmode %s\n",
+				fwnode_get_name(child));
+			continue;
+		}
+
+		alt->ops = ops;
+		typec_altmode_set_drvdata(alt, drvdata);
+		altmodes[index] = alt;
+		index++;
+	}
+}
+EXPORT_SYMBOL_GPL(typec_port_register_altmodes);
+#endif /* CONFIG_NO_GKI */
+
 /**
  * typec_register_port - Register a USB Type-C Port
  * @parent: Parent device
diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig
index 2192d7c4f..5e9b37b3f 100644
--- a/drivers/usb/typec/ucsi/Kconfig
+++ b/drivers/usb/typec/ucsi/Kconfig
@@ -3,6 +3,7 @@
 config TYPEC_UCSI
 	tristate "USB Type-C Connector System Software Interface driver"
 	depends on !CPU_BIG_ENDIAN
+	depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH
 	help
 	  USB Type-C Connector System Software Interface (UCSI) is a
 	  specification for an interface that allows the operating system to
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
index 1cf924f3a..cd3676b96 100644
--- a/drivers/video/backlight/pwm_bl.c
+++ b/drivers/video/backlight/pwm_bl.c
@@ -603,6 +603,8 @@ static int pwm_backlight_probe(struct platform_device *pdev)
 		pb->scale = data->max_brightness;
 	}
 
+	pwm_adjust_config(pb->pwm);
+
 	pb->lth_brightness = data->lth_brightness * (div_u64(state.period,
 				pb->scale));
 
diff --git a/drivers/video/fbdev/kyro/fbdev.c b/drivers/video/fbdev/kyro/fbdev.c
index 25801e8e3..8fbde92ae 100644
--- a/drivers/video/fbdev/kyro/fbdev.c
+++ b/drivers/video/fbdev/kyro/fbdev.c
@@ -372,11 +372,6 @@ static int kyro_dev_overlay_viewport_set(u32 x, u32 y, u32 ulWidth, u32 ulHeight
 		/* probably haven't called CreateOverlay yet */
 		return -EINVAL;
 
-	if (ulWidth == 0 || ulWidth == 0xffffffff ||
-	    ulHeight == 0 || ulHeight == 0xffffffff ||
-	    (x < 2 && ulWidth + 2 == 0))
-		return -EINVAL;
-
 	/* Stop Ramdac Output */
 	DisableRamdacOutput(deviceInfo.pSTGReg);
 
@@ -399,9 +394,6 @@ static int kyrofb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
 {
 	struct kyrofb_info *par = info->par;
 
-	if (!var->pixclock)
-		return -EINVAL;
-
 	if (var->bits_per_pixel != 16 && var->bits_per_pixel != 32) {
 		printk(KERN_WARNING "kyrofb: depth not supported: %u\n", var->bits_per_pixel);
 		return -EINVAL;
diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h
index b0dcc0733..26b4246cb 100644
--- a/include/drm/bridge/analogix_dp.h
+++ b/include/drm/bridge/analogix_dp.h
@@ -8,6 +8,7 @@
 #define _ANALOGIX_DP_H_
 
 #include <drm/drm_crtc.h>
+#include <sound/hdmi-codec.h>
 
 struct analogix_dp_device;
 
@@ -15,11 +16,21 @@ enum analogix_dp_devtype {
 	EXYNOS_DP,
 	RK3288_DP,
 	RK3399_EDP,
+	RK3568_EDP,
+	RK3588_EDP,
 };
 
 static inline bool is_rockchip(enum analogix_dp_devtype type)
 {
-	return type == RK3288_DP || type == RK3399_EDP;
+	switch (type) {
+	case RK3288_DP:
+	case RK3399_EDP:
+	case RK3568_EDP:
+	case RK3588_EDP:
+		return true;
+	default:
+		return false;
+	}
 }
 
 struct analogix_dp_plat_data {
@@ -28,18 +39,26 @@ struct analogix_dp_plat_data {
 	struct drm_encoder *encoder;
 	struct drm_connector *connector;
 	bool skip_connector;
+	bool ssc;
+
+	bool split_mode;
+	struct analogix_dp_device *left;
+	struct analogix_dp_device *right;
 
 	int (*power_on_start)(struct analogix_dp_plat_data *);
 	int (*power_on_end)(struct analogix_dp_plat_data *);
 	int (*power_off)(struct analogix_dp_plat_data *);
 	int (*attach)(struct analogix_dp_plat_data *, struct drm_bridge *,
 		      struct drm_connector *);
+	void (*detach)(struct analogix_dp_plat_data *, struct drm_bridge *);
 	int (*get_modes)(struct analogix_dp_plat_data *,
 			 struct drm_connector *);
+	void (*convert_to_split_mode)(struct drm_display_mode *);
+	void (*convert_to_origin_mode)(struct drm_display_mode *);
 };
 
-int analogix_dp_resume(struct analogix_dp_device *dp);
-int analogix_dp_suspend(struct analogix_dp_device *dp);
+int analogix_dp_runtime_resume(struct analogix_dp_device *dp);
+int analogix_dp_runtime_suspend(struct analogix_dp_device *dp);
 
 struct analogix_dp_device *
 analogix_dp_probe(struct device *dev, struct analogix_dp_plat_data *plat_data);
@@ -50,4 +69,13 @@ void analogix_dp_remove(struct analogix_dp_device *dp);
 int analogix_dp_start_crc(struct drm_connector *connector);
 int analogix_dp_stop_crc(struct drm_connector *connector);
 
+int analogix_dp_audio_hw_params(struct analogix_dp_device *dp,
+				struct hdmi_codec_daifmt *daifmt,
+				struct hdmi_codec_params *params);
+void analogix_dp_audio_shutdown(struct analogix_dp_device *dp);
+int analogix_dp_audio_startup(struct analogix_dp_device *dp);
+int analogix_dp_audio_get_eld(struct analogix_dp_device *dp,
+			      u8 *buf, size_t len);
+int analogix_dp_loader_protect(struct analogix_dp_device *dp);
+
 #endif /* _ANALOGIX_DP_H_ */
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
index ea34ca146..b1057b624 100644
--- a/include/drm/bridge/dw_hdmi.h
+++ b/include/drm/bridge/dw_hdmi.h
@@ -6,12 +6,15 @@
 #ifndef __DW_HDMI__
 #define __DW_HDMI__
 
+#include <drm/drm_property.h>
 #include <sound/hdmi-codec.h>
+#include <media/cec.h>
 
 struct drm_display_info;
 struct drm_display_mode;
 struct drm_encoder;
 struct dw_hdmi;
+struct dw_hdmi_qp;
 struct platform_device;
 
 /**
@@ -92,6 +95,13 @@ enum dw_hdmi_phy_type {
 	DW_HDMI_PHY_VENDOR_PHY = 0xfe,
 };
 
+struct dw_hdmi_audio_tmds_n {
+	unsigned long tmds;
+	unsigned int n_32k;
+	unsigned int n_44k1;
+	unsigned int n_48k;
+};
+
 struct dw_hdmi_mpll_config {
 	unsigned long mpixelclock;
 	struct {
@@ -112,6 +122,15 @@ struct dw_hdmi_phy_config {
 	u16 vlev_ctr;   /* voltage level control */
 };
 
+struct dw_hdmi_link_config {
+	bool dsc_mode;
+	bool frl_mode;
+	int frl_lanes;
+	int rate_per_lane;
+	int hcactive;
+	u8 pps_payload[128];
+};
+
 struct dw_hdmi_phy_ops {
 	int (*init)(struct dw_hdmi *hdmi, void *data,
 		    const struct drm_display_info *display,
@@ -123,12 +142,48 @@ struct dw_hdmi_phy_ops {
 	void (*setup_hpd)(struct dw_hdmi *hdmi, void *data);
 };
 
+struct dw_hdmi_qp_phy_ops {
+	int (*init)(struct dw_hdmi_qp *hdmi, void *data,
+		    struct drm_display_mode *mode);
+	void (*disable)(struct dw_hdmi_qp *hdmi, void *data);
+	enum drm_connector_status (*read_hpd)(struct dw_hdmi_qp *hdmi,
+					      void *data);
+	void (*update_hpd)(struct dw_hdmi_qp *hdmi, void *data,
+			   bool force, bool disabled, bool rxsense);
+	void (*setup_hpd)(struct dw_hdmi_qp *hdmi, void *data);
+	void (*set_mode)(struct dw_hdmi_qp *dw_hdmi, void *data,
+			 u32 mode_mask, bool enable);
+};
+
+struct dw_hdmi_property_ops {
+	void (*attach_properties)(struct drm_connector *connector,
+				  unsigned int color, int version,
+				  void *data);
+	void (*destroy_properties)(struct drm_connector *connector,
+				   void *data);
+	int (*set_property)(struct drm_connector *connector,
+			    struct drm_connector_state *state,
+			    struct drm_property *property,
+			    u64 val,
+			    void *data);
+	int (*get_property)(struct drm_connector *connector,
+			    const struct drm_connector_state *state,
+			    struct drm_property *property,
+			    u64 *val,
+			    void *data);
+};
+
 struct dw_hdmi_plat_data {
 	struct regmap *regm;
 
+	unsigned long input_bus_format;
 	unsigned long input_bus_encoding;
+	unsigned int max_tmdsclk;
 	bool use_drm_infoframe;
 	bool ycbcr_420_allowed;
+	bool unsupported_yuv_input;
+	bool unsupported_deep_color;
+	bool is_hdmi_qp;
 
 	/*
 	 * Private data passed to all the .mode_valid() and .configure_phy()
@@ -137,22 +192,46 @@ struct dw_hdmi_plat_data {
 	void *priv_data;
 
 	/* Platform-specific mode validation (optional). */
-	enum drm_mode_status (*mode_valid)(struct dw_hdmi *hdmi, void *data,
+	enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
+					   void *data,
 					   const struct drm_display_info *info,
 					   const struct drm_display_mode *mode);
 
 	/* Vendor PHY support */
 	const struct dw_hdmi_phy_ops *phy_ops;
+	const struct dw_hdmi_qp_phy_ops *qp_phy_ops;
 	const char *phy_name;
 	void *phy_data;
 	unsigned int phy_force_vendor;
+	const struct dw_hdmi_audio_tmds_n *tmds_n_table;
 
 	/* Synopsys PHY support */
 	const struct dw_hdmi_mpll_config *mpll_cfg;
+	const struct dw_hdmi_mpll_config *mpll_cfg_420;
 	const struct dw_hdmi_curr_ctrl *cur_ctr;
 	const struct dw_hdmi_phy_config *phy_config;
 	int (*configure_phy)(struct dw_hdmi *hdmi, void *data,
 			     unsigned long mpixelclock);
+
+	unsigned long (*get_input_bus_format)(void *data);
+	unsigned long (*get_output_bus_format)(void *data);
+	unsigned long (*get_enc_in_encoding)(void *data);
+	unsigned long (*get_enc_out_encoding)(void *data);
+	unsigned long (*get_quant_range)(void *data);
+	struct drm_property *(*get_hdr_property)(void *data);
+	struct drm_property_blob *(*get_hdr_blob)(void *data);
+	bool (*get_color_changed)(void *data);
+	int (*get_yuv422_format)(struct drm_connector *connector,
+				 struct edid *edid);
+	int (*get_edid_dsc_info)(void *data, struct edid *edid);
+	int (*get_next_hdr_data)(void *data, struct edid *edid,
+				 struct drm_connector *connector);
+	struct dw_hdmi_link_config *(*get_link_cfg)(void *data);
+	void (*set_grf_cfg)(void *data);
+
+	/* Vendor Property support */
+	const struct dw_hdmi_property_ops *property_ops;
+	struct drm_connector *connector;
 };
 
 struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
@@ -161,8 +240,9 @@ void dw_hdmi_remove(struct dw_hdmi *hdmi);
 void dw_hdmi_unbind(struct dw_hdmi *hdmi);
 struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev,
 			     struct drm_encoder *encoder,
-			     const struct dw_hdmi_plat_data *plat_data);
+			     struct dw_hdmi_plat_data *plat_data);
 
+void dw_hdmi_suspend(struct dw_hdmi *hdmi);
 void dw_hdmi_resume(struct dw_hdmi *hdmi);
 
 void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense);
@@ -192,5 +272,28 @@ enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi,
 void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data,
 			    bool force, bool disabled, bool rxsense);
 void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data);
+void dw_hdmi_set_quant_range(struct dw_hdmi *hdmi);
+void dw_hdmi_set_output_type(struct dw_hdmi *hdmi, u64 val);
+bool dw_hdmi_get_output_whether_hdmi(struct dw_hdmi *hdmi);
+int dw_hdmi_get_output_type_cap(struct dw_hdmi *hdmi);
+void dw_hdmi_set_cec_adap(struct dw_hdmi *hdmi, struct cec_adapter *adap);
+
+void dw_hdmi_qp_unbind(struct dw_hdmi_qp *hdmi);
+struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
+				struct drm_encoder *encoder,
+				struct dw_hdmi_plat_data *plat_data);
+void dw_hdmi_qp_suspend(struct device *dev, struct dw_hdmi_qp *hdmi);
+void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi);
+void dw_hdmi_qp_cec_set_hpd(struct dw_hdmi_qp *hdmi, bool plug_in, bool change);
+void dw_hdmi_qp_set_cec_adap(struct dw_hdmi_qp *hdmi, struct cec_adapter *adap);
+int dw_hdmi_qp_set_earc(struct dw_hdmi_qp *hdmi);
+void dw_hdmi_qp_set_sample_rate(struct dw_hdmi_qp *hdmi, unsigned int rate);
+void dw_hdmi_qp_set_channel_count(struct dw_hdmi_qp *hdmi, unsigned int cnt);
+void dw_hdmi_qp_set_channel_status(struct dw_hdmi_qp *hdmi, u8 *channel_status);
+void dw_hdmi_qp_set_channel_allocation(struct dw_hdmi_qp *hdmi, unsigned int ca);
+void dw_hdmi_qp_audio_enable(struct dw_hdmi_qp *hdmi);
+void dw_hdmi_qp_audio_disable(struct dw_hdmi_qp *hdmi);
+int dw_hdmi_qp_set_plugged_cb(struct dw_hdmi_qp *hdmi, hdmi_codec_plugged_cb fn,
+			      struct device *codec_dev);
 
 #endif /* __IMX_HDMI_H__ */
diff --git a/include/drm/bridge/dw_mipi_dsi.h b/include/drm/bridge/dw_mipi_dsi.h
index bda8aa7c2..f89b0476a 100644
--- a/include/drm/bridge/dw_mipi_dsi.h
+++ b/include/drm/bridge/dw_mipi_dsi.h
@@ -66,5 +66,6 @@ void dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi);
 int dw_mipi_dsi_bind(struct dw_mipi_dsi *dsi, struct drm_encoder *encoder);
 void dw_mipi_dsi_unbind(struct dw_mipi_dsi *dsi);
 void dw_mipi_dsi_set_slave(struct dw_mipi_dsi *dsi, struct dw_mipi_dsi *slave);
+struct drm_connector *dw_mipi_dsi_get_connector(struct dw_mipi_dsi *dsi);
 
 #endif /* __DW_MIPI_DSI__ */
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 928136556..f8cd5d8e5 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -175,6 +175,48 @@ struct drm_scdc {
 	struct drm_scrambling scrambling;
 };
 
+#ifdef CONFIG_NO_GKI
+/**
+ * struct drm_hdmi_dsc_cap - DSC capabilities of HDMI sink
+ *
+ * Describes the DSC support provided by HDMI 2.1 sink.
+ * The information is fetched fom additional HFVSDB blocks defined
+ * for HDMI 2.1.
+ */
+struct drm_hdmi_dsc_cap {
+	/** @v_1p2: flag for dsc1.2 version support by sink */
+	bool v_1p2;
+
+	/** @native_420: Does sink support DSC with 4:2:0 compression */
+	bool native_420;
+
+	/**
+	 * @all_bpp: Does sink support all bpp with 4:4:4: or 4:2:2
+	 * compressed formats
+	 */
+	bool all_bpp;
+
+	/**
+	 * @bpc_supported: compressed bpc supported by sink : 10, 12 or 16 bpc
+	 */
+	u8 bpc_supported;
+
+	/** @max_slices: maximum number of Horizontal slices supported by */
+	u8 max_slices;
+
+	/** @clk_per_slice : max pixel clock in MHz supported per slice */
+	int clk_per_slice;
+
+	/** @max_lanes : dsc max lanes supported for Fixed rate Link training */
+	u8 max_lanes;
+
+	/** @max_frl_rate_per_lane : maximum frl rate with DSC per lane */
+	u8 max_frl_rate_per_lane;
+
+	/** @total_chunk_kbytes: max size of chunks in KBs supported per line*/
+	u8 total_chunk_kbytes;
+};
+#endif
 
 /**
  * struct drm_hdmi_info - runtime information about the connected HDMI sink
@@ -207,6 +249,17 @@ struct drm_hdmi_info {
 
 	/** @y420_dc_modes: bitmap of deep color support index */
 	u8 y420_dc_modes;
+
+#ifdef CONFIG_NO_GKI
+	/** @max_frl_rate_per_lane: support fixed rate link */
+	u8 max_frl_rate_per_lane;
+
+	/** @max_lanes: supported by sink */
+	u8 max_lanes;
+
+	/** @dsc_cap: DSC capabilities of the sink */
+	struct drm_hdmi_dsc_cap dsc_cap;
+#endif
 };
 
 /**
@@ -1596,6 +1649,7 @@ drm_connector_is_unregistered(struct drm_connector *connector)
 		DRM_CONNECTOR_UNREGISTERED;
 }
 
+void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode);
 const char *drm_get_connector_type_name(unsigned int connector_type);
 const char *drm_get_connector_status_name(enum drm_connector_status status);
 const char *drm_get_subpixel_order_name(enum subpixel_order order);
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 59b51a09c..3a3d9d887 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -287,7 +287,16 @@ struct drm_crtc_state {
 	 * NULL) is an array of &struct drm_color_lut.
 	 */
 	struct drm_property_blob *gamma_lut;
-
+#if defined(CONFIG_ROCKCHIP_DRM_CUBIC_LUT)
+	/**
+	 * @cubic_lut:
+	 *
+	 * Cubic Lookup table for converting pixel data. See
+	 * drm_crtc_enable_color_mgmt(). The blob (if not NULL) is a 3D array
+	 * of &struct drm_color_lut.
+	 */
+	struct drm_property_blob *cubic_lut;
+#endif
 	/**
 	 * @target_vblank:
 	 *
diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
index e57d0440f..e395d1fc1 100644
--- a/include/drm/drm_drv.h
+++ b/include/drm/drm_drv.h
@@ -29,6 +29,7 @@
 
 #include <linux/list.h>
 #include <linux/irqreturn.h>
+#include <linux/uuid.h>
 
 #include <drm/drm_device.h>
 
@@ -460,6 +461,15 @@ struct drm_driver {
 	int (*gem_prime_mmap)(struct drm_gem_object *obj,
 				struct vm_area_struct *vma);
 
+	/**
+	 * @gem_prime_get_uuid
+	 *
+	 * get_uuid hook for GEM drivers. Retrieves the virtio uuid of the
+	 * given GEM buffer.
+	 */
+	int (*gem_prime_get_uuid)(struct drm_gem_object *obj,
+				  uuid_t *uuid);
+
 	/**
 	 * @dumb_create:
 	 *
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index 4526b6a1e..6f082761e 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -229,6 +229,38 @@ struct detailed_timing {
 				    DRM_EDID_YCBCR420_DC_36 | \
 				    DRM_EDID_YCBCR420_DC_30)
 
+#ifdef CONFIG_NO_GKI
+/* HDMI 2.1 additional fields */
+#define DRM_EDID_MAX_FRL_RATE_MASK		0xf0
+#define DRM_EDID_FAPA_START_LOCATION		(1 << 0)
+#define DRM_EDID_ALLM				(1 << 1)
+#define DRM_EDID_FVA				(1 << 2)
+
+/* Deep Color specific */
+#define DRM_EDID_DC_30BIT_420			(1 << 0)
+#define DRM_EDID_DC_36BIT_420			(1 << 1)
+#define DRM_EDID_DC_48BIT_420			(1 << 2)
+
+/* VRR specific */
+#define DRM_EDID_CNMVRR				(1 << 3)
+#define DRM_EDID_CINEMA_VRR			(1 << 4)
+#define DRM_EDID_MDELTA				(1 << 5)
+#define DRM_EDID_VRR_MAX_UPPER_MASK		0xc0
+#define DRM_EDID_VRR_MAX_LOWER_MASK		0xff
+#define DRM_EDID_VRR_MIN_MASK			0x3f
+
+/* DSC specific */
+#define DRM_EDID_DSC_10BPC			(1 << 0)
+#define DRM_EDID_DSC_12BPC			(1 << 1)
+#define DRM_EDID_DSC_16BPC			(1 << 2)
+#define DRM_EDID_DSC_ALL_BPP			(1 << 3)
+#define DRM_EDID_DSC_NATIVE_420			(1 << 6)
+#define DRM_EDID_DSC_1P2			(1 << 7)
+#define DRM_EDID_DSC_MAX_FRL_RATE_MASK		0xf0
+#define DRM_EDID_DSC_MAX_SLICES			0xf
+#define DRM_EDID_DSC_TOTAL_CHUNK_KBYTES		0x3f
+#endif
+
 /* ELD Header Block */
 #define DRM_ELD_HEADER_BLOCK_SIZE	4
 
diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h
index 42d04607d..726cfe0ff 100644
--- a/include/drm/drm_file.h
+++ b/include/drm/drm_file.h
@@ -411,6 +411,9 @@ void drm_event_cancel_free(struct drm_device *dev,
 			   struct drm_pending_event *p);
 void drm_send_event_locked(struct drm_device *dev, struct drm_pending_event *e);
 void drm_send_event(struct drm_device *dev, struct drm_pending_event *e);
+void drm_send_event_timestamp_locked(struct drm_device *dev,
+				     struct drm_pending_event *e,
+				     ktime_t timestamp);
 
 struct file *mock_drm_getfile(struct drm_minor *minor, unsigned int flags);
 
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index 360e6377e..952f475a6 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -19,12 +19,18 @@ struct drm_dsc_picture_parameter_set;
 #define MIPI_DSI_MSG_REQ_ACK	BIT(0)
 /* use Low Power Mode to transmit message */
 #define MIPI_DSI_MSG_USE_LPM	BIT(1)
+/* read mipi_dsi_msg.ctrl and unicast to only that ctrls */
+#define MIPI_DSI_MSG_UNICAST	BIT(2)
+/* Stack all commands until lastcommand bit and trigger all in one go */
+#define MIPI_DSI_MSG_LASTCOMMAND BIT(3)
 
 /**
  * struct mipi_dsi_msg - read/write DSI buffer
  * @channel: virtual channel id
  * @type: payload data type
  * @flags: flags controlling this message transmission
+ * @ctrl: ctrl index to transmit on
+ * @wait_ms: duration in ms to wait after message transmission
  * @tx_len: length of @tx_buf
  * @tx_buf: data to be written
  * @rx_len: length of @rx_buf
@@ -34,6 +40,8 @@ struct mipi_dsi_msg {
 	u8 channel;
 	u8 type;
 	u16 flags;
+	u32 ctrl;
+	u32 wait_ms;
 
 	size_t tx_len;
 	const void *tx_buf;
@@ -132,6 +140,10 @@ struct mipi_dsi_host *of_find_mipi_dsi_host_by_node(struct device_node *node);
 #define MIPI_DSI_CLOCK_NON_CONTINUOUS	BIT(10)
 /* transmit data in low power */
 #define MIPI_DSI_MODE_LPM		BIT(11)
+/* disable BLLP area */
+#define MIPI_DSI_MODE_VIDEO_BLLP	BIT(12)
+/* disable EOF BLLP area */
+#define MIPI_DSI_MODE_VIDEO_EOF_BLLP	BIT(13)
 
 enum mipi_dsi_pixel_format {
 	MIPI_DSI_FMT_RGB888,
diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h
index a18f73eb3..76d114569 100644
--- a/include/drm/drm_mode_config.h
+++ b/include/drm/drm_mode_config.h
@@ -794,6 +794,19 @@ struct drm_mode_config {
 	 */
 	struct drm_property *gamma_lut_size_property;
 
+#if defined(CONFIG_ROCKCHIP_DRM_CUBIC_LUT)
+	/**
+	 * @cubic_lut_property: Optional CRTC property to set the 3D LUT used to
+	 * convert color spaces.
+	 */
+	struct drm_property *cubic_lut_property;
+	/**
+	 * @cubic_lut_size_property: Optional CRTC property for the size of the
+	 * 3D LUT as supported by the driver (read-only).
+	 */
+	struct drm_property *cubic_lut_size_property;
+#endif
+
 	/**
 	 * @suggested_x_property: Optional connector property with a hint for
 	 * the position of the output on the host's screen.
diff --git a/include/drm/drm_mode_object.h b/include/drm/drm_mode_object.h
index c34a3e803..6292fa663 100644
--- a/include/drm/drm_mode_object.h
+++ b/include/drm/drm_mode_object.h
@@ -60,7 +60,7 @@ struct drm_mode_object {
 	void (*free_cb)(struct kref *kref);
 };
 
-#define DRM_OBJECT_MAX_PROPERTY 24
+#define DRM_OBJECT_MAX_PROPERTY 64
 /**
  * struct drm_object_properties - property tracking for &drm_mode_object
  */
diff --git a/include/drm/drm_prime.h b/include/drm/drm_prime.h
index 0f69f9fbf..a9d0c6b08 100644
--- a/include/drm/drm_prime.h
+++ b/include/drm/drm_prime.h
@@ -107,5 +107,6 @@ void drm_prime_gem_destroy(struct drm_gem_object *obj, struct sg_table *sg);
 int drm_prime_sg_to_page_addr_arrays(struct sg_table *sgt, struct page **pages,
 				     dma_addr_t *addrs, int max_pages);
 
+int drm_gem_dmabuf_get_uuid(struct dma_buf *dma_buf, uuid_t *uuid);
 
 #endif /* __DRM_PRIME_H__ */
diff --git a/include/dt-bindings/soc/rockchip,boot-mode.h b/include/dt-bindings/soc/rockchip,boot-mode.h
index 4b0914c09..1436e1d32 100644
--- a/include/dt-bindings/soc/rockchip,boot-mode.h
+++ b/include/dt-bindings/soc/rockchip,boot-mode.h
@@ -10,7 +10,15 @@
 #define BOOT_BL_DOWNLOAD	(REBOOT_FLAG + 1)
 /* enter recovery */
 #define BOOT_RECOVERY		(REBOOT_FLAG + 3)
- /* enter fastboot mode */
+/* reboot by panic */
+#define BOOT_PANIC		(REBOOT_FLAG + 7)
+/* reboot by watchdog */
+#define BOOT_WATCHDOG		(REBOOT_FLAG + 8)
+/* enter fastboot mode */
 #define BOOT_FASTBOOT		(REBOOT_FLAG + 9)
+/* enter charging mode */
+#define BOOT_CHARGING		(REBOOT_FLAG + 11)
+/* enter usb mass storage mode */
+#define BOOT_UMS		(REBOOT_FLAG + 12)
 
 #endif
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 03a5de5f9..e22088f27 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -32,6 +32,7 @@
 #define CLK_OPS_PARENT_ENABLE	BIT(12)
 /* duty cycle call may be forwarded to the parent clock */
 #define CLK_DUTY_CYCLE_PARENT	BIT(13)
+#define CLK_DONT_HOLD_STATE	BIT(14) /* Don't hold state */
 
 struct clk;
 struct clk_hw;
@@ -252,6 +253,12 @@ struct clk_ops {
 	int		(*init)(struct clk_hw *hw);
 	void		(*terminate)(struct clk_hw *hw);
 	void		(*debug_init)(struct clk_hw *hw, struct dentry *dentry);
+	int		(*pre_rate_change)(struct clk_hw *hw,
+					   unsigned long rate,
+					   unsigned long new_rate);
+	int		(*post_rate_change)(struct clk_hw *hw,
+					    unsigned long old_rate,
+					    unsigned long rate);
 };
 
 /**
@@ -599,6 +606,7 @@ struct clk_divider {
 	u8		shift;
 	u8		width;
 	u8		flags;
+	unsigned long	max_prate;
 	const struct clk_div_table	*table;
 	spinlock_t	*lock;
 };
@@ -947,6 +955,7 @@ struct clk_fractional_divider {
 	u8		nwidth;
 	u32		nmask;
 	u8		flags;
+	unsigned long	max_prate;
 	void		(*approximation)(struct clk_hw *hw,
 				unsigned long rate, unsigned long *parent_rate,
 				unsigned long *m, unsigned long *n);
@@ -957,6 +966,7 @@ struct clk_fractional_divider {
 
 #define CLK_FRAC_DIVIDER_ZERO_BASED		BIT(0)
 #define CLK_FRAC_DIVIDER_BIG_ENDIAN		BIT(1)
+#define CLK_FRAC_DIVIDER_NO_LIMIT		BIT(2)
 
 extern const struct clk_ops clk_fractional_divider_ops;
 struct clk *clk_register_fractional_divider(struct device *dev,
@@ -1028,6 +1038,7 @@ struct clk_composite {
 	struct clk_hw	*mux_hw;
 	struct clk_hw	*rate_hw;
 	struct clk_hw	*gate_hw;
+	struct clk_hw	*brother_hw;
 
 	const struct clk_ops	*mux_ops;
 	const struct clk_ops	*rate_ops;
@@ -1076,6 +1087,7 @@ void devm_clk_unregister(struct device *dev, struct clk *clk);
 
 void clk_hw_unregister(struct clk_hw *hw);
 void devm_clk_hw_unregister(struct device *dev, struct clk_hw *hw);
+void clk_sync_state(struct device *dev);
 
 /* helper functions */
 const char *__clk_get_name(const struct clk *clk);
@@ -1088,6 +1100,9 @@ static inline struct clk_hw *__clk_get_hw(struct clk *clk)
 	return (struct clk_hw *)clk;
 }
 #endif
+struct clk *clk_hw_get_clk(struct clk_hw *hw, const char *con_id);
+struct clk *devm_clk_hw_get_clk(struct device *dev, struct clk_hw *hw,
+				const char *con_id);
 unsigned int clk_hw_get_num_parents(const struct clk_hw *hw);
 struct clk_hw *clk_hw_get_parent(const struct clk_hw *hw);
 struct clk_hw *clk_hw_get_parent_by_index(const struct clk_hw *hw,
diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h
index 0c5706abb..b17991437 100644
--- a/include/linux/dma-buf.h
+++ b/include/linux/dma-buf.h
@@ -209,6 +209,40 @@ struct dma_buf_ops {
 	 */
 	int (*begin_cpu_access)(struct dma_buf *, enum dma_data_direction);
 
+	/**
+	 * @begin_cpu_access_partial:
+	 *
+	 * This is called from dma_buf_begin_cpu_access_partial() and allows the
+	 * exporter to ensure that the memory specified in the range is
+	 * available for cpu access - the exporter might need to allocate or
+	 * swap-in and pin the backing storage.
+	 * The exporter also needs to ensure that cpu access is
+	 * coherent for the access direction. The direction can be used by the
+	 * exporter to optimize the cache flushing, i.e. access with a different
+	 * direction (read instead of write) might return stale or even bogus
+	 * data (e.g. when the exporter needs to copy the data to temporary
+	 * storage).
+	 *
+	 * This callback is optional.
+	 *
+	 * FIXME: This is both called through the DMA_BUF_IOCTL_SYNC command
+	 * from userspace (where storage shouldn't be pinned to avoid handing
+	 * de-factor mlock rights to userspace) and for the kernel-internal
+	 * users of the various kmap interfaces, where the backing storage must
+	 * be pinned to guarantee that the atomic kmap calls can succeed. Since
+	 * there's no in-kernel users of the kmap interfaces yet this isn't a
+	 * real problem.
+	 *
+	 * Returns:
+	 *
+	 * 0 on success or a negative error code on failure. This can for
+	 * example fail when the backing storage can't be allocated. Can also
+	 * return -ERESTARTSYS or -EINTR when the call has been interrupted and
+	 * needs to be restarted.
+	 */
+	int (*begin_cpu_access_partial)(struct dma_buf *dmabuf,
+					enum dma_data_direction,
+					unsigned int offset, unsigned int len);
 	/**
 	 * @end_cpu_access:
 	 *
@@ -227,6 +261,9 @@ struct dma_buf_ops {
 	 * to be restarted.
 	 */
 	int (*end_cpu_access)(struct dma_buf *, enum dma_data_direction);
+	int (*end_cpu_access_partial)(struct dma_buf *dmabuf,
+				      enum dma_data_direction,
+				      unsigned int offset, unsigned int len);
 
 	/**
 	 * @mmap:
@@ -267,7 +304,12 @@ struct dma_buf_ops {
 
 	void *(*vmap)(struct dma_buf *);
 	void (*vunmap)(struct dma_buf *, void *vaddr);
+	int (*get_uuid)(struct dma_buf *dmabuf, uuid_t *uuid);
+	int (*get_flags)(struct dma_buf *dmabuf, unsigned long *flags);
 };
+#ifdef CONFIG_NO_GKI
+typedef int (*dma_buf_destructor)(struct dma_buf *dmabuf, void *dtor_data);
+#endif
 
 /**
  * struct dma_buf - shared buffer object
@@ -343,6 +385,10 @@ struct dma_buf {
 		struct dma_buf *dmabuf;
 	} *sysfs_entry;
 #endif
+#ifdef CONFIG_NO_GKI
+	dma_buf_destructor dtor;
+	void *dtor_data;
+#endif
 };
 
 /**
@@ -412,6 +458,7 @@ struct dma_buf_attachment {
 	const struct dma_buf_attach_ops *importer_ops;
 	void *importer_priv;
 	void *priv;
+	unsigned long dma_map_attrs;
 };
 
 /**
@@ -489,6 +536,9 @@ dma_buf_attachment_is_dynamic(struct dma_buf_attachment *attach)
 	return !!attach->importer_ops;
 }
 
+int get_each_dmabuf(int (*callback)(const struct dma_buf *dmabuf,
+		    void *private), void *private);
+int is_dma_buf_file(struct file *file);
 struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,
 					  struct device *dev);
 struct dma_buf_attachment *
@@ -513,13 +563,22 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *, struct sg_table *,
 void dma_buf_move_notify(struct dma_buf *dma_buf);
 int dma_buf_begin_cpu_access(struct dma_buf *dma_buf,
 			     enum dma_data_direction dir);
+int dma_buf_begin_cpu_access_partial(struct dma_buf *dma_buf,
+				     enum dma_data_direction dir,
+				     unsigned int offset, unsigned int len);
 int dma_buf_end_cpu_access(struct dma_buf *dma_buf,
 			   enum dma_data_direction dir);
+int dma_buf_end_cpu_access_partial(struct dma_buf *dma_buf,
+				     enum dma_data_direction dir,
+				     unsigned int offset, unsigned int len);
 
 int dma_buf_mmap(struct dma_buf *, struct vm_area_struct *,
 		 unsigned long);
 void *dma_buf_vmap(struct dma_buf *);
 void dma_buf_vunmap(struct dma_buf *, void *vaddr);
+long dma_buf_set_name(struct dma_buf *dmabuf, const char *name);
+int dma_buf_get_flags(struct dma_buf *dmabuf, unsigned long *flags);
+int dma_buf_get_uuid(struct dma_buf *dmabuf, uuid_t *uuid);
 
 #ifdef CONFIG_DMABUF_PROCESS_INFO
 /**
@@ -532,4 +591,20 @@ void dma_buf_vunmap(struct dma_buf *, void *vaddr);
  */
 struct dma_buf *get_dma_buf_from_file(struct file *f);
 #endif /* CONFIG_DMABUF_PROCESS_INFO */
+
+#ifdef CONFIG_NO_GKI
+/**
+ * dma_buf_set_destructor - set the dma-buf's destructor
+ * @dmabuf:		[in]	pointer to dma-buf
+ * @dma_buf_destructor	[in]	the destructor function
+ * @dtor_data:		[in]	destructor data associated with this buffer
+ */
+static inline void dma_buf_set_destructor(struct dma_buf *dmabuf,
+					  dma_buf_destructor dtor,
+					  void *dtor_data)
+{
+	dmabuf->dtor = dtor;
+	dmabuf->dtor_data = dtor_data;
+}
+#endif
 #endif /* __DMA_BUF_H__ */
diff --git a/include/linux/dma-fence.h b/include/linux/dma-fence.h
index 09e23adb3..9f12efaaa 100644
--- a/include/linux/dma-fence.h
+++ b/include/linux/dma-fence.h
@@ -372,6 +372,9 @@ static inline void __dma_fence_might_wait(void) {}
 
 int dma_fence_signal(struct dma_fence *fence);
 int dma_fence_signal_locked(struct dma_fence *fence);
+int dma_fence_signal_timestamp(struct dma_fence *fence, ktime_t timestamp);
+int dma_fence_signal_timestamp_locked(struct dma_fence *fence,
+				      ktime_t timestamp);
 signed long dma_fence_default_wait(struct dma_fence *fence,
 				   bool intr, signed long timeout);
 int dma_fence_add_callback(struct dma_fence *fence,
diff --git a/include/linux/dma-heap.h b/include/linux/dma-heap.h
index 83b8cfb2d..b5c6f60ef 100644
--- a/include/linux/dma-heap.h
+++ b/include/linux/dma-heap.h
@@ -16,15 +16,17 @@ struct dma_heap;
 
 /**
  * struct dma_heap_ops - ops to operate on a given heap
- * @allocate:		allocate dmabuf and return fd
+ * @allocate:		allocate dmabuf and return struct dma_buf ptr
+ * @get_pool_size:	if heap maintains memory pools, get pool size in bytes
  *
- * allocate returns dmabuf fd  on success, -errno on error.
+ * allocate returns dmabuf on success, ERR_PTR(-errno) on error.
  */
 struct dma_heap_ops {
-	int (*allocate)(struct dma_heap *heap,
+	struct dma_buf *(*allocate)(struct dma_heap *heap,
 			unsigned long len,
 			unsigned long fd_flags,
 			unsigned long heap_flags);
+	long (*get_pool_size)(struct dma_heap *heap);
 };
 
 /**
@@ -59,10 +61,73 @@ void *dma_heap_get_drvdata(struct dma_heap *heap);
  */
 const char *dma_heap_get_name(struct dma_heap *heap);
 
+/**
+ * dma_heap_get_dev() - get device struct for the heap
+ * @heap: DMA-Heap to retrieve device struct from
+ *
+ * Returns:
+ * The device struct for the heap.
+ */
+struct device *dma_heap_get_dev(struct dma_heap *heap);
+
+/**
+ * dma_heap_get_name() - get heap name
+ * @heap: DMA-Heap to retrieve private data for
+ *
+ * Returns:
+ * The char* for the heap name.
+ */
+const char *dma_heap_get_name(struct dma_heap *heap);
+
 /**
  * dma_heap_add - adds a heap to dmabuf heaps
  * @exp_info:		information needed to register this heap
  */
 struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info);
 
+/**
+ * dma_heap_put - drops a reference to a dmabuf heaps, potentially freeing it
+ * @heap:		heap pointer
+ */
+void dma_heap_put(struct dma_heap *heap);
+
+/**
+ * dma_heap_find - Returns the registered dma_heap with the specified name
+ * @name: Name of the heap to find
+ *
+ * NOTE: dma_heaps returned from this function MUST be released
+ * using dma_heap_put() when the user is done.
+ */
+struct dma_heap *dma_heap_find(const char *name);
+
+/**
+ * dma_heap_buffer_alloc - Allocate dma-buf from a dma_heap
+ * @heap:	dma_heap to allocate from
+ * @len:	size to allocate
+ * @fd_flags:	flags to set on returned dma-buf fd
+ * @heap_flags:	flags to pass to the dma heap
+ *
+ * This is for internal dma-buf allocations only.
+ */
+struct dma_buf *dma_heap_buffer_alloc(struct dma_heap *heap, size_t len,
+				      unsigned int fd_flags,
+				      unsigned int heap_flags);
+
+/** dma_heap_buffer_free - Free dma_buf allocated by dma_heap_buffer_alloc
+ * @dma_buf:	dma_buf to free
+ *
+ * This is really only a simple wrapper to dma_buf_put()
+ */
+void dma_heap_buffer_free(struct dma_buf *);
+
+/**
+ * dma_heap_bufferfd_alloc - Allocate dma-buf fd from a dma_heap
+ * @heap:	dma_heap to allocate from
+ * @len:	size to allocate
+ * @fd_flags:	flags to set on returned dma-buf fd
+ * @heap_flags:	flags to pass to the dma heap
+ */
+int dma_heap_bufferfd_alloc(struct dma_heap *heap, size_t len,
+			    unsigned int fd_flags,
+			    unsigned int heap_flags);
 #endif /* _DMA_HEAPS_H */
diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h
index 2112f21f7..f51561eda 100644
--- a/include/linux/dma-iommu.h
+++ b/include/linux/dma-iommu.h
@@ -37,6 +37,11 @@ void iommu_dma_compose_msi_msg(struct msi_desc *desc,
 
 void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list);
 
+int iommu_dma_reserve_iova(struct device *dev, dma_addr_t base,
+			   u64 size);
+
+int iommu_dma_enable_best_fit_algo(struct device *dev);
+
 #else /* CONFIG_IOMMU_DMA */
 
 struct iommu_domain;
@@ -78,5 +83,16 @@ static inline void iommu_dma_get_resv_regions(struct device *dev, struct list_he
 {
 }
 
+static inline int iommu_dma_reserve_iova(struct device *dev, dma_addr_t base,
+					 u64 size)
+{
+	return -ENODEV;
+}
+
+static inline int iommu_dma_enable_best_fit_algo(struct device *dev)
+{
+	return -ENODEV;
+}
+
 #endif	/* CONFIG_IOMMU_DMA */
 #endif	/* __DMA_IOMMU_H */
diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
index a7d70cdee..7270f12e2 100644
--- a/include/linux/dma-mapping.h
+++ b/include/linux/dma-mapping.h
@@ -61,6 +61,23 @@
  */
 #define DMA_ATTR_PRIVILEGED		(1UL << 9)
 
+/*
+ * DMA_ATTR_SYS_CACHE_ONLY: used to indicate that the buffer should be mapped
+ * with the correct memory attributes so that it can be cached in the system
+ * or last level cache. This is useful for buffers that are being mapped for
+ * devices that are non-coherent, but can use the system cache.
+ */
+#define DMA_ATTR_SYS_CACHE_ONLY		(1UL << 10)
+
+/*
+ * DMA_ATTR_SYS_CACHE_ONLY_NWA: used to indicate that the buffer should be
+ * mapped with the correct memory attributes so that it can be cached in the
+ * system or last level cache, with a no write allocate cache policy. This is
+ * useful for buffers that are being mapped for devices that are non-coherent,
+ * but can use the system cache.
+ */
+#define DMA_ATTR_SYS_CACHE_ONLY_NWA	(1UL << 11)
+
 /*
  * A dma_addr_t can hold any valid DMA or bus address for the platform.  It can
  * be given to a device to use as a DMA source or target.  It is specific to a
diff --git a/include/linux/energy_model.h b/include/linux/energy_model.h
index 5f04a2b35..9ca1ea02f 100644
--- a/include/linux/energy_model.h
+++ b/include/linux/energy_model.h
@@ -29,6 +29,8 @@ struct em_perf_state {
  * em_perf_domain - Performance domain
  * @table:		List of performance states, in ascending order
  * @nr_perf_states:	Number of performance states
+ * @milliwatts:		Flag indicating the power values are in milli-Watts
+ *			or some other scale.
  * @cpus:		Cpumask covering the CPUs of the domain. It's here
  *			for performance reasons to avoid potential cache
  *			misses during energy calculations in the scheduler
@@ -43,6 +45,7 @@ struct em_perf_state {
 struct em_perf_domain {
 	struct em_perf_state *table;
 	int nr_perf_states;
+	int milliwatts;
 	unsigned long cpus[];
 };
 
@@ -95,7 +98,8 @@ struct em_data_callback {
 struct em_perf_domain *em_cpu_get(int cpu);
 struct em_perf_domain *em_pd_get(struct device *dev);
 int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
-				struct em_data_callback *cb, cpumask_t *span);
+				struct em_data_callback *cb, cpumask_t *span,
+				bool milliwatts);
 void em_dev_unregister_perf_domain(struct device *dev);
 
 /**
@@ -119,6 +123,9 @@ static inline unsigned long em_cpu_energy(struct em_perf_domain *pd,
 	struct em_perf_state *ps;
 	int i, cpu;
 
+	if (!sum_util)
+		return 0;
+
 	/*
 	 * In order to predict the performance state, map the utilization of
 	 * the most utilized CPU of the performance domain to a requested
@@ -202,7 +209,8 @@ struct em_data_callback {};
 
 static inline
 int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
-				struct em_data_callback *cb, cpumask_t *span)
+				struct em_data_callback *cb, cpumask_t *span,
+				bool milliwatts)
 {
 	return -EINVAL;
 }
diff --git a/include/linux/extcon.h b/include/linux/extcon.h
index fa1e431ee..6f851be8c 100644
--- a/include/linux/extcon.h
+++ b/include/linux/extcon.h
@@ -37,6 +37,7 @@
 /* USB external connector */
 #define EXTCON_USB		1
 #define EXTCON_USB_HOST		2
+#define EXTCON_USB_VBUS_EN	3
 
 /*
  * Charging external connector
diff --git a/include/linux/freezer.h b/include/linux/freezer.h
index 27828145c..cd57fd734 100644
--- a/include/linux/freezer.h
+++ b/include/linux/freezer.h
@@ -27,6 +27,10 @@ static inline bool frozen(struct task_struct *p)
 	return p->flags & PF_FROZEN;
 }
 
+static inline bool frozen_or_skipped(struct task_struct *p)
+{
+	return p->flags & (PF_FROZEN | PF_FREEZER_SKIP);
+}
 extern bool freezing_slow_path(struct task_struct *p);
 
 /*
@@ -270,6 +274,7 @@ static inline int freezable_schedule_hrtimeout_range(ktime_t *expires,
 
 #else /* !CONFIG_FREEZER */
 static inline bool frozen(struct task_struct *p) { return false; }
+static inline bool frozen_or_skipped(struct task_struct *p) { return false; }
 static inline bool freezing(struct task_struct *p) { return false; }
 static inline void __thaw_task(struct task_struct *t) {}
 
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index e90c267e7..2476f1a97 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -31,6 +31,18 @@
  * if the IOMMU page table format is equivalent.
  */
 #define IOMMU_PRIV	(1 << 5)
+/*
+ * Allow caching in a transparent outer level of cache, also known as
+ * the last-level or system cache, with a read/write allocation policy.
+ * Does not depend on IOMMU_CACHE. Incompatible with IOMMU_SYS_CACHE_NWA.
+ */
+#define IOMMU_SYS_CACHE	(1 << 6)
+/*
+ * Allow caching in a transparent outer level of cache, also known as
+ * the last-level or system cache, with a read allocation policy.
+ * Does not depend on IOMMU_CACHE. Incompatible with IOMMU_SYS_CACHE.
+ */
+#define IOMMU_SYS_CACHE_NWA (1 << 7)
 
 struct iommu_ops;
 struct iommu_group;
@@ -190,7 +202,12 @@ struct iommu_iotlb_gather {
  * @attach_dev: attach device to an iommu domain
  * @detach_dev: detach device from an iommu domain
  * @map: map a physically contiguous memory region to an iommu domain
+ * @map_pages: map a physically contiguous set of pages of the same size to
+ *             an iommu domain.
+ * @map_sg: map a scatter-gather list of physically contiguous chunks to
+ *          an iommu domain.
  * @unmap: unmap a physically contiguous memory region from an iommu domain
+ * @unmap_pages: unmap a number of pages of the same size from an iommu domain
  * @flush_iotlb_all: Synchronously flush all hardware TLBs for this domain
  * @iotlb_sync_map: Sync mappings created recently using @map to the hardware
  * @iotlb_sync: Flush all queued ranges from the hardware TLBs and empty flush
@@ -241,10 +258,20 @@ struct iommu_ops {
 	void (*detach_dev)(struct iommu_domain *domain, struct device *dev);
 	int (*map)(struct iommu_domain *domain, unsigned long iova,
 		   phys_addr_t paddr, size_t size, int prot, gfp_t gfp);
+	int (*map_pages)(struct iommu_domain *domain, unsigned long iova,
+			 phys_addr_t paddr, size_t pgsize, size_t pgcount,
+			 int prot, gfp_t gfp, size_t *mapped);
+	int (*map_sg)(struct iommu_domain *domain, unsigned long iova,
+		      struct scatterlist *sg, unsigned int nents, int prot,
+		      gfp_t gfp, size_t *mapped);
 	size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,
 		     size_t size, struct iommu_iotlb_gather *iotlb_gather);
+	size_t (*unmap_pages)(struct iommu_domain *domain, unsigned long iova,
+			      size_t pgsize, size_t pgcount,
+			      struct iommu_iotlb_gather *iotlb_gather);
 	void (*flush_iotlb_all)(struct iommu_domain *domain);
-	void (*iotlb_sync_map)(struct iommu_domain *domain);
+	void (*iotlb_sync_map)(struct iommu_domain *domain, unsigned long iova,
+			       size_t size);
 	void (*iotlb_sync)(struct iommu_domain *domain,
 			   struct iommu_iotlb_gather *iotlb_gather);
 	phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, dma_addr_t iova);
@@ -561,6 +588,8 @@ static inline void iommu_iotlb_gather_add_page(struct iommu_domain *domain,
 extern struct iommu_group *pci_device_group(struct device *dev);
 /* Generic device grouping function */
 extern struct iommu_group *generic_device_group(struct device *dev);
+extern void rk_iommu_mask_irq(struct device *dev);
+extern void rk_iommu_unmask_irq(struct device *dev);
 /* FSL-MC device grouping function */
 struct iommu_group *fsl_mc_device_group(struct device *dev);
 
@@ -1069,6 +1098,14 @@ static inline struct iommu_fwspec *dev_iommu_fwspec_get(struct device *dev)
 {
 	return NULL;
 }
+
+static inline void rk_iommu_mask_irq(struct device *dev)
+{
+}
+
+static inline void rk_iommu_unmask_irq(struct device *dev)
+{
+}
 #endif /* CONFIG_IOMMU_API */
 
 /**
diff --git a/include/linux/iova.h b/include/linux/iova.h
index 6c19b09e9..0394d44f7 100644
--- a/include/linux/iova.h
+++ b/include/linux/iova.h
@@ -95,6 +95,7 @@ struct iova_domain {
 						   flush-queues */
 	atomic_t fq_timer_on;			/* 1 when timer is active, 0
 						   when not */
+	bool best_fit;
 };
 
 static inline unsigned long iova_size(struct iova *iova)
@@ -163,6 +164,7 @@ void put_iova_domain(struct iova_domain *iovad);
 struct iova *split_and_remove_iova(struct iova_domain *iovad,
 	struct iova *iova, unsigned long pfn_lo, unsigned long pfn_hi);
 void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad);
+void free_global_cached_iovas(struct iova_domain *iovad);
 #else
 static inline int iova_cache_get(void)
 {
@@ -270,6 +272,11 @@ static inline void free_cpu_cached_iovas(unsigned int cpu,
 					 struct iova_domain *iovad)
 {
 }
+
+static inline void free_global_cached_iovas(struct iova_domain *iovad)
+{
+}
+
 #endif
 
 #endif
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index f6d092fdb..43348c487 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -693,6 +693,20 @@ int its_init(struct fwnode_handle *handle, struct rdists *rdists,
 	     struct irq_domain *domain);
 int mbi_init(struct fwnode_handle *fwnode, struct irq_domain *parent);
 
+struct gic_chip_data {
+	struct fwnode_handle	*fwnode;
+	void __iomem		*dist_base;
+	struct redist_region	*redist_regions;
+	struct rdists		rdists;
+	struct irq_domain	*domain;
+	u64			redist_stride;
+	u32			nr_redist_regions;
+	u64			flags;
+	bool			has_rss;
+	unsigned int		ppi_nr;
+	struct partition_desc	**ppi_descs;
+};
+
 static inline bool gic_enable_sre(void)
 {
 	u32 val;
@@ -708,6 +722,8 @@ static inline bool gic_enable_sre(void)
 	return !!(val & ICC_SRE_EL1_SRE);
 }
 
+void gic_resume(void);
+
 #endif
 
 #endif
diff --git a/include/linux/irqchip/arm-gic-v4.h b/include/linux/irqchip/arm-gic-v4.h
index 6976b8331..943c3411c 100644
--- a/include/linux/irqchip/arm-gic-v4.h
+++ b/include/linux/irqchip/arm-gic-v4.h
@@ -39,6 +39,8 @@ struct its_vpe {
 	irq_hw_number_t		vpe_db_lpi;
 	/* VPE resident */
 	bool			resident;
+	/* VPT parse complete */
+	bool			ready;
 	union {
 		/* GICv4.0 implementations */
 		struct {
@@ -104,6 +106,7 @@ enum its_vcpu_info_cmd_type {
 	PROP_UPDATE_AND_INV_VLPI,
 	SCHEDULE_VPE,
 	DESCHEDULE_VPE,
+	COMMIT_VPE,
 	INVALL_VPE,
 	PROP_UPDATE_VSGI,
 };
@@ -129,6 +132,7 @@ int its_alloc_vcpu_irqs(struct its_vm *vm);
 void its_free_vcpu_irqs(struct its_vm *vm);
 int its_make_vpe_resident(struct its_vpe *vpe, bool g0en, bool g1en);
 int its_make_vpe_non_resident(struct its_vpe *vpe, bool db);
+int its_commit_vpe(struct its_vpe *vpe);
 int its_invall_vpe(struct its_vpe *vpe);
 int its_map_vlpi(int irq, struct its_vlpi_map *map);
 int its_get_vlpi(int irq, struct its_vlpi_map *map);
diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h
index e07f6e61c..7cdc5dfa4 100644
--- a/include/linux/mfd/rk808.h
+++ b/include/linux/mfd/rk808.h
@@ -113,6 +113,235 @@ enum rk808_reg {
 #define RK808_INT_STS_MSK_REG2	0x4f
 #define RK808_IO_POL_REG	0x50
 
+/* RK816 */
+enum rk816_reg {
+	RK816_ID_DCDC1,
+	RK816_ID_DCDC2,
+	RK816_ID_DCDC3,
+	RK816_ID_DCDC4,
+	RK816_ID_LDO1,
+	RK816_ID_LDO2,
+	RK816_ID_LDO3,
+	RK816_ID_LDO4,
+	RK816_ID_LDO5,
+	RK816_ID_LDO6,
+};
+
+/*VERSION REGISTER*/
+#define RK816_CHIP_NAME_REG			0x17
+#define RK816_CHIP_VER_REG			0x18
+#define RK816_OTP_VER_REG			0x19
+#define RK816_NUM_REGULATORS			10
+
+/*POWER ON/OFF REGISTER*/
+#define RK816_VB_MON_REG			0x21
+#define RK816_THERMAL_REG			0x22
+#define RK816_PWRON_LP_INT_TIME_REG		0x47
+#define RK816_PWRON_DB_REG			0x48
+#define RK816_DEV_CTRL_REG			0x4B
+#define RK816_ON_SOURCE_REG			0xAE
+#define RK816_OFF_SOURCE_REG			0xAF
+
+/*POWER CHANNELS ENABLE REGISTER*/
+#define RK816_DCDC_EN_REG1			0x23
+#define RK816_DCDC_EN_REG2			0x24
+#define RK816_SLP_DCDC_EN_REG			0x25
+#define RK816_SLP_LDO_EN_REG			0x26
+#define RK816_LDO_EN_REG1			0x27
+#define RK816_LDO_EN_REG2			0x28
+
+/*BUCK AND LDO CONFIG REGISTER*/
+#define RK816_BUCK1_CONFIG_REG			0x2E
+#define RK816_BUCK1_ON_VSEL_REG			0x2F
+#define RK816_BUCK1_SLP_VSEL_REG		0x30
+#define RK816_BUCK2_CONFIG_REG			0x32
+#define RK816_BUCK2_ON_VSEL_REG			0x33
+#define RK816_BUCK2_SLP_VSEL_REG		0x34
+#define RK816_BUCK3_CONFIG_REG			0x36
+#define RK816_BUCK4_CONFIG_REG			0x37
+#define RK816_BUCK4_ON_VSEL_REG			0x38
+#define RK816_BUCK4_SLP_VSEL_REG		0x39
+#define RK816_LDO1_ON_VSEL_REG			0x3B
+#define RK816_LDO1_SLP_VSEL_REG			0x3C
+#define RK816_LDO2_ON_VSEL_REG			0x3D
+#define RK816_LDO2_SLP_VSEL_REG			0x3E
+#define RK816_LDO3_ON_VSEL_REG			0x3F
+#define RK816_LDO3_SLP_VSEL_REG			0x40
+#define RK816_LDO4_ON_VSEL_REG			0x41
+#define RK816_LDO4_SLP_VSEL_REG			0x42
+#define RK816_LDO5_ON_VSEL_REG			0x43
+#define RK816_LDO5_SLP_VSEL_REG			0x44
+#define RK816_LDO6_ON_VSEL_REG			0x45
+#define RK816_LDO6_SLP_VSEL_REG			0x46
+#define RK816_GPIO_IO_POL_REG			0x50
+
+/*CHARGER BOOST AND OTG REGISTER*/
+#define RK816_OTG_BUCK_LDO_CONFIG_REG           0x2A
+#define RK816_CHRG_CONFIG_REG                   0x2B
+#define RK816_BOOST_ON_VESL_REG                 0x54
+#define RK816_BOOST_SLP_VSEL_REG                0x55
+#define RK816_CHRG_BOOST_CONFIG_REG             0x9A
+#define RK816_SUP_STS_REG                       0xA0
+#define RK816_USB_CTRL_REG                      0xA1
+#define RK816_CHRG_CTRL_REG1                    0xA3
+#define RK816_CHRG_CTRL_REG2                    0xA4
+#define RK816_CHRG_CTRL_REG3                    0xA5
+#define RK816_BAT_CTRL_REG                      0xA6
+#define RK816_BAT_HTS_TS_REG                    0xA8
+#define RK816_BAT_LTS_TS_REG                    0xA9
+
+#define RK816_TS_CTRL_REG			0xAC
+#define RK816_ADC_CTRL_REG			0xAD
+#define RK816_GGCON_REG				0xB0
+#define RK816_GGSTS_REG				0xB1
+#define RK816_ZERO_CUR_ADC_REGH			0xB2
+#define RK816_ZERO_CUR_ADC_REGL			0xB3
+#define RK816_GASCNT_CAL_REG3			0xB4
+#define RK816_GASCNT_CAL_REG2			0xB5
+#define RK816_GASCNT_CAL_REG1			0xB6
+#define RK816_GASCNT_CAL_REG0			0xB7
+#define RK816_GASCNT_REG3			0xB8
+#define RK816_GASCNT_REG2			0xB9
+#define RK816_GASCNT_REG1			0xBA
+#define RK816_GASCNT_REG0			0xBB
+#define RK816_BAT_CUR_AVG_REGH			0xBC
+#define RK816_BAT_CUR_AVG_REGL			0xBD
+#define RK816_TS_ADC_REGH			0xBE
+#define RK816_TS_ADC_REGL			0xBF
+#define RK816_USB_ADC_REGH			0xC0
+#define RK816_USB_ADC_REGL			0xC1
+#define RK816_BAT_OCV_REGH			0xC2
+#define RK816_BAT_OCV_REGL			0xC3
+#define RK816_BAT_VOL_REGH			0xC4
+#define RK816_BAT_VOL_REGL			0xC5
+#define RK816_RELAX_ENTRY_THRES_REGH		0xC6
+#define RK816_RELAX_ENTRY_THRES_REGL		0xC7
+#define RK816_RELAX_EXIT_THRES_REGH		0xC8
+#define RK816_RELAX_EXIT_THRES_REGL		0xC9
+#define RK816_RELAX_VOL1_REGH			0xCA
+#define RK816_RELAX_VOL1_REGL			0xCB
+#define RK816_RELAX_VOL2_REGH			0xCC
+#define RK816_RELAX_VOL2_REGL			0xCD
+#define RK816_RELAX_CUR1_REGH			0xCE
+#define RK816_RELAX_CUR1_REGL			0xCF
+#define RK816_RELAX_CUR2_REGH			0xD0
+#define RK816_RELAX_CUR2_REGL			0xD1
+#define RK816_CAL_OFFSET_REGH			0xD2
+#define RK816_CAL_OFFSET_REGL			0xD3
+#define RK816_NON_ACT_TIMER_CNT_REG		0xD4
+#define RK816_VCALIB0_REGH			0xD5
+#define RK816_VCALIB0_REGL			0xD6
+#define RK816_VCALIB1_REGH			0xD7
+#define RK816_VCALIB1_REGL			0xD8
+#define RK816_FCC_GASCNT_REG3			0xD9
+#define RK816_FCC_GASCNT_REG2			0xDA
+#define RK816_FCC_GASCNT_REG1			0xDB
+#define RK816_FCC_GASCNT_REG0			0xDC
+#define RK816_IOFFSET_REGH			0xDD
+#define RK816_IOFFSET_REGL			0xDE
+#define RK816_SLEEP_CON_SAMP_CUR_REG		0xDF
+
+/*DATA REGISTER*/
+#define RK816_SOC_REG				0xE0
+#define RK816_REMAIN_CAP_REG3			0xE1
+#define RK816_REMAIN_CAP_REG2			0xE2
+#define RK816_REMAIN_CAP_REG1			0xE3
+#define RK816_REMAIN_CAP_REG0			0xE4
+#define RK816_UPDATE_LEVE_REG			0xE5
+#define RK816_NEW_FCC_REG3			0xE6
+#define RK816_NEW_FCC_REG2			0xE7
+#define RK816_NEW_FCC_REG1			0xE8
+#define RK816_NEW_FCC_REG0			0xE9
+#define RK816_NON_ACT_TIMER_CNT_REG_SAVE	0xEA
+#define RK816_OCV_VOL_VALID_REG			0xEB
+#define RK816_REBOOT_CNT_REG			0xEC
+#define RK816_PCB_IOFFSET_REG			0xED
+#define RK816_MISC_MARK_REG			0xEE
+#define RK816_HALT_CNT_REG			0xEF
+#define RK816_CALC_REST_REGH			0xF0
+#define RK816_CALC_REST_REGL			0xF1
+#define DATA18_REG				0xF2
+
+/*INTERRUPT REGISTER*/
+#define RK816_INT_STS_REG1			0x49
+#define RK816_INT_STS_MSK_REG1			0x4A
+#define RK816_INT_STS_REG2			0x4C
+#define RK816_INT_STS_MSK_REG2			0x4D
+#define RK816_INT_STS_REG3			0x4E
+#define RK816_INT_STS_MSK_REG3			0x4F
+#define RK816_GPIO_IO_POL_REG			0x50
+
+#define RK816_DATA18_REG			0xF2
+
+/* IRQ Definitions */
+#define RK816_IRQ_PWRON_FALL			0
+#define RK816_IRQ_PWRON_RISE			1
+#define RK816_IRQ_VB_LOW			2
+#define RK816_IRQ_PWRON				3
+#define RK816_IRQ_PWRON_LP			4
+#define RK816_IRQ_HOTDIE			5
+#define RK816_IRQ_RTC_ALARM			6
+#define RK816_IRQ_RTC_PERIOD			7
+#define RK816_IRQ_USB_OV			8
+#define RK816_IRQ_PLUG_IN			9
+#define RK816_IRQ_PLUG_OUT			10
+#define RK816_IRQ_CHG_OK			11
+#define RK816_IRQ_CHG_TE			12
+#define RK816_IRQ_CHG_TS			13
+#define RK816_IRQ_CHG_CVTLIM			14
+#define RK816_IRQ_DISCHG_ILIM			15
+
+#define RK816_IRQ_PWRON_FALL_MSK		BIT(5)
+#define RK816_IRQ_PWRON_RISE_MSK		BIT(6)
+#define RK816_IRQ_VB_LOW_MSK			BIT(1)
+#define RK816_IRQ_PWRON_MSK			BIT(2)
+#define RK816_IRQ_PWRON_LP_MSK			BIT(3)
+#define RK816_IRQ_HOTDIE_MSK			BIT(4)
+#define RK816_IRQ_RTC_ALARM_MSK			BIT(5)
+#define RK816_IRQ_RTC_PERIOD_MSK		BIT(6)
+#define RK816_IRQ_USB_OV_MSK			BIT(7)
+#define RK816_IRQ_PLUG_IN_MSK			BIT(0)
+#define RK816_IRQ_PLUG_OUT_MSK			BIT(1)
+#define RK816_IRQ_CHG_OK_MSK			BIT(2)
+#define RK816_IRQ_CHG_TE_MSK			BIT(3)
+#define RK816_IRQ_CHG_TS_MSK			BIT(4)
+#define RK816_IRQ_CHG_CVTLIM_MSK		BIT(6)
+#define RK816_IRQ_DISCHG_ILIM_MSK		BIT(7)
+
+#define RK816_VBAT_LOW_2V8			0x00
+#define RK816_VBAT_LOW_2V9			0x01
+#define RK816_VBAT_LOW_3V0			0x02
+#define RK816_VBAT_LOW_3V1			0x03
+#define RK816_VBAT_LOW_3V2			0x04
+#define RK816_VBAT_LOW_3V3			0x05
+#define RK816_VBAT_LOW_3V4			0x06
+#define RK816_VBAT_LOW_3V5			0x07
+#define RK816_PWR_FALL_INT_STATUS		(0x1 << 5)
+#define RK816_PWR_RISE_INT_STATUS		(0x1 << 6)
+#define RK816_ALARM_INT_STATUS			(0x1 << 5)
+#define EN_VBAT_LOW_IRQ				(0x1 << 4)
+#define VBAT_LOW_ACT_MASK			(0x1 << 4)
+#define RTC_TIMER_ALARM_INT_MSK			(0x3 << 2)
+#define RTC_TIMER_ALARM_INT_DIS			(0x0 << 2)
+#define RTC_PERIOD_ALARM_INT_MSK		(0x3 << 5)
+#define RTC_PERIOD_ALARM_INT_ST			(0x3 << 5)
+#define RTC_PERIOD_ALARM_INT_DIS		(0x3 << 5)
+#define RTC_PERIOD_ALARM_INT_EN			(0x9f)
+#define REG_WRITE_MSK				0xff
+#define BUCK4_MAX_ILIMIT			0x2c
+#define BUCK_RATE_MSK				(0x3 << 3)
+#define BUCK_RATE_12_5MV_US			(0x2 << 3)
+#define ALL_INT_FLAGS_ST			0xff
+#define PLUGIN_OUT_INT_EN			0xfc
+#define RK816_PWRON_FALL_RISE_INT_EN		0x9f
+#define BUCK1_2_IMAX_MAX			(0x3 << 6)
+#define BUCK3_4_IMAX_MAX			(0x3 << 3)
+#define BOOST_DISABLE				((0x1 << 5) | (0x0 << 1))
+#define BUCK4_VRP_3PERCENT			0xc0
+#define RK816_BUCK_DVS_CONFIRM			(0x1 << 7)
+#define RK816_TYPE_ES2				0x05
+#define RK816_CHIP_VERSION_MASK			0x0f
+
 /* RK818 */
 #define RK818_DCDC1			0
 #define RK818_LDO1			4
@@ -138,6 +367,8 @@ enum rk818_reg {
 	RK818_ID_OTG_SWITCH,
 };
 
+#define RK818_VB_MON_REG		0x21
+#define RK818_THERMAL_REG		0x22
 #define RK818_DCDC_EN_REG		0x23
 #define RK818_LDO_EN_REG		0x24
 #define RK818_SLEEP_SET_OFF_REG1	0x25
@@ -190,7 +421,84 @@ enum rk818_reg {
 #define RK818_BOOST_LDO9_SLP_VSEL_REG	0x55
 #define RK818_BOOST_CTRL_REG		0x56
 #define RK818_DCDC_ILMAX		0x90
+#define RK818_CHRG_COMP_REG		0x9a
+#define RK818_SUP_STS_REG		0xa0
 #define RK818_USB_CTRL_REG		0xa1
+#define RK818_CHRG_CTRL_REG1		0xa3
+#define RK818_CHRG_CTRL_REG2		0xa4
+#define RK818_CHRG_CTRL_REG3		0xa5
+#define RK818_BAT_CTRL_REG		0xa6
+#define RK818_BAT_HTS_TS1_REG		0xa8
+#define RK818_BAT_LTS_TS1_REG		0xa9
+#define RK818_BAT_HTS_TS2_REG		0xaa
+#define RK818_BAT_LTS_TS2_REG		0xab
+#define RK818_TS_CTRL_REG		0xac
+#define RK818_ADC_CTRL_REG		0xad
+#define RK818_ON_SOURCE_REG		0xae
+#define RK818_OFF_SOURCE_REG		0xaf
+#define RK818_GGCON_REG			0xb0
+#define RK818_GGSTS_REG			0xb1
+#define RK818_FRAME_SMP_INTERV_REG	0xb2
+#define RK818_AUTO_SLP_CUR_THR_REG	0xb3
+#define RK818_GASCNT_CAL_REG3		0xb4
+#define RK818_GASCNT_CAL_REG2		0xb5
+#define RK818_GASCNT_CAL_REG1		0xb6
+#define RK818_GASCNT_CAL_REG0		0xb7
+#define RK818_GASCNT3_REG		0xb8
+#define RK818_GASCNT2_REG		0xb9
+#define RK818_GASCNT1_REG		0xba
+#define RK818_GASCNT0_REG		0xbb
+#define RK818_BAT_CUR_AVG_REGH		0xbc
+#define RK818_BAT_CUR_AVG_REGL		0xbd
+#define RK818_TS1_ADC_REGH		0xbe
+#define RK818_TS1_ADC_REGL		0xbf
+#define RK818_TS2_ADC_REGH		0xc0
+#define RK818_TS2_ADC_REGL		0xc1
+#define RK818_BAT_OCV_REGH		0xc2
+#define RK818_BAT_OCV_REGL		0xc3
+#define RK818_BAT_VOL_REGH		0xc4
+#define RK818_BAT_VOL_REGL		0xc5
+#define RK818_RELAX_ENTRY_THRES_REGH	0xc6
+#define RK818_RELAX_ENTRY_THRES_REGL	0xc7
+#define RK818_RELAX_EXIT_THRES_REGH	0xc8
+#define RK818_RELAX_EXIT_THRES_REGL	0xc9
+#define RK818_RELAX_VOL1_REGH		0xca
+#define RK818_RELAX_VOL1_REGL		0xcb
+#define RK818_RELAX_VOL2_REGH		0xcc
+#define RK818_RELAX_VOL2_REGL		0xcd
+#define RK818_BAT_CUR_R_CALC_REGH	0xce
+#define RK818_BAT_CUR_R_CALC_REGL	0xcf
+#define RK818_BAT_VOL_R_CALC_REGH	0xd0
+#define RK818_BAT_VOL_R_CALC_REGL	0xd1
+#define RK818_CAL_OFFSET_REGH		0xd2
+#define RK818_CAL_OFFSET_REGL		0xd3
+#define RK818_NON_ACT_TIMER_CNT_REG	0xd4
+#define RK818_VCALIB0_REGH		0xd5
+#define RK818_VCALIB0_REGL		0xd6
+#define RK818_VCALIB1_REGH		0xd7
+#define RK818_VCALIB1_REGL		0xd8
+#define RK818_IOFFSET_REGH		0xdd
+#define RK818_IOFFSET_REGL		0xde
+#define RK818_SOC_REG			0xe0
+#define RK818_REMAIN_CAP_REG3		0xe1
+#define RK818_REMAIN_CAP_REG2		0xe2
+#define RK818_REMAIN_CAP_REG1		0xe3
+#define RK818_REMAIN_CAP_REG0		0xe4
+#define RK818_UPDAT_LEVE_REG		0xe5
+#define RK818_NEW_FCC_REG3		0xe6
+#define RK818_NEW_FCC_REG2		0xe7
+#define RK818_NEW_FCC_REG1		0xe8
+#define RK818_NEW_FCC_REG0		0xe9
+#define RK818_NON_ACT_TIMER_CNT_SAVE_REG	0xea
+#define RK818_OCV_VOL_VALID_REG		0xeb
+#define RK818_REBOOT_CNT_REG		0xec
+#define RK818_POFFSET_REG		0xed
+#define RK818_MISC_MARK_REG		0xee
+#define RK818_HALT_CNT_REG		0xef
+#define RK818_CALC_REST_REGH		0xf0
+#define RK818_CALC_REST_REGL		0xf1
+#define RK818_SAVE_DATA19		0xf2
+#define RK818_NUM_REGULATOR		17
 
 #define RK818_H5V_EN			BIT(0)
 #define RK818_REF_RDY_CTRL		BIT(1)
@@ -255,14 +563,22 @@ enum rk805_reg {
 #define RK805_PWRON_FALL_RISE_INT_MSK	0x81
 
 /* RK805 IRQ Definitions */
-#define RK805_IRQ_PWRON_RISE		0
 #define RK805_IRQ_VB_LOW		1
 #define RK805_IRQ_PWRON			2
 #define RK805_IRQ_PWRON_LP		3
 #define RK805_IRQ_HOTDIE		4
 #define RK805_IRQ_RTC_ALARM		5
 #define RK805_IRQ_RTC_PERIOD		6
-#define RK805_IRQ_PWRON_FALL		7
+
+/*
+ * When PMIC irq occurs, regmap-irq.c will traverse all PMIC child
+ * interrupts from low index 0 to high index, we give fall interrupt
+ * high priority to be called earlier than rise, so that it can be
+ * override by late rise event. This can helps to solve key release
+ * glitch which make a wrongly fall event immediately after rise.
+ */
+#define RK805_IRQ_PWRON_FALL		0
+#define RK805_IRQ_PWRON_RISE		7
 
 #define RK805_IRQ_PWRON_RISE_MSK	BIT(0)
 #define RK805_IRQ_VB_LOW_MSK		BIT(1)
@@ -289,6 +605,16 @@ enum rk805_reg {
 #define RK805_INT_ALARM_EN		(1 << 3)
 #define RK805_INT_TIMER_EN		(1 << 2)
 
+#define RK805_SLP_LDO_EN_OFFSET		-1
+#define RK805_SLP_DCDC_EN_OFFSET	2
+
+#define RK805_RAMP_RATE_OFFSET		3
+#define RK805_RAMP_RATE_MASK		(3 << RK805_RAMP_RATE_OFFSET)
+#define RK805_RAMP_RATE_3MV_PER_US	(0 << RK805_RAMP_RATE_OFFSET)
+#define RK805_RAMP_RATE_6MV_PER_US	(1 << RK805_RAMP_RATE_OFFSET)
+#define RK805_RAMP_RATE_12_5MV_PER_US	(2 << RK805_RAMP_RATE_OFFSET)
+#define RK805_RAMP_RATE_25MV_PER_US	(3 << RK805_RAMP_RATE_OFFSET)
+
 /* RK808 IRQ Definitions */
 #define RK808_IRQ_VOUT_LO	0
 #define RK808_IRQ_VB_LO		1
@@ -348,6 +674,107 @@ enum rk805_reg {
 
 #define RK818_NUM_IRQ		16
 
+/*RK818_DCDC_EN_REG*/
+#define BUCK1_EN_MASK		BIT(0)
+#define BUCK2_EN_MASK		BIT(1)
+#define BUCK3_EN_MASK		BIT(2)
+#define BUCK4_EN_MASK		BIT(3)
+#define BOOST_EN_MASK		BIT(4)
+#define LDO9_EN_MASK		BIT(5)
+#define SWITCH_EN_MASK		BIT(6)
+#define OTG_EN_MASK		BIT(7)
+
+#define BUCK1_EN_ENABLE		BIT(0)
+#define BUCK2_EN_ENABLE		BIT(1)
+#define BUCK3_EN_ENABLE		BIT(2)
+#define BUCK4_EN_ENABLE		BIT(3)
+#define BOOST_EN_ENABLE		BIT(4)
+#define LDO9_EN_ENABLE		BIT(5)
+#define SWITCH_EN_ENABLE	BIT(6)
+#define OTG_EN_ENABLE		BIT(7)
+
+#define BUCK1_SLP_SET_MASK	BIT(0)
+#define BUCK2_SLP_SET_MASK	BIT(1)
+#define BUCK3_SLP_SET_MASK	BIT(2)
+#define BUCK4_SLP_SET_MASK	BIT(3)
+#define BOOST_SLP_SET_MASK	BIT(4)
+#define LDO9_SLP_SET_MASK	BIT(5)
+#define SWITCH_SLP_SET_MASK	BIT(6)
+#define OTG_SLP_SET_MASK	BIT(7)
+
+#define BUCK1_SLP_SET_OFF	BIT(0)
+#define BUCK2_SLP_SET_OFF	BIT(1)
+#define BUCK3_SLP_SET_OFF	BIT(2)
+#define BUCK4_SLP_SET_OFF	BIT(3)
+#define BOOST_SLP_SET_OFF	BIT(4)
+#define LDO9_SLP_SET_OFF	BIT(5)
+#define SWITCH_SLP_SET_OFF	BIT(6)
+#define OTG_SLP_SET_OFF		BIT(7)
+#define OTG_BOOST_SLP_OFF	(BOOST_SLP_SET_OFF | OTG_SLP_SET_OFF)
+
+#define BUCK1_SLP_SET_ON	BIT(0)
+#define BUCK2_SLP_SET_ON	BIT(1)
+#define BUCK3_SLP_SET_ON	BIT(2)
+#define BUCK4_SLP_SET_ON	BIT(3)
+#define BOOST_SLP_SET_ON	BIT(4)
+#define LDO9_SLP_SET_ON		BIT(5)
+#define SWITCH_SLP_SET_ON	BIT(6)
+#define OTG_SLP_SET_ON		BIT(7)
+
+#define VOUT_LO_MASK		BIT(0)
+#define VB_LO_MASK		BIT(1)
+#define PWRON_MASK		BIT(2)
+#define PWRON_LP_MASK		BIT(3)
+#define HOTDIE_MASK		BIT(4)
+#define RTC_ALARM_MASK		BIT(5)
+#define RTC_PERIOD_MASK		BIT(6)
+#define USB_OV_MASK		BIT(7)
+
+#define VOUT_LO_DISABLE		BIT(0)
+#define VB_LO_DISABLE		BIT(1)
+#define PWRON_DISABLE		BIT(2)
+#define PWRON_LP_DISABLE	BIT(3)
+#define HOTDIE_DISABLE		BIT(4)
+#define RTC_ALARM_DISABLE	BIT(5)
+#define RTC_PERIOD_DISABLE	BIT(6)
+#define USB_OV_INT_DISABLE	BIT(7)
+
+#define VOUT_LO_ENABLE		(0 << 0)
+#define VB_LO_ENABLE		(0 << 1)
+#define PWRON_ENABLE		(0 << 2)
+#define PWRON_LP_ENABLE		(0 << 3)
+#define HOTDIE_ENABLE		(0 << 4)
+#define RTC_ALARM_ENABLE	(0 << 5)
+#define RTC_PERIOD_ENABLE	(0 << 6)
+#define USB_OV_INT_ENABLE	(0 << 7)
+
+#define PLUG_IN_MASK		BIT(0)
+#define PLUG_OUT_MASK		BIT(1)
+#define CHGOK_MASK		BIT(2)
+#define CHGTE_MASK		BIT(3)
+#define CHGTS1_MASK		BIT(4)
+#define TS2_MASK		BIT(5)
+#define CHG_CVTLIM_MASK		BIT(6)
+#define DISCHG_ILIM_MASK	BIT(7)
+
+#define PLUG_IN_DISABLE		BIT(0)
+#define PLUG_OUT_DISABLE	BIT(1)
+#define CHGOK_DISABLE		BIT(2)
+#define CHGTE_DISABLE		BIT(3)
+#define CHGTS1_DISABLE		BIT(4)
+#define TS2_DISABLE		BIT(5)
+#define CHG_CVTLIM_DISABLE	BIT(6)
+#define DISCHG_ILIM_DISABLE	BIT(7)
+
+#define PLUG_IN_ENABLE		BIT(0)
+#define PLUG_OUT_ENABLE		BIT(1)
+#define CHGOK_ENABLE		BIT(2)
+#define CHGTE_ENABLE		BIT(3)
+#define CHGTS1_ENABLE		BIT(4)
+#define TS2_ENABLE		BIT(5)
+#define CHG_CVTLIM_ENABLE	BIT(6)
+#define DISCHG_ILIM_ENABLE	BIT(7)
+
 #define RK808_VBAT_LOW_2V8	0x00
 #define RK808_VBAT_LOW_2V9	0x01
 #define RK808_VBAT_LOW_3V0	0x02
@@ -381,7 +808,10 @@ enum rk805_reg {
 
 #define VOUT_LO_INT	BIT(0)
 #define CLK32KOUT2_EN	BIT(0)
+#define CLK32KOUT2_FUNC		(0 << 1)
+#define CLK32KOUT2_FUNC_MASK	BIT(1)
 
+#define TEMP105C			0x08
 #define TEMP115C			0x0c
 #define TEMP_HOTDIE_MSK			0x0c
 #define SLP_SD_MSK			(0x3 << 2)
@@ -391,6 +821,7 @@ enum rk805_reg {
 #define PWM_MODE_MSK			BIT(7)
 #define FPWM_MODE			BIT(7)
 #define AUTO_PWM_MODE			0
+#define REGS_WMSK			0xf0
 
 enum rk817_reg_id {
 	RK817_ID_DCDC1 = 0,
@@ -436,6 +867,10 @@ enum rk809_reg_id {
 #define RK817_RTC_INT_REG		0xf
 #define RK817_RTC_COMP_LSB_REG		0x10
 #define RK817_RTC_COMP_MSB_REG		0x11
+#define RK817_ADC_CONFIG0		0x50
+#define RK817_CURE_ADC_K0		0xb0
+#define RK817_POWER_EN_SAVE0            0x99
+#define RK817_POWER_EN_SAVE1            0xa4
 
 #define RK817_POWER_EN_REG(i)		(0xb1 + (i))
 #define RK817_POWER_SLP_EN_REG(i)	(0xb5 + (i))
@@ -462,6 +897,9 @@ enum rk809_reg_id {
 #define RK817_LDO_ON_VSEL_REG(idx)	(0xcc + (idx) * 2)
 #define RK817_BOOST_OTG_CFG		(0xde)
 
+#define RK817_CHRG_OUT			0xe4
+#define RK817_CHRG_IN			0xe5
+#define RK817_CHRG_STS			0xeb
 #define RK817_ID_MSB			0xed
 #define RK817_ID_LSB			0xee
 
@@ -609,16 +1047,27 @@ enum {
 	RK805_ID = 0x8050,
 	RK808_ID = 0x0000,
 	RK809_ID = 0x8090,
+	RK816_ID = 0x8160,
 	RK817_ID = 0x8170,
 	RK818_ID = 0x8180,
 };
 
+struct rk808_pin_info {
+	struct pinctrl *p;
+	struct pinctrl_state *reset;
+	struct pinctrl_state *power_off;
+	struct pinctrl_state *sleep;
+};
+
 struct rk808 {
 	struct i2c_client		*i2c;
 	struct regmap_irq_chip_data	*irq_data;
+	struct regmap_irq_chip_data	*battery_irq_data;
 	struct regmap			*regmap;
 	long				variant;
 	const struct regmap_config	*regmap_cfg;
 	const struct regmap_irq_chip	*regmap_irq_chip;
+	void				(*pm_pwroff_prep_fn)(void);
+	struct rk808_pin_info *pins;
 };
 #endif /* __LINUX_REGULATOR_RK808_H */
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 29aa50711..7768c644a 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -162,6 +162,10 @@ struct mmc_request {
 	bool			cap_cmd_during_tfr;
 
 	int			tag;
+#ifdef CONFIG_MMC_CRYPTO
+	const struct bio_crypt_ctx *crypto_ctx;
+	int			crypto_key_slot;
+#endif
 };
 
 struct mmc_card;
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 40d7e98fc..4e8b65492 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -374,6 +374,11 @@ struct mmc_host {
 #define MMC_CAP2_CQE_DCMD	(1 << 24)	/* CQE can issue a direct command */
 #define MMC_CAP2_AVOID_3_3V	(1 << 25)	/* Host must negotiate down from 3.3V */
 #define MMC_CAP2_MERGE_CAPABLE	(1 << 26)	/* Host can merge a segment over the segment size */
+#ifdef CONFIG_MMC_CRYPTO
+#define MMC_CAP2_CRYPTO		(1 << 27)	/* Host supports inline encryption */
+#else
+#define MMC_CAP2_CRYPTO		0
+#endif
 
 	int			fixed_drv_type;	/* fixed driver type for non-removable media */
 
@@ -468,6 +473,9 @@ struct mmc_host {
 	bool			cqe_enabled;
 	bool			cqe_on;
 
+#ifdef CONFIG_MMC_CRYPTO
+	struct blk_keyslot_manager ksm;
+#endif
 	/* Host Software Queue support */
 	bool			hsq_enabled;
 
diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h
index 545578fb8..29e09d23e 100644
--- a/include/linux/mmc/mmc.h
+++ b/include/linux/mmc/mmc.h
@@ -449,4 +449,11 @@ static inline bool mmc_ready_for_data(u32 status)
 
 #define mmc_driver_type_mask(n)		(1 << (n))
 
+struct mmc_card;
+extern int mmc_select_bus_width(struct mmc_card *card);
+extern int mmc_select_hs(struct mmc_card *card);
+extern int mmc_select_hs_ddr(struct mmc_card *card);
+extern int mmc_select_hs400(struct mmc_card *card);
+extern int mmc_hs200_tuning(struct mmc_card *card);
+extern int mmc_select_timing(struct mmc_card *card);
 #endif /* LINUX_MMC_MMC_H */
diff --git a/include/linux/mmc/pm.h b/include/linux/mmc/pm.h
index 3549f8045..1d554b858 100644
--- a/include/linux/mmc/pm.h
+++ b/include/linux/mmc/pm.h
@@ -23,5 +23,6 @@ typedef unsigned int mmc_pm_flag_t;
 
 #define MMC_PM_KEEP_POWER	(1 << 0)	/* preserve card power during suspend */
 #define MMC_PM_WAKE_SDIO_IRQ	(1 << 1)	/* wake up host system on SDIO IRQ assertion */
+#define MMC_PM_IGNORE_PM_NOTIFY	(1 << 2)	/* ignore mmc pm notify */
 
 #endif /* LINUX_MMC_PM_H */
diff --git a/include/linux/mmu_context.h b/include/linux/mmu_context.h
index 03dee12d2..bc4ac3c52 100644
--- a/include/linux/mmu_context.h
+++ b/include/linux/mmu_context.h
@@ -14,4 +14,12 @@
 static inline void leave_mm(int cpu) { }
 #endif
 
+/*
+ * CPUs that are capable of running task @p. By default, we assume a sane,
+ * homogeneous system. Must contain at least one active CPU.
+ */
+#ifndef task_cpu_possible_mask
+# define task_cpu_possible_mask(p)	cpu_possible_mask
+#endif
+
 #endif
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 81a55e974..e51b3d8e6 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -49,6 +49,7 @@ enum {
 	POWER_SUPPLY_CHARGE_TYPE_ADAPTIVE,	/* dynamically adjusted speed */
 	POWER_SUPPLY_CHARGE_TYPE_CUSTOM,	/* use CHARGE_CONTROL_* props */
 	POWER_SUPPLY_CHARGE_TYPE_LONGLIFE,	/* slow speed, longer life */
+	POWER_SUPPLY_CHARGE_TYPE_TAPER = 50,	/* charging in CV phase */
 };
 
 enum {
@@ -386,12 +387,22 @@ extern void power_supply_put(struct power_supply *psy);
 #ifdef CONFIG_OF
 extern struct power_supply *power_supply_get_by_phandle(struct device_node *np,
 							const char *property);
+extern int power_supply_get_by_phandle_array(struct device_node *np,
+					     const char *property,
+					     struct power_supply **psy,
+					     ssize_t size);
 extern struct power_supply *devm_power_supply_get_by_phandle(
 				    struct device *dev, const char *property);
 #else /* !CONFIG_OF */
 static inline struct power_supply *
 power_supply_get_by_phandle(struct device_node *np, const char *property)
 { return NULL; }
+static inline int
+power_supply_get_by_phandle_array(struct device_node *np,
+				  const char *property,
+				  struct power_supply **psy,
+				  int size)
+{ return 0; }
 static inline struct power_supply *
 devm_power_supply_get_by_phandle(struct device *dev, const char *property)
 { return NULL; }
@@ -426,9 +437,16 @@ static inline int power_supply_is_system_supplied(void) { return -ENOSYS; }
 extern int power_supply_get_property(struct power_supply *psy,
 			    enum power_supply_property psp,
 			    union power_supply_propval *val);
+#if IS_ENABLED(CONFIG_POWER_SUPPLY)
 extern int power_supply_set_property(struct power_supply *psy,
 			    enum power_supply_property psp,
 			    const union power_supply_propval *val);
+#else
+static inline int power_supply_set_property(struct power_supply *psy,
+			    enum power_supply_property psp,
+			    const union power_supply_propval *val)
+{ return 0; }
+#endif
 extern int power_supply_property_is_writeable(struct power_supply *psy,
 					enum power_supply_property psp);
 extern void power_supply_external_power_changed(struct power_supply *psy);
diff --git a/include/linux/pwm.h b/include/linux/pwm.h
index a13ff383f..cb4abed98 100644
--- a/include/linux/pwm.h
+++ b/include/linux/pwm.h
@@ -48,6 +48,10 @@ enum {
 	PWMF_EXPORTED = 1 << 1,
 };
 
+enum pwm_output_type {
+	PWM_OUTPUT_FIXED = 1 << 0,
+	PWM_OUTPUT_MODULATED = 1 << 1,
+};
 /*
  * struct pwm_state - state of a PWM channel
  * @period: PWM period (in nanoseconds)
@@ -59,6 +63,10 @@ struct pwm_state {
 	u64 period;
 	u64 duty_cycle;
 	enum pwm_polarity polarity;
+	enum pwm_output_type output_type;
+#ifdef CONFIG_PWM_ROCKCHIP_ONESHOT
+	u64 oneshot_count;
+#endif /* CONFIG_PWM_ROCKCHIP_ONESHOT */
 	bool enabled;
 };
 
@@ -145,6 +153,13 @@ static inline enum pwm_polarity pwm_get_polarity(const struct pwm_device *pwm)
 
 	return state.polarity;
 }
+static inline enum pwm_output_type pwm_get_output_type(
+		const struct pwm_device *pwm)
+{
+	struct pwm_state state;
+	pwm_get_state(pwm, &state);
+	return state.output_type;
+}
 
 static inline void pwm_get_args(const struct pwm_device *pwm,
 				struct pwm_args *args)
@@ -264,6 +279,8 @@ struct pwm_ops {
 		     const struct pwm_state *state);
 	void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm,
 			  struct pwm_state *state);
+	int (*get_output_type_supported)(struct pwm_chip *chip,
+			struct pwm_device *pwm);
 	struct module *owner;
 
 	/* Only used by legacy drivers */
@@ -317,6 +334,15 @@ struct pwm_device *pwm_request(int pwm_id, const char *label);
 void pwm_free(struct pwm_device *pwm);
 int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state);
 int pwm_adjust_config(struct pwm_device *pwm);
+static inline int pwm_get_output_type_supported(struct pwm_device *pwm)
+{
+	if (!pwm)
+		return -EINVAL;
+	if (pwm->chip->ops->get_output_type_supported)
+		return pwm->chip->ops->get_output_type_supported(pwm->chip,
+				pwm);
+	return PWM_OUTPUT_FIXED;
+}
 
 /**
  * pwm_config() - change a PWM device configuration
@@ -436,6 +462,10 @@ static inline int pwm_adjust_config(struct pwm_device *pwm)
 	return -ENOTSUPP;
 }
 
+static inline int pwm_get_output_type_supported(struct pwm_device *pwm)
+{
+	return -EINVAL;
+}
 static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
 			     int period_ns)
 {
diff --git a/include/linux/reboot-mode.h b/include/linux/reboot-mode.h
index 4a2abb38d..a7aa69d00 100644
--- a/include/linux/reboot-mode.h
+++ b/include/linux/reboot-mode.h
@@ -6,7 +6,9 @@ struct reboot_mode_driver {
 	struct device *dev;
 	struct list_head head;
 	int (*write)(struct reboot_mode_driver *reboot, unsigned int magic);
+	int (*read)(struct reboot_mode_driver *reboot);
 	struct notifier_block reboot_notifier;
+	struct notifier_block panic_notifier;
 };
 
 int reboot_mode_register(struct reboot_mode_driver *reboot);
diff --git a/include/linux/reboot.h b/include/linux/reboot.h
index 3734cd8f3..1c8d7856c 100644
--- a/include/linux/reboot.h
+++ b/include/linux/reboot.h
@@ -49,6 +49,26 @@ extern int register_restart_handler(struct notifier_block *);
 extern int unregister_restart_handler(struct notifier_block *);
 extern void do_kernel_restart(char *cmd);
 
+#ifdef CONFIG_NO_GKI
+extern int register_pre_restart_handler(struct notifier_block *nb);
+extern int unregister_pre_restart_handler(struct notifier_block *nb);
+extern void do_kernel_pre_restart(char *cmd);
+#else
+static inline int register_pre_restart_handler(struct notifier_block *nb)
+{
+	return 0;
+}
+
+static inline int unregister_pre_restart_handler(struct notifier_block *nb)
+{
+	return 0;
+}
+
+static inline void do_kernel_pre_restart(char *cmd)
+{
+}
+#endif
+
 /*
  * Architecture-specific implementations of sys_reboot commands.
  */
diff --git a/include/linux/sched.h b/include/linux/sched.h
index d590141f0..6e1cafd23 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -851,6 +851,9 @@ struct task_struct {
 	 */
 	struct uclamp_se		uclamp[UCLAMP_CNT];
 #endif
+#ifdef CONFIG_HOTPLUG_CPU
+	struct list_head		percpu_kthread_node;
+#endif
 
 #ifdef CONFIG_PREEMPT_NOTIFIERS
 	/* List of struct preempt_notifier: */
@@ -1034,6 +1037,10 @@ struct task_struct {
 	u64				stimescaled;
 #endif
 	u64				gtime;
+#ifdef CONFIG_CPU_FREQ_TIMES
+	u64				*time_in_state;
+	unsigned int			max_state;
+#endif
 	struct prev_cputime		prev_cputime;
 #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
 	struct vtime			vtime;
@@ -1145,6 +1152,7 @@ struct task_struct {
 	raw_spinlock_t			pi_lock;
 
 	struct wake_q_node		wake_q;
+	int				wake_q_count;
 
 #ifdef CONFIG_RT_MUTEXES
 	/* PI waiters blocked on a rt_mutex held by this task: */
@@ -1369,7 +1377,7 @@ struct task_struct {
 	u64				timer_slack_ns;
 	u64				default_timer_slack_ns;
 
-#ifdef CONFIG_KASAN
+#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS)
 	unsigned int			kasan_depth;
 #endif
 
diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h
index 40df88728..b41ca9c54 100644
--- a/include/linux/stmmac.h
+++ b/include/linux/stmmac.h
@@ -183,6 +183,7 @@ struct plat_stmmacenet_data {
 	void (*serdes_powerdown)(struct net_device *ndev, void *priv);
 	int (*init)(struct platform_device *pdev, void *priv);
 	void (*exit)(struct platform_device *pdev, void *priv);
+	void (*get_eth_addr)(void *priv, unsigned char *addr);
 	struct mac_device_info *(*setup)(void *priv);
 	void *bsp_priv;
 	struct clk *stmmac_clk;
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index a2d229ab6..4d352204e 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -525,6 +525,8 @@ extern struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev,
 extern int usb_string_ids_n(struct usb_composite_dev *c, unsigned n);
 
 extern void composite_disconnect(struct usb_gadget *gadget);
+extern void composite_reset(struct usb_gadget *gadget);
+
 extern int composite_setup(struct usb_gadget *gadget,
 		const struct usb_ctrlrequest *ctrl);
 extern void composite_suspend(struct usb_gadget *gadget);
@@ -590,6 +592,7 @@ struct usb_function_instance {
 	struct config_group group;
 	struct list_head cfs_list;
 	struct usb_function_driver *fd;
+	struct usb_function *f;
 	int (*set_inst_name)(struct usb_function_instance *inst,
 			      const char *name);
 	void (*free_func_inst)(struct usb_function_instance *inst);
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index e7351d64f..e5cdba2db 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -26,6 +26,7 @@
 #include <linux/types.h>
 #include <linux/workqueue.h>
 #include <linux/usb/ch9.h>
+#include <linux/android_kabi.h>
 
 #define UDC_TRACE_STR_MAX	512
 
@@ -122,6 +123,8 @@ struct usb_request {
 
 	int			status;
 	unsigned		actual;
+
+	ANDROID_KABI_RESERVE(1);
 };
 
 /*-------------------------------------------------------------------------*/
@@ -152,6 +155,8 @@ struct usb_ep_ops {
 
 	int (*fifo_status) (struct usb_ep *ep);
 	void (*fifo_flush) (struct usb_ep *ep);
+
+	ANDROID_KABI_RESERVE(1);
 };
 
 /**
@@ -217,6 +222,7 @@ struct usb_ep_caps {
  *	enabled and remains valid until the endpoint is disabled.
  * @comp_desc: In case of SuperSpeed support, this is the endpoint companion
  *	descriptor that is used to configure the endpoint
+ * @transfer_type: Used to specify transfer type of EP.
  *
  * the bus controller driver lists all the general purpose endpoints in
  * gadget->ep_list.  the control endpoint (gadget->ep0) is not in that list,
@@ -240,6 +246,11 @@ struct usb_ep {
 	u8			address;
 	const struct usb_endpoint_descriptor	*desc;
 	const struct usb_ss_ep_comp_descriptor	*comp_desc;
+#if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
+	u8			transfer_type;
+#endif
+
+	ANDROID_KABI_RESERVE(1);
 };
 
 /*-------------------------------------------------------------------------*/
@@ -323,9 +334,18 @@ struct usb_gadget_ops {
 			struct usb_gadget_driver *);
 	int	(*udc_stop)(struct usb_gadget *);
 	void	(*udc_set_speed)(struct usb_gadget *, enum usb_device_speed);
+	void	(*udc_set_ssp_rate)(struct usb_gadget *gadget,
+			enum usb_ssp_rate rate);
+	void	(*udc_async_callbacks)(struct usb_gadget *gadget, bool enable);
 	struct usb_ep *(*match_ep)(struct usb_gadget *,
 			struct usb_endpoint_descriptor *,
 			struct usb_ss_ep_comp_descriptor *);
+	int	(*check_config)(struct usb_gadget *gadget);
+
+	ANDROID_KABI_RESERVE(1);
+	ANDROID_KABI_RESERVE(2);
+	ANDROID_KABI_RESERVE(3);
+	ANDROID_KABI_RESERVE(4);
 };
 
 /**
@@ -339,6 +359,10 @@ struct usb_gadget_ops {
  * @speed: Speed of current connection to USB host.
  * @max_speed: Maximal speed the UDC can handle.  UDC must support this
  *      and all slower speeds.
+ * @ssp_rate: Current connected SuperSpeed Plus signaling rate and lane count.
+ * @max_ssp_rate: Maximum SuperSpeed Plus signaling rate and lane count the UDC
+ *	can handle. The UDC must support this and all slower speeds and lower
+ *	number of lanes.
  * @state: the state we are now (attached, suspended, configured, etc)
  * @name: Identifies the controller hardware type.  Used in diagnostics
  *	and sometimes configuration.
@@ -406,6 +430,11 @@ struct usb_gadget {
 	struct list_head		ep_list;	/* of usb_ep */
 	enum usb_device_speed		speed;
 	enum usb_device_speed		max_speed;
+
+	/* USB SuperSpeed Plus only */
+	enum usb_ssp_rate		ssp_rate;
+	enum usb_ssp_rate		max_ssp_rate;
+
 	enum usb_device_state		state;
 	const char			*name;
 	struct device			dev;
@@ -433,6 +462,11 @@ struct usb_gadget {
 	unsigned			connected:1;
 	unsigned			lpm_capable:1;
 	int				irq;
+
+	ANDROID_KABI_RESERVE(1);
+	ANDROID_KABI_RESERVE(2);
+	ANDROID_KABI_RESERVE(3);
+	ANDROID_KABI_RESERVE(4);
 };
 #define work_to_gadget(w)	(container_of((w), struct usb_gadget, work))
 
@@ -596,6 +630,7 @@ int usb_gadget_connect(struct usb_gadget *gadget);
 int usb_gadget_disconnect(struct usb_gadget *gadget);
 int usb_gadget_deactivate(struct usb_gadget *gadget);
 int usb_gadget_activate(struct usb_gadget *gadget);
+int usb_gadget_check_config(struct usb_gadget *gadget);
 #else
 static inline int usb_gadget_frame_number(struct usb_gadget *gadget)
 { return 0; }
@@ -619,6 +654,8 @@ static inline int usb_gadget_deactivate(struct usb_gadget *gadget)
 { return 0; }
 static inline int usb_gadget_activate(struct usb_gadget *gadget)
 { return 0; }
+static inline int usb_gadget_check_config(struct usb_gadget *gadget)
+{ return 0; }
 #endif /* CONFIG_USB_GADGET */
 
 /*-------------------------------------------------------------------------*/
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index c0cf20b19..70106b347 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -25,6 +25,7 @@
 #include <linux/rwsem.h>
 #include <linux/interrupt.h>
 #include <linux/idr.h>
+#include <linux/android_kabi.h>
 
 #define MAX_TOPO_LEVEL		6
 
@@ -228,6 +229,11 @@ struct usb_hcd {
 	 * (ohci 32, uhci 1024, ehci 256/512/1024).
 	 */
 
+	ANDROID_KABI_RESERVE(1);
+	ANDROID_KABI_RESERVE(2);
+	ANDROID_KABI_RESERVE(3);
+	ANDROID_KABI_RESERVE(4);
+
 	/* The HC driver's private data is stored at the end of
 	 * this structure.
 	 */
@@ -413,6 +419,10 @@ struct hc_driver {
 	/* Call for power on/off the port if necessary */
 	int	(*port_power)(struct usb_hcd *hcd, int portnum, bool enable);
 
+	ANDROID_KABI_RESERVE(1);
+	ANDROID_KABI_RESERVE(2);
+	ANDROID_KABI_RESERVE(3);
+	ANDROID_KABI_RESERVE(4);
 };
 
 static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd)
@@ -564,6 +574,11 @@ struct usb_tt {
 	spinlock_t		lock;
 	struct list_head	clear_list;	/* of usb_tt_clear */
 	struct work_struct	clear_work;
+
+	ANDROID_KABI_RESERVE(1);
+	ANDROID_KABI_RESERVE(2);
+	ANDROID_KABI_RESERVE(3);
+	ANDROID_KABI_RESERVE(4);
 };
 
 struct usb_tt_clear {
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index 69f1b6328..10cb3d193 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -12,6 +12,7 @@
 
 #include <linux/phy/phy.h>
 #include <linux/usb/phy.h>
+#include <linux/android_kabi.h>
 
 struct usb_otg {
 	u8			default_a;
@@ -40,6 +41,7 @@ struct usb_otg {
 	/* start or continue HNP role switch */
 	int	(*start_hnp)(struct usb_otg *otg);
 
+	ANDROID_KABI_RESERVE(1);
 };
 
 /**
diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h
index 433040ff8..96b7ff66f 100644
--- a/include/linux/usb/pd.h
+++ b/include/linux/usb/pd.h
@@ -225,6 +225,7 @@ enum pd_pdo_type {
 #define PDO_FIXED_EXTPOWER		BIT(27) /* Externally powered */
 #define PDO_FIXED_USB_COMM		BIT(26) /* USB communications capable */
 #define PDO_FIXED_DATA_SWAP		BIT(25) /* Data role swap supported */
+#define PDO_FIXED_UNCHUNK_EXT		BIT(24) /* Unchunked Extended Message supported (Source) */
 #define PDO_FIXED_FRS_CURR_MASK		(BIT(24) | BIT(23)) /* FR_Swap Current (Sink) */
 #define PDO_FIXED_FRS_CURR_SHIFT	23
 #define PDO_FIXED_VOLT_SHIFT		10	/* 50mV units */
@@ -466,6 +467,7 @@ static inline unsigned int rdo_max_power(u32 rdo)
 #define PD_T_DRP_SRC		30
 #define PD_T_PS_SOURCE_OFF	920
 #define PD_T_PS_SOURCE_ON	480
+#define PD_T_PS_SOURCE_ON_PRS	450	/* 390 - 480ms */
 #define PD_T_PS_HARD_RESET	30
 #define PD_T_SRC_RECOVER	760
 #define PD_T_SRC_RECOVER_MAX	1000
@@ -478,14 +480,19 @@ static inline unsigned int rdo_max_power(u32 rdo)
 #define PD_T_NEWSRC		250	/* Maximum of 275ms */
 #define PD_T_SWAP_SRC_START	20	/* Minimum of 20ms */
 #define PD_T_BIST_CONT_MODE	50	/* 30 - 60 ms */
+#define PD_T_SINK_TX		16	/* 16 - 20 ms */
+#define PD_T_CHUNK_NOT_SUPP	42	/* 40 - 50 ms */
 
 #define PD_T_DRP_TRY		100	/* 75 - 150 ms */
 #define PD_T_DRP_TRYWAIT	600	/* 400 - 800 ms */
 
 #define PD_T_CC_DEBOUNCE	200	/* 100 - 200 ms */
 #define PD_T_PD_DEBOUNCE	20	/* 10 - 20 ms */
+#define PD_T_TRY_CC_DEBOUNCE	15	/* 10 - 20 ms */
 
 #define PD_N_CAPS_COUNT		(PD_T_NO_RESPONSE / PD_T_SEND_SOURCE_CAP)
 #define PD_N_HARD_RESET_COUNT	2
 
+#define PD_P_SNK_STDBY_MW	2500	/* 2500 mW */
+
 #endif /* __LINUX_USB_PD_H */
diff --git a/include/linux/usb/pd_ext_sdb.h b/include/linux/usb/pd_ext_sdb.h
index 0eb83ce19..b517ebc8f 100644
--- a/include/linux/usb/pd_ext_sdb.h
+++ b/include/linux/usb/pd_ext_sdb.h
@@ -24,8 +24,4 @@ enum usb_pd_ext_sdb_fields {
 #define USB_PD_EXT_SDB_EVENT_OVP		BIT(3)
 #define USB_PD_EXT_SDB_EVENT_CF_CV_MODE		BIT(4)
 
-#define USB_PD_EXT_SDB_PPS_EVENTS	(USB_PD_EXT_SDB_EVENT_OCP |	\
-					 USB_PD_EXT_SDB_EVENT_OTP |	\
-					 USB_PD_EXT_SDB_EVENT_OVP)
-
 #endif /* __LINUX_USB_PD_EXT_SDB_H */
diff --git a/include/linux/usb/pd_vdo.h b/include/linux/usb/pd_vdo.h
index 68bdc4e2f..7f5e330a6 100644
--- a/include/linux/usb/pd_vdo.h
+++ b/include/linux/usb/pd_vdo.h
@@ -21,22 +21,24 @@
  * ----------
  * <31:16>  :: SVID
  * <15>     :: VDM type ( 1b == structured, 0b == unstructured )
- * <14:13>  :: Structured VDM version (can only be 00 == 1.0 currently)
+ * <14:13>  :: Structured VDM version
  * <12:11>  :: reserved
  * <10:8>   :: object position (1-7 valid ... used for enter/exit mode only)
  * <7:6>    :: command type (SVDM only?)
  * <5>      :: reserved (SVDM), command type (UVDM)
  * <4:0>    :: command
  */
-#define VDO(vid, type, custom)				\
+#define VDO(vid, type, ver, custom)			\
 	(((vid) << 16) |				\
 	 ((type) << 15) |				\
+	 ((ver) << 13) |				\
 	 ((custom) & 0x7FFF))
 
 #define VDO_SVDM_TYPE		(1 << 15)
 #define VDO_SVDM_VERS(x)	((x) << 13)
 #define VDO_OPOS(x)		((x) << 8)
 #define VDO_CMDT(x)		((x) << 6)
+#define VDO_SVDM_VERS_MASK	VDO_SVDM_VERS(0x3)
 #define VDO_OPOS_MASK		VDO_OPOS(0x7)
 #define VDO_CMDT_MASK		VDO_CMDT(0x3)
 
@@ -74,6 +76,7 @@
 
 #define PD_VDO_VID(vdo)		((vdo) >> 16)
 #define PD_VDO_SVDM(vdo)	(((vdo) >> 15) & 1)
+#define PD_VDO_SVDM_VER(vdo)	(((vdo) >> 13) & 0x3)
 #define PD_VDO_OPOS(vdo)	(((vdo) >> 8) & 0x7)
 #define PD_VDO_CMD(vdo)		((vdo) & 0x1f)
 #define PD_VDO_CMDT(vdo)	(((vdo) >> 6) & 0x3)
@@ -103,25 +106,50 @@
  * --------------------
  * <31>     :: data capable as a USB host
  * <30>     :: data capable as a USB device
- * <29:27>  :: product type
+ * <29:27>  :: product type (UFP / Cable / VPD)
  * <26>     :: modal operation supported (1b == yes)
- * <25:16>  :: Reserved, Shall be set to zero
+ * <25:23>  :: product type (DFP) (SVDM version 2.0+ only; set to zero in version 1.0)
+ * <22:21>  :: connector type (SVDM version 2.0+ only; set to zero in version 1.0)
+ * <20:16>  :: Reserved, Shall be set to zero
  * <15:0>   :: USB-IF assigned VID for this cable vendor
  */
+
+/* PD Rev2.0 definition */
 #define IDH_PTYPE_UNDEF		0
+
+/* SOP Product Type (UFP) */
+#define IDH_PTYPE_NOT_UFP	0
 #define IDH_PTYPE_HUB		1
 #define IDH_PTYPE_PERIPH	2
+#define IDH_PTYPE_PSD		3
+#define IDH_PTYPE_AMA		5
+
+/* SOP' Product Type (Cable Plug / VPD) */
+#define IDH_PTYPE_NOT_CABLE	0
 #define IDH_PTYPE_PCABLE	3
 #define IDH_PTYPE_ACABLE	4
-#define IDH_PTYPE_AMA		5
+#define IDH_PTYPE_VPD		6
 
-#define VDO_IDH(usbh, usbd, ptype, is_modal, vid)		\
-	((usbh) << 31 | (usbd) << 30 | ((ptype) & 0x7) << 27	\
-	 | (is_modal) << 26 | ((vid) & 0xffff))
+/* SOP Product Type (DFP) */
+#define IDH_PTYPE_NOT_DFP	0
+#define IDH_PTYPE_DFP_HUB	1
+#define IDH_PTYPE_DFP_HOST	2
+#define IDH_PTYPE_DFP_PB	3
+
+/* ID Header Mask */
+#define IDH_DFP_MASK		GENMASK(25, 23)
+#define IDH_CONN_MASK		GENMASK(22, 21)
+
+#define VDO_IDH(usbh, usbd, ufp_cable, is_modal, dfp, conn, vid)		\
+	((usbh) << 31 | (usbd) << 30 | ((ufp_cable) & 0x7) << 27		\
+	 | (is_modal) << 26 | ((dfp) & 0x7) << 23 | ((conn) & 0x3) << 21	\
+	 | ((vid) & 0xffff))
 
 #define PD_IDH_PTYPE(vdo)	(((vdo) >> 27) & 0x7)
 #define PD_IDH_VID(vdo)		((vdo) & 0xffff)
 #define PD_IDH_MODAL_SUPP(vdo)	((vdo) & (1 << 26))
+#define PD_IDH_DFP_PTYPE(vdo)	(((vdo) >> 23) & 0x7)
+#define PD_IDH_CONN_TYPE(vdo)	(((vdo) >> 21) & 0x3)
 
 /*
  * Cert Stat VDO
@@ -129,6 +157,7 @@
  * <31:0>  : USB-IF assigned XID for this cable
  */
 #define PD_CSTAT_XID(vdo)	(vdo)
+#define VDO_CERT(xid)		((xid) & 0xffffffff)
 
 /*
  * Product VDO
@@ -140,77 +169,270 @@
 #define PD_PRODUCT_PID(vdo)	(((vdo) >> 16) & 0xffff)
 
 /*
- * UFP VDO1
+ * UFP VDO (PD Revision 3.0+ only)
  * --------
  * <31:29> :: UFP VDO version
  * <28>    :: Reserved
  * <27:24> :: Device capability
- * <23:6>  :: Reserved
+ * <23:22> :: Connector type (10b == receptacle, 11b == captive plug)
+ * <21:11> :: Reserved
+ * <10:8>  :: Vconn power (AMA only)
+ * <7>     :: Vconn required (AMA only, 0b == no, 1b == yes)
+ * <6>     :: Vbus required (AMA only, 0b == yes, 1b == no)
  * <5:3>   :: Alternate modes
  * <2:0>   :: USB highest speed
  */
-#define PD_VDO1_UFP_DEVCAP(vdo)	(((vdo) & GENMASK(27, 24)) >> 24)
+#define PD_VDO_UFP_DEVCAP(vdo)	(((vdo) & GENMASK(27, 24)) >> 24)
 
+/* UFP VDO Version */
+#define UFP_VDO_VER1_2		2
+
+/* Device Capability */
 #define DEV_USB2_CAPABLE	BIT(0)
 #define DEV_USB2_BILLBOARD	BIT(1)
 #define DEV_USB3_CAPABLE	BIT(2)
 #define DEV_USB4_CAPABLE	BIT(3)
 
+/* Connector Type */
+#define UFP_RECEPTACLE		2
+#define UFP_CAPTIVE		3
+
+/* Vconn Power (AMA only, set to AMA_VCONN_NOT_REQ if Vconn is not required) */
+#define AMA_VCONN_PWR_1W	0
+#define AMA_VCONN_PWR_1W5	1
+#define AMA_VCONN_PWR_2W	2
+#define AMA_VCONN_PWR_3W	3
+#define AMA_VCONN_PWR_4W	4
+#define AMA_VCONN_PWR_5W	5
+#define AMA_VCONN_PWR_6W	6
+
+/* Vconn Required (AMA only) */
+#define AMA_VCONN_NOT_REQ	0
+#define AMA_VCONN_REQ		1
+
+/* Vbus Required (AMA only) */
+#define AMA_VBUS_REQ		0
+#define AMA_VBUS_NOT_REQ	1
+
+/* Alternate Modes */
+#define UFP_ALTMODE_NOT_SUPP	0
+#define UFP_ALTMODE_TBT3	BIT(0)
+#define UFP_ALTMODE_RECFG	BIT(1)
+#define UFP_ALTMODE_NO_RECFG	BIT(2)
+
+/* USB Highest Speed */
+#define UFP_USB2_ONLY		0
+#define UFP_USB32_GEN1		1
+#define UFP_USB32_4_GEN2	2
+#define UFP_USB4_GEN3		3
+
+#define VDO_UFP(ver, cap, conn, vcpwr, vcr, vbr, alt, spd)			\
+	(((ver) & 0x7) << 29 | ((cap) & 0xf) << 24 | ((conn) & 0x3) << 22	\
+	 | ((vcpwr) & 0x7) << 8 | (vcr) << 7 | (vbr) << 6 | ((alt) & 0x7) << 3	\
+	 | ((spd) & 0x7))
+
 /*
- * DFP VDO
+ * DFP VDO (PD Revision 3.0+ only)
  * --------
  * <31:29> :: DFP VDO version
  * <28:27> :: Reserved
  * <26:24> :: Host capability
- * <23:5>  :: Reserved
+ * <23:22> :: Connector type (10b == receptacle, 11b == captive plug)
+ * <21:5>  :: Reserved
  * <4:0>   :: Port number
  */
 #define PD_VDO_DFP_HOSTCAP(vdo)	(((vdo) & GENMASK(26, 24)) >> 24)
 
+#define DFP_VDO_VER1_1		1
 #define HOST_USB2_CAPABLE	BIT(0)
 #define HOST_USB3_CAPABLE	BIT(1)
 #define HOST_USB4_CAPABLE	BIT(2)
+#define DFP_RECEPTACLE		2
+#define DFP_CAPTIVE		3
+
+#define VDO_DFP(ver, cap, conn, pnum)						\
+	(((ver) & 0x7) << 29 | ((cap) & 0x7) << 24 | ((conn) & 0x3) << 22	\
+	 | ((pnum) & 0x1f))
 
 /*
- * Cable VDO
+ * Cable VDO (for both Passive and Active Cable VDO in PD Rev2.0)
  * ---------
  * <31:28> :: Cable HW version
  * <27:24> :: Cable FW version
  * <23:20> :: Reserved, Shall be set to zero
- * <19:18> :: type-C to Type-A/B/C (00b == A, 01 == B, 10 == C)
- * <17>    :: Type-C to Plug/Receptacle (0b == plug, 1b == receptacle)
+ * <19:18> :: type-C to Type-A/B/C/Captive (00b == A, 01 == B, 10 == C, 11 == Captive)
+ * <17>    :: Reserved, Shall be set to zero
  * <16:13> :: cable latency (0001 == <10ns(~1m length))
  * <12:11> :: cable termination type (11b == both ends active VCONN req)
  * <10>    :: SSTX1 Directionality support (0b == fixed, 1b == cfgable)
  * <9>     :: SSTX2 Directionality support
  * <8>     :: SSRX1 Directionality support
  * <7>     :: SSRX2 Directionality support
- * <6:5>   :: Vbus current handling capability
+ * <6:5>   :: Vbus current handling capability (01b == 3A, 10b == 5A)
  * <4>     :: Vbus through cable (0b == no, 1b == yes)
  * <3>     :: SOP" controller present? (0b == no, 1b == yes)
  * <2:0>   :: USB SS Signaling support
+ *
+ * Passive Cable VDO (PD Rev3.0+)
+ * ---------
+ * <31:28> :: Cable HW version
+ * <27:24> :: Cable FW version
+ * <23:21> :: VDO version
+ * <20>    :: Reserved, Shall be set to zero
+ * <19:18> :: Type-C to Type-C/Captive (10b == C, 11b == Captive)
+ * <17>    :: Reserved, Shall be set to zero
+ * <16:13> :: cable latency (0001 == <10ns(~1m length))
+ * <12:11> :: cable termination type (10b == Vconn not req, 01b == Vconn req)
+ * <10:9>  :: Maximum Vbus voltage (00b == 20V, 01b == 30V, 10b == 40V, 11b == 50V)
+ * <8:7>   :: Reserved, Shall be set to zero
+ * <6:5>   :: Vbus current handling capability (01b == 3A, 10b == 5A)
+ * <4:3>   :: Reserved, Shall be set to zero
+ * <2:0>   :: USB highest speed
+ *
+ * Active Cable VDO 1 (PD Rev3.0+)
+ * ---------
+ * <31:28> :: Cable HW version
+ * <27:24> :: Cable FW version
+ * <23:21> :: VDO version
+ * <20>    :: Reserved, Shall be set to zero
+ * <19:18> :: Connector type (10b == C, 11b == Captive)
+ * <17>    :: Reserved, Shall be set to zero
+ * <16:13> :: cable latency (0001 == <10ns(~1m length))
+ * <12:11> :: cable termination type (10b == one end active, 11b == both ends active VCONN req)
+ * <10:9>  :: Maximum Vbus voltage (00b == 20V, 01b == 30V, 10b == 40V, 11b == 50V)
+ * <8>     :: SBU supported (0b == supported, 1b == not supported)
+ * <7>     :: SBU type (0b == passive, 1b == active)
+ * <6:5>   :: Vbus current handling capability (01b == 3A, 10b == 5A)
+ * <2:0>   :: USB highest speed
  */
+/* Cable VDO Version */
+#define CABLE_VDO_VER1_0	0
+#define CABLE_VDO_VER1_3	3
+
+/* Connector Type (_ATYPE and _BTYPE are for PD Rev2.0 only) */
 #define CABLE_ATYPE		0
 #define CABLE_BTYPE		1
 #define CABLE_CTYPE		2
-#define CABLE_PLUG		0
-#define CABLE_RECEPTACLE	1
-#define CABLE_CURR_1A5		0
+#define CABLE_CAPTIVE		3
+
+/* Cable Latency */
+#define CABLE_LATENCY_1M	1
+#define CABLE_LATENCY_2M	2
+#define CABLE_LATENCY_3M	3
+#define CABLE_LATENCY_4M	4
+#define CABLE_LATENCY_5M	5
+#define CABLE_LATENCY_6M	6
+#define CABLE_LATENCY_7M	7
+#define CABLE_LATENCY_7M_PLUS	8
+
+/* Cable Termination Type */
+#define PCABLE_VCONN_NOT_REQ	0
+#define PCABLE_VCONN_REQ	1
+#define ACABLE_ONE_END		2
+#define ACABLE_BOTH_END		3
+
+/* Maximum Vbus Voltage */
+#define CABLE_MAX_VBUS_20V	0
+#define CABLE_MAX_VBUS_30V	1
+#define CABLE_MAX_VBUS_40V	2
+#define CABLE_MAX_VBUS_50V	3
+
+/* Active Cable SBU Supported/Type */
+#define ACABLE_SBU_SUPP		0
+#define ACABLE_SBU_NOT_SUPP	1
+#define ACABLE_SBU_PASSIVE	0
+#define ACABLE_SBU_ACTIVE	1
+
+/* Vbus Current Handling Capability */
+#define CABLE_CURR_DEF		0
 #define CABLE_CURR_3A		1
 #define CABLE_CURR_5A		2
+
+/* USB SuperSpeed Signaling Support (PD Rev2.0) */
 #define CABLE_USBSS_U2_ONLY	0
 #define CABLE_USBSS_U31_GEN1	1
 #define CABLE_USBSS_U31_GEN2	2
-#define VDO_CABLE(hw, fw, cbl, gdr, lat, term, tx1d, tx2d, rx1d, rx2d, cur,\
-		  vps, sopp, usbss) \
-	(((hw) & 0x7) << 28 | ((fw) & 0x7) << 24 | ((cbl) & 0x3) << 18	\
-	 | (gdr) << 17 | ((lat) & 0x7) << 13 | ((term) & 0x3) << 11	\
-	 | (tx1d) << 10 | (tx2d) << 9 | (rx1d) << 8 | (rx2d) << 7	\
-	 | ((cur) & 0x3) << 5 | (vps) << 4 | (sopp) << 3		\
-	 | ((usbss) & 0x7))
+
+/* USB Highest Speed */
+#define CABLE_USB2_ONLY		0
+#define CABLE_USB32_GEN1	1
+#define CABLE_USB32_4_GEN2	2
+#define CABLE_USB4_GEN3		3
+
+#define VDO_CABLE(hw, fw, cbl, lat, term, tx1d, tx2d, rx1d, rx2d, cur, vps, sopp, usbss) \
+	(((hw) & 0x7) << 28 | ((fw) & 0x7) << 24 | ((cbl) & 0x3) << 18		\
+	 | ((lat) & 0x7) << 13 | ((term) & 0x3) << 11 | (tx1d) << 10		\
+	 | (tx2d) << 9 | (rx1d) << 8 | (rx2d) << 7 | ((cur) & 0x3) << 5		\
+	 | (vps) << 4 | (sopp) << 3 | ((usbss) & 0x7))
+#define VDO_PCABLE(hw, fw, ver, conn, lat, term, vbm, cur, spd)			\
+	(((hw) & 0xf) << 28 | ((fw) & 0xf) << 24 | ((ver) & 0x7) << 21		\
+	 | ((conn) & 0x3) << 18 | ((lat) & 0xf) << 13 | ((term) & 0x3) << 11	\
+	 | ((vbm) & 0x3) << 9 | ((cur) & 0x3) << 5 | ((spd) & 0x7))
+#define VDO_ACABLE1(hw, fw, ver, conn, lat, term, vbm, sbu, sbut, cur, vbt, sopp, spd) \
+	(((hw) & 0xf) << 28 | ((fw) & 0xf) << 24 | ((ver) & 0x7) << 21		\
+	 | ((conn) & 0x3) << 18	| ((lat) & 0xf) << 13 | ((term) & 0x3) << 11	\
+	 | ((vbm) & 0x3) << 9 | (sbu) << 8 | (sbut) << 7 | ((cur) & 0x3) << 5	\
+	 | (vbt) << 4 | (sopp) << 3 | ((spd) & 0x7))
+
+#define VDO_TYPEC_CABLE_TYPE(vdo)	(((vdo) >> 18) & 0x3)
+
+/*
+ * Active Cable VDO 2
+ * ---------
+ * <31:24> :: Maximum operating temperature
+ * <23:16> :: Shutdown temperature
+ * <15>    :: Reserved, Shall be set to zero
+ * <14:12> :: U3/CLd power
+ * <11>    :: U3 to U0 transition mode (0b == direct, 1b == through U3S)
+ * <10>    :: Physical connection (0b == copper, 1b == optical)
+ * <9>     :: Active element (0b == redriver, 1b == retimer)
+ * <8>     :: USB4 supported (0b == yes, 1b == no)
+ * <7:6>   :: USB2 hub hops consumed
+ * <5>     :: USB2 supported (0b == yes, 1b == no)
+ * <4>     :: USB3.2 supported (0b == yes, 1b == no)
+ * <3>     :: USB lanes supported (0b == one lane, 1b == two lanes)
+ * <2>     :: Optically isolated active cable (0b == no, 1b == yes)
+ * <1>     :: Reserved, Shall be set to zero
+ * <0>     :: USB gen (0b == gen1, 1b == gen2+)
+ */
+
+/* U3/CLd Power*/
+#define ACAB2_U3_CLD_10MW_PLUS	0
+#define ACAB2_U3_CLD_10MW	1
+#define ACAB2_U3_CLD_5MW	2
+#define ACAB2_U3_CLD_1MW	3
+#define ACAB2_U3_CLD_500UW	4
+#define ACAB2_U3_CLD_200UW	5
+#define ACAB2_U3_CLD_50UW	6
+
+/* Other Active Cable VDO 2 Fields */
+#define ACAB2_U3U0_DIRECT	0
+#define ACAB2_U3U0_U3S		1
+#define ACAB2_PHY_COPPER	0
+#define ACAB2_PHY_OPTICAL	1
+#define ACAB2_REDRIVER		0
+#define ACAB2_RETIMER		1
+#define ACAB2_USB4_SUPP		0
+#define ACAB2_USB4_NOT_SUPP	1
+#define ACAB2_USB2_SUPP		0
+#define ACAB2_USB2_NOT_SUPP	1
+#define ACAB2_USB32_SUPP	0
+#define ACAB2_USB32_NOT_SUPP	1
+#define ACAB2_LANES_ONE		0
+#define ACAB2_LANES_TWO		1
+#define ACAB2_OPT_ISO_NO	0
+#define ACAB2_OPT_ISO_YES	1
+#define ACAB2_GEN_1		0
+#define ACAB2_GEN_2_PLUS	1
+
+#define VDO_ACABLE2(mtemp, stemp, u3p, trans, phy, ele, u4, hops, u2, u32, lane, iso, gen)	\
+	(((mtemp) & 0xff) << 24 | ((stemp) & 0xff) << 16 | ((u3p) & 0x7) << 12	\
+	 | (trans) << 11 | (phy) << 10 | (ele) << 9 | (u4) << 8			\
+	 | ((hops) & 0x3) << 6 | (u2) << 5 | (u32) << 4 | (lane) << 3		\
+	 | (iso) << 2 | (gen))
 
 /*
- * AMA VDO
+ * AMA VDO (PD Rev2.0)
  * ---------
  * <31:28> :: Cable HW version
  * <27:24> :: Cable FW version
@@ -233,18 +455,40 @@
 #define PD_VDO_AMA_VCONN_REQ(vdo)	(((vdo) >> 4) & 1)
 #define PD_VDO_AMA_VBUS_REQ(vdo)	(((vdo) >> 3) & 1)
 
-#define AMA_VCONN_PWR_1W	0
-#define AMA_VCONN_PWR_1W5	1
-#define AMA_VCONN_PWR_2W	2
-#define AMA_VCONN_PWR_3W	3
-#define AMA_VCONN_PWR_4W	4
-#define AMA_VCONN_PWR_5W	5
-#define AMA_VCONN_PWR_6W	6
 #define AMA_USBSS_U2_ONLY	0
 #define AMA_USBSS_U31_GEN1	1
 #define AMA_USBSS_U31_GEN2	2
 #define AMA_USBSS_BBONLY	3
 
+/*
+ * VPD VDO
+ * ---------
+ * <31:28> :: HW version
+ * <27:24> :: FW version
+ * <23:21> :: VDO version
+ * <20:17> :: Reserved, Shall be set to zero
+ * <16:15> :: Maximum Vbus voltage (00b == 20V, 01b == 30V, 10b == 40V, 11b == 50V)
+ * <14>    :: Charge through current support (0b == 3A, 1b == 5A)
+ * <13>    :: Reserved, Shall be set to zero
+ * <12:7>  :: Vbus impedance
+ * <6:1>   :: Ground impedance
+ * <0>     :: Charge through support (0b == no, 1b == yes)
+ */
+#define VPD_VDO_VER1_0		0
+#define VPD_MAX_VBUS_20V	0
+#define VPD_MAX_VBUS_30V	1
+#define VPD_MAX_VBUS_40V	2
+#define VPD_MAX_VBUS_50V	3
+#define VPDCT_CURR_3A		0
+#define VPDCT_CURR_5A		1
+#define VPDCT_NOT_SUPP		0
+#define VPDCT_SUPP		1
+
+#define VDO_VPD(hw, fw, ver, vbm, curr, vbi, gi, ct)			\
+	(((hw) & 0xf) << 28 | ((fw) & 0xf) << 24 | ((ver) & 0x7) << 21	\
+	 | ((vbm) & 0x3) << 15 | (curr) << 14 | ((vbi) & 0x3f) << 7	\
+	 | ((gi) & 0x3f) << 1 | (ct))
+
 /*
  * SVDM Discover SVIDs request -> response
  *
diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h
index e4de6bc1f..888d2fdb6 100644
--- a/include/linux/usb/phy.h
+++ b/include/linux/usb/phy.h
@@ -13,6 +13,7 @@
 #include <linux/extcon.h>
 #include <linux/notifier.h>
 #include <linux/usb.h>
+#include <linux/android_kabi.h>
 #include <uapi/linux/usb/charger.h>
 
 enum usb_phy_interface {
@@ -155,6 +156,8 @@ struct usb_phy {
 	 * manually detect the charger type.
 	 */
 	enum usb_charger_type (*charger_detect)(struct usb_phy *x);
+
+	ANDROID_KABI_RESERVE(1);
 };
 
 /* for board-specific init logic */
diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h
index 5e4c497f5..6567f7445 100644
--- a/include/linux/usb/quirks.h
+++ b/include/linux/usb/quirks.h
@@ -72,4 +72,7 @@
 /* device has endpoints that should be ignored */
 #define USB_QUIRK_ENDPOINT_IGNORE		BIT(15)
 
+/* device can't support auto suspend function */
+#define USB_QUIRK_AUTO_SUSPEND			BIT(16)
+
 #endif /* __LINUX_USB_QUIRKS_H */
diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h
index 09762d26f..cb69546f8 100644
--- a/include/linux/usb/tcpm.h
+++ b/include/linux/usb/tcpm.h
@@ -19,6 +19,10 @@ enum typec_cc_status {
 	TYPEC_CC_RP_3_0,
 };
 
+/* Collision Avoidance */
+#define SINK_TX_NG	TYPEC_CC_RP_1_5
+#define SINK_TX_OK	TYPEC_CC_RP_3_0
+
 enum typec_cc_polarity {
 	TYPEC_POLARITY_CC1,
 	TYPEC_POLARITY_CC2,
@@ -62,6 +66,8 @@ enum tcpm_transmit_type {
  *		For example, some tcpcs may include BC1.2 charger detection
  *		and use that in this case.
  * @set_cc:	Called to set value of CC pins
+ * @apply_rc:	Optional; Needed to move TCPCI based chipset to APPLY_RC state
+ *		as stated by the TCPCI specification.
  * @get_cc:	Called to read current CC pin values
  * @set_polarity:
  *		Called to set polarity
@@ -83,6 +89,39 @@ enum tcpm_transmit_type {
  *		Optional; Called to enable/disable PD 3.0 fast role swap.
  *		Enabling frs is accessory dependent as not all PD3.0
  *		accessories support fast role swap.
+ * @frs_sourcing_vbus:
+ *		Optional; Called to notify that vbus is now being sourced.
+ *		Low level drivers can perform chip specific operations, if any.
+ * @enable_auto_vbus_discharge:
+ *		Optional; TCPCI spec based TCPC implementations can optionally
+ *		support hardware to autonomously dischrge vbus upon disconnecting
+ *		as sink or source. TCPM signals TCPC to enable the mechanism upon
+ *		entering connected state and signals disabling upon disconnect.
+ * @set_auto_vbus_discharge_threshold:
+ *		Mandatory when enable_auto_vbus_discharge is implemented. TCPM
+ *		calls this function to allow lower levels drivers to program the
+ *		vbus threshold voltage below which the vbus discharge circuit
+ *		will be turned on. requested_vbus_voltage is set to 0 when vbus
+ *		is going to disappear knowingly i.e. during PR_SWAP and
+ *		HARD_RESET etc.
+ * @is_vbus_vsafe0v:
+ *		Optional; TCPCI spec based TCPC implementations are expected to
+ *		detect VSAFE0V voltage level at vbus. When detection of VSAFE0V
+ *		is supported by TCPC, set this callback for TCPM to query
+ *		whether vbus is at VSAFE0V when needed.
+ *		Returns true when vbus is at VSAFE0V, false otherwise.
+ * @set_partner_usb_comm_capable:
+ *              Optional; The USB Communications Capable bit indicates if port
+ *              partner is capable of communication over the USB data lines
+ *              (e.g. D+/- or SS Tx/Rx). Called to notify the status of the bit.
+ * @check_contaminant:
+ *		Optional; The callback is called when CC pins report open status
+ *		at the end of the toggling period. Chip level drivers are
+ *		expected to check for contaminant and re-enable toggling if
+ *		needed. When 0 is not returned, check_contaminant is expected to
+ *		restart toggling after checking the connector for contaminant.
+ *		This forces the TCPM state machine to tranistion to TOGGLING state
+ *		without calling start_toggling callback.
  */
 struct tcpc_dev {
 	struct fwnode_handle *fwnode;
@@ -91,6 +130,8 @@ struct tcpc_dev {
 	int (*get_vbus)(struct tcpc_dev *dev);
 	int (*get_current_limit)(struct tcpc_dev *dev);
 	int (*set_cc)(struct tcpc_dev *dev, enum typec_cc_status cc);
+	int (*apply_rc)(struct tcpc_dev *dev, enum typec_cc_status cc,
+			enum typec_cc_polarity polarity);
 	int (*get_cc)(struct tcpc_dev *dev, enum typec_cc_status *cc1,
 		      enum typec_cc_status *cc2);
 	int (*set_polarity)(struct tcpc_dev *dev,
@@ -106,9 +147,16 @@ struct tcpc_dev {
 			      enum typec_cc_status cc);
 	int (*try_role)(struct tcpc_dev *dev, int role);
 	int (*pd_transmit)(struct tcpc_dev *dev, enum tcpm_transmit_type type,
-			   const struct pd_message *msg);
+			   const struct pd_message *msg, unsigned int negotiated_rev);
 	int (*set_bist_data)(struct tcpc_dev *dev, bool on);
 	int (*enable_frs)(struct tcpc_dev *dev, bool enable);
+	void (*frs_sourcing_vbus)(struct tcpc_dev *dev);
+	int (*enable_auto_vbus_discharge)(struct tcpc_dev *dev, bool enable);
+	int (*set_auto_vbus_discharge_threshold)(struct tcpc_dev *dev, enum typec_pwr_opmode mode,
+						 bool pps_active, u32 requested_vbus_voltage);
+	int (*check_contaminant)(struct tcpc_dev *dev);
+	bool (*is_vbus_vsafe0v)(struct tcpc_dev *dev);
+	void (*set_partner_usb_comm_capable)(struct tcpc_dev *dev, bool enable);
 };
 
 struct tcpm_port;
@@ -116,6 +164,10 @@ struct tcpm_port;
 struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc);
 void tcpm_unregister_port(struct tcpm_port *port);
 
+int tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo,
+				  unsigned int nr_pdo,
+				  unsigned int operating_snk_mw);
+
 void tcpm_vbus_change(struct tcpm_port *port);
 void tcpm_cc_change(struct tcpm_port *port);
 void tcpm_sink_frs(struct tcpm_port *port);
@@ -126,5 +178,7 @@ void tcpm_pd_transmit_complete(struct tcpm_port *port,
 			       enum tcpm_transmit_status status);
 void tcpm_pd_hard_reset(struct tcpm_port *port);
 void tcpm_tcpc_reset(struct tcpm_port *port);
+bool tcpm_is_debouncing(struct tcpm_port *tcpm);
+bool tcpm_is_toggling(struct tcpm_port *port);
 
 #endif /* __LINUX_USB_TCPM_H */
diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h
index 6be558045..34cc102b3 100644
--- a/include/linux/usb/typec.h
+++ b/include/linux/usb/typec.h
@@ -4,6 +4,7 @@
 #define __LINUX_USB_TYPEC_H
 
 #include <linux/types.h>
+#include <linux/android_kabi.h>
 
 /* USB Type-C Specification releases */
 #define USB_TYPEC_REV_1_0	0x100 /* 1.0 */
@@ -17,6 +18,7 @@ struct typec_partner;
 struct typec_cable;
 struct typec_plug;
 struct typec_port;
+struct typec_altmode_ops;
 
 struct fwnode_handle;
 struct device;
@@ -126,15 +128,31 @@ struct typec_altmode_desc {
 	enum typec_port_data	roles;
 };
 
+void typec_partner_set_pd_revision(struct typec_partner *partner, u16 pd_revision);
+int typec_partner_set_num_altmodes(struct typec_partner *partner, int num_altmodes);
 struct typec_altmode
 *typec_partner_register_altmode(struct typec_partner *partner,
 				const struct typec_altmode_desc *desc);
+int typec_plug_set_num_altmodes(struct typec_plug *plug, int num_altmodes);
 struct typec_altmode
 *typec_plug_register_altmode(struct typec_plug *plug,
 			     const struct typec_altmode_desc *desc);
 struct typec_altmode
 *typec_port_register_altmode(struct typec_port *port,
 			     const struct typec_altmode_desc *desc);
+
+#ifdef CONFIG_NO_GKI
+void typec_port_register_altmodes(struct typec_port *port,
+	const struct typec_altmode_ops *ops, void *drvdata,
+	struct typec_altmode **altmodes, size_t n);
+#else
+static inline void typec_port_register_altmodes(struct typec_port *port,
+	const struct typec_altmode_ops *ops, void *drvdata,
+	struct typec_altmode **altmodes, size_t n)
+{
+}
+#endif
+
 void typec_unregister_altmode(struct typec_altmode *altmode);
 
 struct typec_port *typec_altmode2port(struct typec_altmode *alt);
@@ -162,6 +180,7 @@ struct typec_plug_desc {
  * @type: The plug type from USB PD Cable VDO
  * @active: Is the cable active or passive
  * @identity: Result of Discover Identity command
+ * @pd_revision: USB Power Delivery Specification revision if supported
  *
  * Represents USB Type-C Cable attached to USB Type-C port.
  */
@@ -169,6 +188,8 @@ struct typec_cable_desc {
 	enum typec_plug_type	type;
 	unsigned int		active:1;
 	struct usb_pd_identity	*identity;
+	u16			pd_revision; /* 0300H = "3.0" */
+
 };
 
 /*
@@ -176,15 +197,22 @@ struct typec_cable_desc {
  * @usb_pd: USB Power Delivery support
  * @accessory: Audio, Debug or none.
  * @identity: Discover Identity command data
+ * @pd_revision: USB Power Delivery Specification Revision if supported
  *
  * Details about a partner that is attached to USB Type-C port. If @identity
  * member exists when partner is registered, a directory named "identity" is
  * created to sysfs for the partner device.
+ *
+ * @pd_revision is based on the setting of the "Specification Revision" field
+ * in the message header on the initial "Source Capabilities" message received
+ * from the partner, or a "Request" message received from the partner, depending
+ * on whether our port is a Sink or a Source.
  */
 struct typec_partner_desc {
 	unsigned int		usb_pd:1;
 	enum typec_accessory	accessory;
 	struct usb_pd_identity	*identity;
+	u16			pd_revision; /* 0300H = "3.0" */
 };
 
 /**
@@ -202,6 +230,13 @@ struct typec_operations {
 	int (*vconn_set)(struct typec_port *port, enum typec_role role);
 	int (*port_type_set)(struct typec_port *port,
 			     enum typec_port_type type);
+	ANDROID_KABI_RESERVE(1);
+};
+
+enum usb_pd_svdm_ver {
+	SVDM_VER_1_0 = 0,
+	SVDM_VER_2_0 = 1,
+	SVDM_VER_MAX = SVDM_VER_2_0,
 };
 
 /*
@@ -210,6 +245,7 @@ struct typec_operations {
  * @data: Supported data role of the port
  * @revision: USB Type-C Specification release. Binary coded decimal
  * @pd_revision: USB Power Delivery Specification revision if supported
+ * @svdm_version: USB PD Structured VDM version if supported
  * @prefer_role: Initial role preference (DRP ports).
  * @accessory: Supported Accessory Modes
  * @fwnode: Optional fwnode of the port
@@ -223,6 +259,7 @@ struct typec_capability {
 	enum typec_port_data	data;
 	u16			revision; /* 0120H = "1.2" */
 	u16			pd_revision; /* 0300H = "3.0" */
+	enum usb_pd_svdm_ver	svdm_version;
 	int			prefer_role;
 	enum typec_accessory	accessory[TYPEC_MAX_ACCESSORY];
 	unsigned int		orientation_aware:1;
@@ -231,6 +268,7 @@ struct typec_capability {
 	void			*driver_data;
 
 	const struct typec_operations	*ops;
+	ANDROID_KABI_RESERVE(1);
 };
 
 /* Specific to try_role(). Indicates the user want's to clear the preference. */
@@ -273,4 +311,8 @@ int typec_find_orientation(const char *name);
 int typec_find_port_power_role(const char *name);
 int typec_find_power_role(const char *name);
 int typec_find_port_data_role(const char *name);
+
+void typec_partner_set_svdm_version(struct typec_partner *partner,
+				    enum usb_pd_svdm_ver svdm_version);
+int typec_get_negotiated_svdm_version(struct typec_port *port);
 #endif /* __LINUX_USB_TYPEC_H */
diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h
index 5e0a7b764..60531284d 100644
--- a/include/linux/usb/typec_altmode.h
+++ b/include/linux/usb/typec_altmode.h
@@ -30,6 +30,7 @@ struct typec_altmode {
 
 	char				*desc;
 	const struct typec_altmode_ops	*ops;
+	ANDROID_KABI_RESERVE(1);
 };
 
 #define to_typec_altmode(d) container_of(d, struct typec_altmode, dev)
@@ -63,6 +64,7 @@ struct typec_altmode_ops {
 	int (*notify)(struct typec_altmode *altmode, unsigned long conf,
 		      void *data);
 	int (*activate)(struct typec_altmode *altmode, int activate);
+	ANDROID_KABI_RESERVE(1);
 };
 
 int typec_altmode_enter(struct typec_altmode *altmode, u32 *vdo);
@@ -132,6 +134,16 @@ typec_altmode_get_orientation(struct typec_altmode *altmode)
 	return typec_get_orientation(typec_altmode2port(altmode));
 }
 
+/**
+ * typec_altmode_get_svdm_version - Get negotiated SVDM version
+ * @altmode: Handle to the alternate mode
+ */
+static inline int
+typec_altmode_get_svdm_version(struct typec_altmode *altmode)
+{
+	return typec_get_negotiated_svdm_version(typec_altmode2port(altmode));
+}
+
 /**
  * struct typec_altmode_driver - USB Type-C alternate mode device driver
  * @id_table: Null terminated array of SVIDs
diff --git a/include/linux/usb/typec_tbt.h b/include/linux/usb/typec_tbt.h
index 47c2d501d..63dd44b72 100644
--- a/include/linux/usb/typec_tbt.h
+++ b/include/linux/usb/typec_tbt.h
@@ -39,12 +39,16 @@ struct typec_thunderbolt_data {
 #define   TBT_CABLE_USB3_GEN1		1
 #define   TBT_CABLE_USB3_PASSIVE	2
 #define   TBT_CABLE_10_AND_20GBPS	3
-#define TBT_CABLE_ROUNDED		BIT(19)
+#define TBT_CABLE_ROUNDED_SUPPORT(_vdo_) \
+					(((_vdo_) & GENMASK(20, 19)) >> 19)
+#define   TBT_GEN3_NON_ROUNDED                 0
+#define   TBT_GEN3_GEN4_ROUNDED_NON_ROUNDED    1
 #define TBT_CABLE_OPTICAL		BIT(21)
 #define TBT_CABLE_RETIMER		BIT(22)
 #define TBT_CABLE_LINK_TRAINING		BIT(23)
 
 #define TBT_SET_CABLE_SPEED(_s_)	(((_s_) & GENMASK(2, 0)) << 16)
+#define TBT_SET_CABLE_ROUNDED(_g_)	(((_g_) & GENMASK(1, 0)) << 19)
 
 /* TBT3 Device Enter Mode VDO bits */
 #define TBT_ENTER_MODE_CABLE_SPEED(s)	TBT_SET_CABLE_SPEED(s)
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index 8110c29fa..195512a3a 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -23,6 +23,8 @@
 #ifndef	__LINUX_USB_USBNET_H
 #define	__LINUX_USB_USBNET_H
 
+#include <linux/android_kabi.h>
+
 /* interface from usbnet core to each USB networking link we handle */
 struct usbnet {
 	/* housekeeping */
@@ -83,8 +85,11 @@ struct usbnet {
 #		define EVENT_LINK_CHANGE	11
 #		define EVENT_SET_RX_MODE	12
 #		define EVENT_NO_IP_ALIGN	13
-	u32			rx_speed;	/* in bps - NOT Mbps */
-	u32			tx_speed;	/* in bps - NOT Mbps */
+
+	ANDROID_KABI_RESERVE(1);
+	ANDROID_KABI_RESERVE(2);
+	ANDROID_KABI_RESERVE(3);
+	ANDROID_KABI_RESERVE(4);
 };
 
 static inline struct usb_driver *driver_of(struct usb_interface *intf)
@@ -174,6 +179,9 @@ struct driver_info {
 	int		out;		/* tx endpoint */
 
 	unsigned long	data;		/* Misc driver specific data */
+
+	ANDROID_KABI_RESERVE(1);
+	ANDROID_KABI_RESERVE(2);
 };
 
 /* Minidrivers are just drivers using the "usbnet" core as a powerful
diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
index 92cd9f038..03b43b0f7 100644
--- a/include/media/v4l2-async.h
+++ b/include/media/v4l2-async.h
@@ -266,6 +266,21 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev,
 int v4l2_async_subdev_notifier_register(struct v4l2_subdev *sd,
 					struct v4l2_async_notifier *notifier);
 
+/**
+ * v4l2_async_notifier_clr_unready_dev - remove unready subdevice
+ *
+ * @notifier: pointer to &struct v4l2_async_notifier
+ */
+#if IS_ENABLED(CONFIG_NO_GKI)
+int v4l2_async_notifier_clr_unready_dev(struct v4l2_async_notifier *notifier);
+#else
+static inline int
+v4l2_async_notifier_clr_unready_dev(struct v4l2_async_notifier *notifier)
+{
+	return 0;
+}
+#endif
+
 /**
  * v4l2_async_notifier_unregister - unregisters a subdevice
  *	asynchronous notifier
diff --git a/include/soc/rockchip/rockchip_sip.h b/include/soc/rockchip/rockchip_sip.h
index c46a9ae2a..4afba01c6 100644
--- a/include/soc/rockchip/rockchip_sip.h
+++ b/include/soc/rockchip/rockchip_sip.h
@@ -15,6 +15,12 @@
 #define ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE	0x05
 #define ROCKCHIP_SIP_CONFIG_DRAM_CLR_IRQ	0x06
 #define ROCKCHIP_SIP_CONFIG_DRAM_SET_PARAM	0x07
-#define ROCKCHIP_SIP_CONFIG_DRAM_SET_ODT_PD	0x08
+#define ROCKCHIP_SIP_CONFIG_DRAM_GET_VERSION	0x08
+#define ROCKCHIP_SIP_CONFIG_DRAM_POST_SET_RATE	0x09
+#define ROCKCHIP_SIP_CONFIG_DRAM_SET_MSCH_RL	0x0a
+#define ROCKCHIP_SIP_CONFIG_DRAM_DEBUG		0x0b
+#define ROCKCHIP_SIP_CONFIG_MCU_START		0x0c
+#define ROCKCHIP_SIP_CONFIG_DRAM_GET_FREQ_INFO	0x0e
+#define ROCKCHIP_SIP_CONFIG_DRAM_ADDRMAP_GET	0x10
 
 #endif
diff --git a/include/sound/hdmi-codec.h b/include/sound/hdmi-codec.h
index b55970859..4fc733c8c 100644
--- a/include/sound/hdmi-codec.h
+++ b/include/sound/hdmi-codec.h
@@ -34,6 +34,11 @@ struct hdmi_codec_daifmt {
 	unsigned int frame_clk_inv:1;
 	unsigned int bit_clk_master:1;
 	unsigned int frame_clk_master:1;
+	/* bit_fmt could be standard PCM format or
+	 * IEC958 encoded format. ALSA IEC958 plugin will pass
+	 * IEC958_SUBFRAME format to the underneath driver.
+	 */
+	snd_pcm_format_t bit_fmt;
 };
 
 /*
@@ -60,12 +65,22 @@ struct hdmi_codec_ops {
 
 	/*
 	 * Configures HDMI-encoder for audio stream.
-	 * Mandatory
+	 * Having either prepare or hw_params is mandatory.
 	 */
 	int (*hw_params)(struct device *dev, void *data,
 			 struct hdmi_codec_daifmt *fmt,
 			 struct hdmi_codec_params *hparms);
 
+	/*
+	 * Configures HDMI-encoder for audio stream. Can be called
+	 * multiple times for each setup.
+	 *
+	 * Having either prepare or hw_params is mandatory.
+	 */
+	int (*prepare)(struct device *dev, void *data,
+		       struct hdmi_codec_daifmt *fmt,
+		       struct hdmi_codec_params *hparms);
+
 	/*
 	 * Shuts down the audio stream.
 	 * Mandatory
diff --git a/include/sound/hwdep.h b/include/sound/hwdep.h
index 8d6cdb254..d99a53025 100644
--- a/include/sound/hwdep.h
+++ b/include/sound/hwdep.h
@@ -9,6 +9,7 @@
 
 #include <sound/asound.h>
 #include <linux/poll.h>
+#include <linux/android_kabi.h>
 
 struct snd_hwdep;
 
@@ -34,6 +35,8 @@ struct snd_hwdep_ops {
 			  struct snd_hwdep_dsp_status *status);
 	int (*dsp_load)(struct snd_hwdep *hw,
 			struct snd_hwdep_dsp_image *image);
+
+	ANDROID_KABI_RESERVE(1);
 };
 
 struct snd_hwdep {
@@ -59,6 +62,8 @@ struct snd_hwdep {
 	int used;			/* reference counter */
 	unsigned int dsp_loaded;	/* bit fields of loaded dsp indices */
 	unsigned int exclusive:1;	/* exclusive access mode */
+
+	ANDROID_KABI_RESERVE(1);
 };
 
 extern int snd_hwdep_new(struct snd_card *card, char *id, int device,
diff --git a/include/sound/pcm_iec958.h b/include/sound/pcm_iec958.h
index 0939aa45e..64e84441c 100644
--- a/include/sound/pcm_iec958.h
+++ b/include/sound/pcm_iec958.h
@@ -4,6 +4,14 @@
 
 #include <linux/types.h>
 
+int snd_pcm_create_iec958_consumer_default(u8 *cs, size_t len);
+
+int snd_pcm_fill_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
+				 size_t len);
+
+int snd_pcm_fill_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
+					   u8 *cs, size_t len);
+
 int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
 	size_t len);
 
diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h
index dad9d3b4a..1b26ff448 100644
--- a/include/uapi/drm/drm_fourcc.h
+++ b/include/uapi/drm/drm_fourcc.h
@@ -242,6 +242,8 @@ extern "C" {
  * index 1 = Cr:Cb plane, [39:0] Cr1:Cb1:Cr0:Cb0 little endian
  */
 #define DRM_FORMAT_NV15		fourcc_code('N', 'V', '1', '5') /* 2x2 subsampled Cr:Cb plane */
+#define DRM_FORMAT_NV20		fourcc_code('N', 'V', '2', '0') /* 2x1 subsampled Cr:Cb plane */
+#define DRM_FORMAT_NV30		fourcc_code('N', 'V', '3', '0') /* non-subsampled Cr:Cb plane */
 
 /*
  * 2 plane YCbCr MSB aligned
diff --git a/include/uapi/linux/dma-buf.h b/include/uapi/linux/dma-buf.h
index f76d11725..37aeab6d1 100644
--- a/include/uapi/linux/dma-buf.h
+++ b/include/uapi/linux/dma-buf.h
@@ -47,4 +47,12 @@ struct dma_buf_sync {
 #define DMA_BUF_SET_NAME_A	_IOW(DMA_BUF_BASE, 1, __u32)
 #define DMA_BUF_SET_NAME_B	_IOW(DMA_BUF_BASE, 1, __u64)
 
+struct dma_buf_sync_partial {
+	__u64 flags;
+	__u32 offset;
+	__u32 len;
+};
+
+#define DMA_BUF_IOCTL_SYNC_PARTIAL	_IOW(DMA_BUF_BASE, 2, struct dma_buf_sync_partial)
+
 #endif
diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h
index 84fa53ffb..096f891d4 100644
--- a/include/uapi/linux/media-bus-format.h
+++ b/include/uapi/linux/media-bus-format.h
@@ -34,7 +34,7 @@
 
 #define MEDIA_BUS_FMT_FIXED			0x0001
 
-/* RGB - next is	0x101d */
+/* RGB - next is	0x1024 */
 #define MEDIA_BUS_FMT_RGB444_1X12		0x1016
 #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE	0x1001
 #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE	0x1002
@@ -63,6 +63,10 @@
 #define MEDIA_BUS_FMT_RGB101010_1X30		0x1018
 #define MEDIA_BUS_FMT_RGB121212_1X36		0x1019
 #define MEDIA_BUS_FMT_RGB161616_1X48		0x101a
+#define MEDIA_BUS_FMT_RGB888_DUMMY_4X8		0x101f
+#define MEDIA_BUS_FMT_BGR888_DUMMY_4X8		0x1020
+#define MEDIA_BUS_FMT_RGB101010_1X7X5_SPWG	0x1022
+#define MEDIA_BUS_FMT_RGB101010_1X7X5_JEIDA	0x1023
 
 /* YUV (including grey) - next is	0x202e */
 #define MEDIA_BUS_FMT_Y8_1X8			0x2001
diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h
index f51bc8f36..7e3fe68af 100644
--- a/include/uapi/linux/serial_reg.h
+++ b/include/uapi/linux/serial_reg.h
@@ -30,6 +30,7 @@
  * Sleep mode for ST16650 and TI16750.  For the ST16650, EFR[4]=1
  */
 #define UART_IERX_SLEEP		0x10 /* Enable sleep mode */
+#define UART_IER_PTIME		0x80 /* Enable programmable transmit interrupt mode */
 
 #define UART_IIR	2	/* In:  Interrupt ID Register */
 #define UART_IIR_NO_INT		0x01 /* No interrupts pending */
diff --git a/include/uapi/linux/usb/g_uvc.h b/include/uapi/linux/usb/g_uvc.h
index 652f169a0..428926e35 100644
--- a/include/uapi/linux/usb/g_uvc.h
+++ b/include/uapi/linux/usb/g_uvc.h
@@ -19,7 +19,9 @@
 #define UVC_EVENT_STREAMOFF		(V4L2_EVENT_PRIVATE_START + 3)
 #define UVC_EVENT_SETUP			(V4L2_EVENT_PRIVATE_START + 4)
 #define UVC_EVENT_DATA			(V4L2_EVENT_PRIVATE_START + 5)
-#define UVC_EVENT_LAST			(V4L2_EVENT_PRIVATE_START + 5)
+#define UVC_EVENT_SUSPEND		(V4L2_EVENT_PRIVATE_START + 6)
+#define UVC_EVENT_RESUME		(V4L2_EVENT_PRIVATE_START + 7)
+#define UVC_EVENT_LAST			(V4L2_EVENT_PRIVATE_START + 7)
 
 struct uvc_request_data {
 	__s32 length;
diff --git a/include/uapi/linux/usb/video.h b/include/uapi/linux/usb/video.h
index bfdae12cd..b82436fd9 100644
--- a/include/uapi/linux/usb/video.h
+++ b/include/uapi/linux/usb/video.h
@@ -567,5 +567,63 @@ struct UVC_FRAME_MJPEG(n) {				\
 	__le32 dwFrameInterval[n];			\
 } __attribute__ ((packed))
 
+/* Frame Based Payload - 3.1.1. Frame Based Video Format Descriptor */
+struct uvc_format_framebased {
+	__u8  bLength;
+	__u8  bDescriptorType;
+	__u8  bDescriptorSubType;
+	__u8  bFormatIndex;
+	__u8  bNumFrameDescriptors;
+	__u8  guidFormat[16];
+	__u8  bBitsPerPixel;
+	__u8  bDefaultFrameIndex;
+	__u8  bAspectRatioX;
+	__u8  bAspectRatioY;
+	__u8  bmInterfaceFlags;
+	__u8  bCopyProtect;
+	__u8  bVariableSize;
+} __attribute__((__packed__));
+
+#define UVC_DT_FORMAT_FRAMEBASED_SIZE			28
+
+/* Frame Based Payload - 3.1.2. Frame Based Video Frame Descriptor */
+struct uvc_frame_framebased {
+	__u8  bLength;
+	__u8  bDescriptorType;
+	__u8  bDescriptorSubType;
+	__u8  bFrameIndex;
+	__u8  bmCapabilities;
+	__u16 wWidth;
+	__u16 wHeight;
+	__u32 dwMinBitRate;
+	__u32 dwMaxBitRate;
+	__u32 dwDefaultFrameInterval;
+	__u8  bFrameIntervalType;
+	__u32 dwBytesPerLine;
+	__u32 dwFrameInterval[];
+} __attribute__((__packed__));
+
+#define UVC_DT_FRAME_FRAMEBASED_SIZE(n)                        (26+4*(n))
+
+#define UVC_FRAME_FRAMEBASED(n) \
+	uvc_frame_framebased_##n
+
+#define DECLARE_UVC_FRAME_FRAMEBASED(n)		\
+struct UVC_FRAME_FRAMEBASED(n) {		\
+	__u8  bLength;				\
+	__u8  bDescriptorType;			\
+	__u8  bDescriptorSubType;		\
+	__u8  bFrameIndex;			\
+	__u8  bmCapabilities;			\
+	__u16 wWidth;				\
+	__u16 wHeight;				\
+	__u32 dwMinBitRate;			\
+	__u32 dwMaxBitRate;			\
+	__u32 dwDefaultFrameInterval;		\
+	__u8  bFrameIntervalType;		\
+	__u32 dwBytesPerLine;			\
+	__u32 dwFrameInterval[n];		\
+} __attribute__ ((packed))
+
 #endif /* __LINUX_USB_VIDEO_H */
 
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index a184c4939..b80ac7753 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -65,6 +65,7 @@
 #define V4L2_CTRL_CLASS_FM_RX		0x00a10000	/* FM Receiver controls */
 #define V4L2_CTRL_CLASS_RF_TUNER	0x00a20000	/* RF tuner controls */
 #define V4L2_CTRL_CLASS_DETECT		0x00a30000	/* Detection controls */
+#define V4L2_CTRL_CLASS_COLORIMETRY	0x00a50000	/* Colorimetry controls */
 
 /* User-class control IDs */
 
@@ -188,6 +189,12 @@ enum v4l2_colorfx {
  */
 #define V4L2_CID_USER_MAX217X_BASE		(V4L2_CID_USER_BASE + 0x1090)
 
+/*
+ * The base for the tc35874x driver controls.
+ * We reserve 16 controls for this driver.
+ */
+#define V4L2_CID_USER_TC35874X_BASE		(V4L2_CID_USER_BASE + 0x10a0)
+
 /* The base for the imx driver controls.
  * We reserve 16 controls for this driver. */
 #define V4L2_CID_USER_IMX_BASE			(V4L2_CID_USER_BASE + 0x10b0)
@@ -415,6 +422,12 @@ enum v4l2_mpeg_video_multi_slice_mode {
 #define V4L2_CID_MPEG_VIDEO_MV_H_SEARCH_RANGE		(V4L2_CID_MPEG_BASE+227)
 #define V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE		(V4L2_CID_MPEG_BASE+228)
 #define V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME		(V4L2_CID_MPEG_BASE+229)
+#define V4L2_CID_MPEG_VIDEO_BASELAYER_PRIORITY_ID	(V4L2_CID_MPEG_BASE+230)
+#define V4L2_CID_MPEG_VIDEO_AU_DELIMITER		(V4L2_CID_MPEG_BASE+231)
+#define V4L2_CID_MPEG_VIDEO_LTR_COUNT			(V4L2_CID_MPEG_BASE+232)
+#define V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX		(V4L2_CID_MPEG_BASE+233)
+#define V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES		(V4L2_CID_MPEG_BASE+234)
+#define V4L2_CID_MPEG_VIDEO_DEC_CONCEAL_COLOR		(V4L2_CID_MPEG_BASE+235)
 
 /* CIDs for the MPEG-2 Part 2 (H.262) codec */
 #define V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL			(V4L2_CID_MPEG_BASE+270)
@@ -578,6 +591,15 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type {
 #define V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MAX_QP	(V4L2_CID_MPEG_BASE+386)
 #define V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MIN_QP	(V4L2_CID_MPEG_BASE+387)
 #define V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MAX_QP	(V4L2_CID_MPEG_BASE+388)
+#define V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MIN_QP	(V4L2_CID_MPEG_BASE+389)
+#define V4L2_CID_MPEG_VIDEO_H264_B_FRAME_MAX_QP	(V4L2_CID_MPEG_BASE+390)
+#define V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L0_BR	(V4L2_CID_MPEG_BASE+391)
+#define V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L1_BR	(V4L2_CID_MPEG_BASE+392)
+#define V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L2_BR	(V4L2_CID_MPEG_BASE+393)
+#define V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L3_BR	(V4L2_CID_MPEG_BASE+394)
+#define V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L4_BR	(V4L2_CID_MPEG_BASE+395)
+#define V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L5_BR	(V4L2_CID_MPEG_BASE+396)
+#define V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L6_BR	(V4L2_CID_MPEG_BASE+397)
 #define V4L2_CID_MPEG_VIDEO_MPEG4_I_FRAME_QP	(V4L2_CID_MPEG_BASE+400)
 #define V4L2_CID_MPEG_VIDEO_MPEG4_P_FRAME_QP	(V4L2_CID_MPEG_BASE+401)
 #define V4L2_CID_MPEG_VIDEO_MPEG4_B_FRAME_QP	(V4L2_CID_MPEG_BASE+402)
@@ -768,6 +790,15 @@ enum v4l2_mpeg_video_frame_skip_mode {
 	V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT	= 2,
 };
 
+#define V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_MIN_QP		(V4L2_CID_MPEG_BASE + 647)
+#define V4L2_CID_MPEG_VIDEO_HEVC_I_FRAME_MAX_QP		(V4L2_CID_MPEG_BASE + 648)
+#define V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_MIN_QP		(V4L2_CID_MPEG_BASE + 649)
+#define V4L2_CID_MPEG_VIDEO_HEVC_P_FRAME_MAX_QP		(V4L2_CID_MPEG_BASE + 650)
+#define V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MIN_QP		(V4L2_CID_MPEG_BASE + 651)
+#define V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MAX_QP		(V4L2_CID_MPEG_BASE + 652)
+#define V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY		(V4L2_CID_MPEG_BASE + 653)
+#define V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE	(V4L2_CID_MPEG_BASE + 654)
+
 /*  MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */
 #define V4L2_CID_MPEG_CX2341X_BASE				(V4L2_CTRL_CLASS_MPEG | 0x1000)
 #define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE		(V4L2_CID_MPEG_CX2341X_BASE+0)
@@ -1171,4 +1202,38 @@ enum v4l2_detect_md_mode {
 #define V4L2_CID_DETECT_MD_THRESHOLD_GRID	(V4L2_CID_DETECT_CLASS_BASE + 3)
 #define V4L2_CID_DETECT_MD_REGION_GRID		(V4L2_CID_DETECT_CLASS_BASE + 4)
 
+#define V4L2_CID_COLORIMETRY_CLASS_BASE	(V4L2_CTRL_CLASS_COLORIMETRY | 0x900)
+#define V4L2_CID_COLORIMETRY_CLASS	(V4L2_CTRL_CLASS_COLORIMETRY | 1)
+
+#define V4L2_CID_COLORIMETRY_HDR10_CLL_INFO	(V4L2_CID_COLORIMETRY_CLASS_BASE + 0)
+
+struct v4l2_ctrl_hdr10_cll_info {
+	__u16 max_content_light_level;
+	__u16 max_pic_average_light_level;
+};
+
+#define V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY	(V4L2_CID_COLORIMETRY_CLASS_BASE + 1)
+
+#define V4L2_HDR10_MASTERING_PRIMARIES_X_LOW	5
+#define V4L2_HDR10_MASTERING_PRIMARIES_X_HIGH	37000
+#define V4L2_HDR10_MASTERING_PRIMARIES_Y_LOW	5
+#define V4L2_HDR10_MASTERING_PRIMARIES_Y_HIGH	42000
+#define V4L2_HDR10_MASTERING_WHITE_POINT_X_LOW	5
+#define V4L2_HDR10_MASTERING_WHITE_POINT_X_HIGH	37000
+#define V4L2_HDR10_MASTERING_WHITE_POINT_Y_LOW	5
+#define V4L2_HDR10_MASTERING_WHITE_POINT_Y_HIGH	42000
+#define V4L2_HDR10_MASTERING_MAX_LUMA_LOW	50000
+#define V4L2_HDR10_MASTERING_MAX_LUMA_HIGH	100000000
+#define V4L2_HDR10_MASTERING_MIN_LUMA_LOW	1
+#define V4L2_HDR10_MASTERING_MIN_LUMA_HIGH	50000
+
+struct v4l2_ctrl_hdr10_mastering_display {
+	__u16 display_primaries_x[3];
+	__u16 display_primaries_y[3];
+	__u16 white_point_x;
+	__u16 white_point_y;
+	__u32 max_display_mastering_luminance;
+	__u32 min_display_mastering_luminance;
+};
+
 #endif
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index b28817c59..1e650c284 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -689,6 +689,7 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_JPEG     v4l2_fourcc('J', 'P', 'E', 'G') /* JFIF JPEG     */
 #define V4L2_PIX_FMT_DV       v4l2_fourcc('d', 'v', 's', 'd') /* 1394          */
 #define V4L2_PIX_FMT_MPEG     v4l2_fourcc('M', 'P', 'E', 'G') /* MPEG-1/2/4 Multiplexed */
+#define V4L2_PIX_FMT_H265     v4l2_fourcc('H', '2', '6', '5') /* H265 with start codes */
 #define V4L2_PIX_FMT_H264     v4l2_fourcc('H', '2', '6', '4') /* H264 with start codes */
 #define V4L2_PIX_FMT_H264_NO_SC v4l2_fourcc('A', 'V', 'C', '1') /* H264 without start codes */
 #define V4L2_PIX_FMT_H264_MVC v4l2_fourcc('M', '2', '6', '4') /* H264 MVC */
@@ -1778,6 +1779,14 @@ enum v4l2_ctrl_type {
 	V4L2_CTRL_TYPE_U16	     = 0x0101,
 	V4L2_CTRL_TYPE_U32	     = 0x0102,
 	V4L2_CTRL_TYPE_AREA          = 0x0106,
+	V4L2_CTRL_TYPE_HDR10_CLL_INFO		= 0x0110,
+	V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY	= 0x0111,
+	V4L2_CTRL_TYPE_H264_SPS             = 0x0200,
+	V4L2_CTRL_TYPE_H264_PPS		    = 0x0201,
+	V4L2_CTRL_TYPE_H264_SCALING_MATRIX  = 0x0202,
+	V4L2_CTRL_TYPE_H264_SLICE_PARAMS    = 0x0203,
+	V4L2_CTRL_TYPE_H264_DECODE_PARAMS   = 0x0204,
+	V4L2_CTRL_TYPE_H264_PRED_WEIGHTS    = 0x0205,
 };
 
 /*  Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
diff --git a/init/Kconfig b/init/Kconfig
index 4b5a6fc0f..cf5edde30 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1352,6 +1352,11 @@ config BLK_DEV_INITRD
 if BLK_DEV_INITRD
 
 source "usr/Kconfig"
+config INITRD_ASYNC
+	bool "Initrd async"
+	depends on NO_GKI
+	help
+	  Init ramdisk async, can reduce kernel init time.
 
 endif
 
@@ -1711,6 +1716,13 @@ config KALLSYMS
 	  symbolic stack backtraces. This increases the size of the kernel
 	  somewhat, as all symbols have to be loaded into the kernel image.
 
+config WGCM
+	bool "Enable Workgroup Control Monitor API"
+	default n
+	help
+	  Enable Workgroup Control Monitor API, userspace can get the number of
+	  blocked threads.
+
 config KALLSYMS_ALL
 	bool "Include all symbols in kallsyms"
 	depends on DEBUG_KERNEL && KALLSYMS
diff --git a/init/initramfs.c b/init/initramfs.c
index 55b74d7e5..f4c4e2404 100644
--- a/init/initramfs.c
+++ b/init/initramfs.c
@@ -12,6 +12,7 @@
 #include <linux/file.h>
 #include <linux/memblock.h>
 #include <linux/namei.h>
+#include <linux/initramfs.h>
 #include <linux/init_syscalls.h>
 
 static ssize_t __init xwrite(struct file *file, const char *p, size_t count,
@@ -465,6 +466,9 @@ static char * __init unpack_to_rootfs(char *buf, unsigned long len)
 	state = Start;
 	this_header = 0;
 	message = NULL;
+#if defined(CONFIG_ROCKCHIP_THUNDER_BOOT) && defined(CONFIG_ROCKCHIP_HW_DECOMPRESS) && defined(CONFIG_INITRD_ASYNC)
+	wait_initrd_hw_decom_done();
+#endif
 	while (!message && len) {
 		loff_t saved_offset = this_header;
 		if (*buf == '0' && !(this_header & 3)) {
@@ -639,4 +643,23 @@ static int __init populate_rootfs(void)
 	flush_delayed_fput();
 	return 0;
 }
+
+#if IS_BUILTIN(CONFIG_INITRD_ASYNC)
+#include <linux/kthread.h>
+#include <linux/async.h>
+
+static void __init unpack_rootfs_async(void *unused, async_cookie_t cookie)
+{
+	populate_rootfs();
+}
+
+static int __init populate_rootfs_async(void)
+{
+	async_schedule(unpack_rootfs_async, NULL);
+	return 0;
+}
+
+pure_initcall(populate_rootfs_async);
+#else
 rootfs_initcall(populate_rootfs);
+#endif
diff --git a/init/main.c b/init/main.c
index 30d6afeae..f54324ddb 100644
--- a/init/main.c
+++ b/init/main.c
@@ -113,6 +113,10 @@
 
 #include <kunit/test.h>
 
+#ifdef CONFIG_SCHED_RTG_AUTHORITY
+extern int init_rtg_authority_control(void);
+#endif
+
 static int kernel_init(void *);
 
 extern void init_IRQ(void);
@@ -926,6 +930,10 @@ asmlinkage __visible void __init __no_sanitize_address start_kernel(void)
 		local_irq_disable();
 	radix_tree_init();
 
+#ifdef CONFIG_SCHED_RTG_AUTHORITY
+	BUG_ON(init_rtg_authority_control());
+#endif
+
 	/*
 	 * Set up housekeeping before setting up workqueues to allow the unbound
 	 * workqueue to take non-housekeeping into account.
@@ -1524,6 +1532,9 @@ static noinline void __init kernel_init_freeable(void)
 	smp_init();
 	sched_init_smp();
 
+#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT
+	kthread_run(defer_free_memblock, NULL, "defer_mem");
+#endif
 	padata_init();
 	page_alloc_init_late();
 	/* Initialize page ext after all struct pages are initialized. */
@@ -1532,6 +1543,9 @@ static noinline void __init kernel_init_freeable(void)
 	do_basic_setup();
 
 	kunit_run_all_tests();
+#if IS_BUILTIN(CONFIG_INITRD_ASYNC)
+	async_synchronize_full();
+#endif
 
 	console_on_rootfs();
 
diff --git a/ipc/msg.c b/ipc/msg.c
index 8ded6b8f1..6e6c8e0c9 100644
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -147,7 +147,7 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params)
 	key_t key = params->key;
 	int msgflg = params->flg;
 
-	msq = kvmalloc(sizeof(*msq), GFP_KERNEL_ACCOUNT);
+	msq = kvmalloc(sizeof(*msq), GFP_KERNEL);
 	if (unlikely(!msq))
 		return -ENOMEM;
 
diff --git a/ipc/namespace.c b/ipc/namespace.c
index c94c05846..24e7b4532 100644
--- a/ipc/namespace.c
+++ b/ipc/namespace.c
@@ -42,7 +42,7 @@ static struct ipc_namespace *create_ipc_ns(struct user_namespace *user_ns,
 		goto fail;
 
 	err = -ENOMEM;
-	ns = kzalloc(sizeof(struct ipc_namespace), GFP_KERNEL_ACCOUNT);
+	ns = kzalloc(sizeof(struct ipc_namespace), GFP_KERNEL);
 	if (ns == NULL)
 		goto fail_dec;
 
diff --git a/ipc/sem.c b/ipc/sem.c
index 916f7a90b..d735cecd9 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -511,7 +511,7 @@ static struct sem_array *sem_alloc(size_t nsems)
 	if (nsems > (INT_MAX - sizeof(*sma)) / sizeof(sma->sems[0]))
 		return NULL;
 
-	sma = kvzalloc(struct_size(sma, sems, nsems), GFP_KERNEL_ACCOUNT);
+	sma = kvzalloc(struct_size(sma, sems, nsems), GFP_KERNEL);
 	if (unlikely(!sma))
 		return NULL;
 
@@ -1852,7 +1852,7 @@ static inline int get_undo_list(struct sem_undo_list **undo_listp)
 
 	undo_list = current->sysvsem.undo_list;
 	if (!undo_list) {
-		undo_list = kzalloc(sizeof(*undo_list), GFP_KERNEL_ACCOUNT);
+		undo_list = kzalloc(sizeof(*undo_list), GFP_KERNEL);
 		if (undo_list == NULL)
 			return -ENOMEM;
 		spin_lock_init(&undo_list->lock);
@@ -1937,7 +1937,7 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
 	rcu_read_unlock();
 
 	/* step 2: allocate new undo structure */
-	new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL_ACCOUNT);
+	new = kzalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL);
 	if (!new) {
 		ipc_rcu_putref(&sma->sem_perm, sem_rcu_free);
 		return ERR_PTR(-ENOMEM);
diff --git a/ipc/shm.c b/ipc/shm.c
index b418731d6..471ac3e74 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -711,7 +711,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
 			ns->shm_tot + numpages > ns->shm_ctlall)
 		return -ENOSPC;
 
-	shp = kvmalloc(sizeof(*shp), GFP_KERNEL_ACCOUNT);
+	shp = kvmalloc(sizeof(*shp), GFP_KERNEL);
 	if (unlikely(!shp))
 		return -ENOMEM;
 
diff --git a/ipc/util.c b/ipc/util.c
index 7c3601dad..bbb5190af 100644
--- a/ipc/util.c
+++ b/ipc/util.c
@@ -754,13 +754,21 @@ struct pid_namespace *ipc_seq_pid_ns(struct seq_file *s)
 static struct kern_ipc_perm *sysvipc_find_ipc(struct ipc_ids *ids, loff_t pos,
 					      loff_t *new_pos)
 {
-	struct kern_ipc_perm *ipc = NULL;
-	int max_idx = ipc_get_maxidx(ids);
+	struct kern_ipc_perm *ipc;
+	int total, id;
+
+	total = 0;
+	for (id = 0; id < pos && total < ids->in_use; id++) {
+		ipc = idr_find(&ids->ipcs_idr, id);
+		if (ipc != NULL)
+			total++;
+	}
 
-	if (max_idx == -1 || pos > max_idx)
+	ipc = NULL;
+	if (total >= ids->in_use)
 		goto out;
 
-	for (; pos <= max_idx; pos++) {
+	for (; pos < ipc_mni; pos++) {
 		ipc = idr_find(&ids->ipcs_idr, pos);
 		if (ipc != NULL) {
 			rcu_read_lock();
diff --git a/kernel/cgroup/legacy_freezer.c b/kernel/cgroup/legacy_freezer.c
index 08236798d..081d026f1 100644
--- a/kernel/cgroup/legacy_freezer.c
+++ b/kernel/cgroup/legacy_freezer.c
@@ -479,3 +479,4 @@ struct cgroup_subsys freezer_cgrp_subsys = {
 	.fork		= freezer_fork,
 	.legacy_cftypes	= files,
 };
+EXPORT_SYMBOL_GPL(freezer_cgrp_subsys);
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 2cda6a3ef..7b8933e78 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -275,11 +275,13 @@ void cpu_maps_update_begin(void)
 {
 	mutex_lock(&cpu_add_remove_lock);
 }
+EXPORT_SYMBOL_GPL(cpu_maps_update_begin);
 
 void cpu_maps_update_done(void)
 {
 	mutex_unlock(&cpu_add_remove_lock);
 }
+EXPORT_SYMBOL_GPL(cpu_maps_update_done);
 
 /*
  * If set, cpu_up and cpu_down will return -EBUSY and do nothing.
@@ -1054,7 +1056,7 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen,
 	struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu);
 	int prev_state, ret = 0;
 
-	if (num_online_cpus() == 1)
+	if (num_active_cpus() == 1 && cpu_active(cpu))
 		return -EBUSY;
 
 	if (!cpu_present(cpu))
diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c
index e2999a070..79cb6d063 100644
--- a/kernel/irq/generic-chip.c
+++ b/kernel/irq/generic-chip.c
@@ -200,6 +200,7 @@ int irq_gc_set_wake(struct irq_data *d, unsigned int on)
 	irq_gc_unlock(gc);
 	return 0;
 }
+EXPORT_SYMBOL_GPL(irq_gc_set_wake);
 
 static u32 irq_readl_be(void __iomem *addr)
 {
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index 5899260a8..f5b51d8fe 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -17,4 +17,5 @@ obj-$(CONFIG_PM_WAKELOCKS)	+= wakelock.o
 
 obj-$(CONFIG_MAGIC_SYSRQ)	+= poweroff.o
 
+
 obj-$(CONFIG_ENERGY_MODEL)	+= energy_model.o
diff --git a/kernel/power/energy_model.c b/kernel/power/energy_model.c
index 119b929dc..41430128d 100644
--- a/kernel/power/energy_model.c
+++ b/kernel/power/energy_model.c
@@ -52,6 +52,17 @@ static int em_debug_cpus_show(struct seq_file *s, void *unused)
 }
 DEFINE_SHOW_ATTRIBUTE(em_debug_cpus);
 
+static int em_debug_units_show(struct seq_file *s, void *unused)
+{
+	struct em_perf_domain *pd = s->private;
+	char *units = pd->milliwatts ? "milliWatts" : "bogoWatts";
+
+	seq_printf(s, "%s\n", units);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(em_debug_units);
+
 static void em_debug_create_pd(struct device *dev)
 {
 	struct dentry *d;
@@ -64,6 +75,8 @@ static void em_debug_create_pd(struct device *dev)
 		debugfs_create_file("cpus", 0444, d, dev->em_pd->cpus,
 				    &em_debug_cpus_fops);
 
+	debugfs_create_file("units", 0444, d, dev->em_pd, &em_debug_units_fops);
+
 	/* Create a sub-directory for each performance state */
 	for (i = 0; i < dev->em_pd->nr_perf_states; i++)
 		em_debug_create_ps(&dev->em_pd->table[i], d);
@@ -245,17 +258,24 @@ EXPORT_SYMBOL_GPL(em_cpu_get);
  * @cpus	: Pointer to cpumask_t, which in case of a CPU device is
  *		obligatory. It can be taken from i.e. 'policy->cpus'. For other
  *		type of devices this should be set to NULL.
+ * @milliwatts	: Flag indicating that the power values are in milliWatts or
+ *		in some other scale. It must be set properly.
  *
  * Create Energy Model tables for a performance domain using the callbacks
  * defined in cb.
  *
+ * The @milliwatts is important to set with correct value. Some kernel
+ * sub-systems might rely on this flag and check if all devices in the EM are
+ * using the same scale.
+ *
  * If multiple clients register the same performance domain, all but the first
  * registration will be ignored.
  *
  * Return 0 on success
  */
 int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
-				struct em_data_callback *cb, cpumask_t *cpus)
+				struct em_data_callback *cb, cpumask_t *cpus,
+				bool milliwatts)
 {
 	unsigned long cap, prev_cap = 0;
 	int cpu, ret;
@@ -308,6 +328,8 @@ int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
 	if (ret)
 		goto unlock;
 
+	dev->em_pd->milliwatts = milliwatts;
+
 	em_debug_create_pd(dev);
 	dev_info(dev, "EM: created perf domain\n");
 
diff --git a/kernel/power/power.h b/kernel/power/power.h
index 24f12d534..778bf431e 100644
--- a/kernel/power/power.h
+++ b/kernel/power/power.h
@@ -106,7 +106,7 @@ extern int create_basic_memory_bitmaps(void);
 extern void free_basic_memory_bitmaps(void);
 extern int hibernate_preallocate_memory(void);
 
-extern void clear_free_pages(void);
+extern void clear_or_poison_free_pages(void);
 
 /**
  *	Auxiliary structure used for reading the snapshot image data and
diff --git a/kernel/power/process.c b/kernel/power/process.c
index b9faa363c..013dda3f1 100644
--- a/kernel/power/process.c
+++ b/kernel/power/process.c
@@ -85,23 +85,27 @@ static int try_to_freeze_tasks(bool user_only)
 	elapsed = ktime_sub(end, start);
 	elapsed_msecs = ktime_to_ms(elapsed);
 
-	if (todo) {
+	if (wakeup) {
 		pr_cont("\n");
-		pr_err("Freezing of tasks %s after %d.%03d seconds "
-		       "(%d tasks refusing to freeze, wq_busy=%d):\n",
-		       wakeup ? "aborted" : "failed",
+		pr_err("Freezing of tasks aborted after %d.%03d seconds",
+		       elapsed_msecs / 1000, elapsed_msecs % 1000);
+	} else if (todo) {
+		pr_cont("\n");
+		pr_err("Freezing of tasks failed after %d.%03d seconds"
+		       " (%d tasks refusing to freeze, wq_busy=%d):\n",
 		       elapsed_msecs / 1000, elapsed_msecs % 1000,
 		       todo - wq_busy, wq_busy);
 
 		if (wq_busy)
 			show_workqueue_state();
 
-		if (!wakeup || pm_debug_messages_on) {
+		if (pm_debug_messages_on) {
 			read_lock(&tasklist_lock);
 			for_each_process_thread(g, p) {
 				if (p != current && !freezer_should_skip(p)
-				    && freezing(p) && !frozen(p))
+				    && freezing(p) && !frozen(p)) {
 					sched_show_task(p);
+				}
 			}
 			read_unlock(&tasklist_lock);
 		}
@@ -134,7 +138,7 @@ int freeze_processes(void)
 	if (!pm_freezing)
 		atomic_inc(&system_freezing_cnt);
 
-	pm_wakeup_clear(0);
+	pm_wakeup_clear(true);
 	pr_info("Freezing user space processes ... ");
 	pm_freezing = true;
 	error = try_to_freeze_tasks(true);
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 4aa4d5d39..545958377 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -30,6 +30,7 @@
 #include <trace/events/power.h>
 #include <linux/compiler.h>
 #include <linux/moduleparam.h>
+#include <linux/wakeup_reason.h>
 
 #include "power.h"
 
@@ -138,6 +139,9 @@ static void s2idle_loop(void)
 			break;
 		}
 
+		pm_wakeup_clear(false);
+		clear_wakeup_reasons();
+
 		s2idle_enter();
 	}
 
@@ -357,6 +361,7 @@ static int suspend_prepare(suspend_state_t state)
 	if (!error)
 		return 0;
 
+	log_suspend_abort_reason("One or more tasks refusing to freeze");
 	suspend_stats.failed_freeze++;
 	dpm_save_failed_step(SUSPEND_FREEZE);
 	pm_notifier_call_chain(PM_POST_SUSPEND);
@@ -386,7 +391,7 @@ void __weak arch_suspend_enable_irqs(void)
  */
 static int suspend_enter(suspend_state_t state, bool *wakeup)
 {
-	int error;
+	int error, last_dev;
 
 	error = platform_suspend_prepare(state);
 	if (error)
@@ -394,7 +399,11 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
 
 	error = dpm_suspend_late(PMSG_SUSPEND);
 	if (error) {
+		last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
+		last_dev %= REC_FAILED_NUM;
 		pr_err("late suspend of devices failed\n");
+		log_suspend_abort_reason("late suspend of %s device failed",
+					 suspend_stats.failed_devs[last_dev]);
 		goto Platform_finish;
 	}
 	error = platform_suspend_prepare_late(state);
@@ -403,7 +412,11 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
 
 	error = dpm_suspend_noirq(PMSG_SUSPEND);
 	if (error) {
+		last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
+		last_dev %= REC_FAILED_NUM;
 		pr_err("noirq suspend of devices failed\n");
+		log_suspend_abort_reason("noirq suspend of %s device failed",
+					 suspend_stats.failed_devs[last_dev]);
 		goto Platform_early_resume;
 	}
 	error = platform_suspend_prepare_noirq(state);
@@ -419,8 +432,10 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
 	}
 
 	error = suspend_disable_secondary_cpus();
-	if (error || suspend_test(TEST_CPUS))
+	if (error || suspend_test(TEST_CPUS)) {
+		log_suspend_abort_reason("Disabling non-boot cpus failed");
 		goto Enable_cpus;
+	}
 
 	arch_suspend_disable_irqs();
 	BUG_ON(!irqs_disabled());
@@ -491,6 +506,8 @@ int suspend_devices_and_enter(suspend_state_t state)
 	error = dpm_suspend_start(PMSG_SUSPEND);
 	if (error) {
 		pr_err("Some devices failed to suspend, or early wake event detected\n");
+		log_suspend_abort_reason(
+				"Some devices failed to suspend, or early wake event detected");
 		goto Recover_platform;
 	}
 	suspend_test_finish("suspend devices");
diff --git a/kernel/reboot.c b/kernel/reboot.c
index af6f23d8b..240805041 100644
--- a/kernel/reboot.c
+++ b/kernel/reboot.c
@@ -32,7 +32,9 @@ EXPORT_SYMBOL(cad_pid);
 #define DEFAULT_REBOOT_MODE
 #endif
 enum reboot_mode reboot_mode DEFAULT_REBOOT_MODE;
+EXPORT_SYMBOL_GPL(reboot_mode);
 enum reboot_mode panic_reboot_mode = REBOOT_UNDEFINED;
+EXPORT_SYMBOL_GPL(panic_reboot_mode);
 
 /*
  * This variable is used privately to keep track of whether or not
@@ -215,6 +217,27 @@ void do_kernel_restart(char *cmd)
 	atomic_notifier_call_chain(&restart_handler_list, reboot_mode, cmd);
 }
 
+#ifdef CONFIG_NO_GKI
+static ATOMIC_NOTIFIER_HEAD(pre_restart_handler_list);
+
+int register_pre_restart_handler(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_register(&pre_restart_handler_list, nb);
+}
+EXPORT_SYMBOL(register_pre_restart_handler);
+
+int unregister_pre_restart_handler(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_unregister(&pre_restart_handler_list, nb);
+}
+EXPORT_SYMBOL(unregister_pre_restart_handler);
+
+void do_kernel_pre_restart(char *cmd)
+{
+	atomic_notifier_call_chain(&pre_restart_handler_list, reboot_mode, cmd);
+}
+#endif
+
 void migrate_to_reboot_cpu(void)
 {
 	/* The boot cpu is always logical cpu 0 */
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index e86758a81..88c49b643 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -18,6 +18,7 @@
 #include <linux/scs.h>
 #include <linux/irq.h>
 #include <linux/delay.h>
+#include <linux/wgcm.h>
 
 #ifdef CONFIG_QOS_CTRL
 #include <linux/sched/qos_ctrl.h>
@@ -34,6 +35,7 @@
 #include "smp.h"
 #include "walt.h"
 #include "rtg/rtg.h"
+#include <rtg/rtg_qos.h>
 
 /*
  * Export tracepoints that act as a bare tracehook (ie: have no trace event
@@ -50,6 +52,14 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(sched_overutilized_tp);
 EXPORT_TRACEPOINT_SYMBOL_GPL(sched_util_est_cfs_tp);
 EXPORT_TRACEPOINT_SYMBOL_GPL(sched_util_est_se_tp);
 EXPORT_TRACEPOINT_SYMBOL_GPL(sched_update_nr_running_tp);
+EXPORT_TRACEPOINT_SYMBOL_GPL(sched_switch);
+EXPORT_TRACEPOINT_SYMBOL_GPL(sched_waking);
+#ifdef CONFIG_SCHEDSTATS
+EXPORT_TRACEPOINT_SYMBOL_GPL(sched_stat_sleep);
+EXPORT_TRACEPOINT_SYMBOL_GPL(sched_stat_wait);
+EXPORT_TRACEPOINT_SYMBOL_GPL(sched_stat_iowait);
+EXPORT_TRACEPOINT_SYMBOL_GPL(sched_stat_blocked);
+#endif
 
 DEFINE_PER_CPU_SHARED_ALIGNED(struct rq, runqueues);
 
@@ -663,7 +673,7 @@ int get_nohz_timer_target(void)
 	int i, cpu = smp_processor_id(), default_cpu = -1;
 	struct sched_domain *sd;
 
-	if (housekeeping_cpu(cpu, HK_FLAG_TIMER)) {
+	if (housekeeping_cpu(cpu, HK_FLAG_TIMER) && cpu_active(cpu)) {
 		if (!idle_cpu(cpu))
 			return cpu;
 		default_cpu = cpu;
@@ -683,8 +693,20 @@ int get_nohz_timer_target(void)
 		}
 	}
 
-	if (default_cpu == -1)
-		default_cpu = housekeeping_any_cpu(HK_FLAG_TIMER);
+	if (default_cpu == -1) {
+		for_each_cpu_and(i, cpu_active_mask,
+				 housekeeping_cpumask(HK_FLAG_TIMER)) {
+			if (cpu == i)
+				continue;
+			if (!idle_cpu(i)) {
+				cpu = i;
+				goto unlock;
+			}
+		}
+		default_cpu = cpumask_any(cpu_active_mask);
+		if (unlikely(default_cpu >= nr_cpu_ids))
+			goto unlock;
+	}
 	cpu = default_cpu;
 unlock:
 	rcu_read_unlock();
@@ -1654,11 +1676,15 @@ void activate_task(struct rq *rq, struct task_struct *p, int flags)
 {
 	enqueue_task(rq, p, flags);
 
+	wgcm_activate_task(p);
+
 	p->on_rq = TASK_ON_RQ_QUEUED;
 }
 
 void deactivate_task(struct rq *rq, struct task_struct *p, int flags)
 {
+	wgcm_deactivate_task(p, flags);
+
 	p->on_rq = (flags & DEQUEUE_SLEEP) ? 0 : TASK_ON_RQ_MIGRATING;
 
 	dequeue_task(rq, p, flags);
@@ -1770,7 +1796,9 @@ static inline bool is_cpu_allowed(struct task_struct *p, int cpu)
 	if (is_per_cpu_kthread(p))
 		return cpu_online(cpu);
 
-	return cpu_active(cpu);
+	if (!cpu_active(cpu))
+		return false;
+	return cpumask_test_cpu(cpu, task_cpu_possible_mask(p));
 }
 
 /*
@@ -2433,7 +2461,7 @@ static int select_fallback_rq(int cpu, struct task_struct *p)
 			}
 			fallthrough;
 		case possible:
-			do_set_cpus_allowed(p, cpu_possible_mask);
+			do_set_cpus_allowed(p, task_cpu_possible_mask(p));
 			state = fail;
 			break;
 
@@ -2627,6 +2655,8 @@ ttwu_do_activate(struct rq *rq, struct task_struct *p, int wake_flags,
 {
 	int en_flags = ENQUEUE_WAKEUP | ENQUEUE_NOCLOCK;
 
+	if (wake_flags & WF_SYNC)
+		en_flags |= ENQUEUE_WAKEUP_SYNC;
 	lockdep_assert_held(&rq->lock);
 
 	if (p->sched_contributes_to_load)
@@ -3023,6 +3053,19 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
 	if (!(p->state & state))
 		goto unlock;
 
+#ifdef CONFIG_FREEZER
+	/*
+	 * If we're going to wake up a thread which may be frozen, then
+	 * we can only do so if we have an active CPU which is capable of
+	 * running it. This may not be the case when resuming from suspend,
+	 * as the secondary CPUs may not yet be back online. See __thaw_task()
+	 * for the actual wakeup.
+	 */
+	if (unlikely(frozen_or_skipped(p)) &&
+	    !cpumask_intersects(cpu_active_mask, task_cpu_possible_mask(p)))
+		goto unlock;
+#endif
+
 	trace_sched_waking(p);
 
 	/* We're going to change ->state: */
@@ -3269,6 +3312,8 @@ static void __sched_fork(unsigned long clone_flags, struct task_struct *p)
 #ifdef CONFIG_SCHED_RTG
 	p->rtg_depth = 0;
 #endif
+
+	wgcm_clear_child(p);
 }
 
 DEFINE_STATIC_KEY_FALSE(sched_numa_balancing);
@@ -5720,16 +5765,19 @@ int sched_setscheduler(struct task_struct *p, int policy,
 {
 	return _sched_setscheduler(p, policy, param, true);
 }
+EXPORT_SYMBOL_GPL(sched_setscheduler);
 
 int sched_setattr(struct task_struct *p, const struct sched_attr *attr)
 {
 	return __sched_setscheduler(p, attr, true, true);
 }
+EXPORT_SYMBOL_GPL(sched_setattr);
 
 int sched_setattr_nocheck(struct task_struct *p, const struct sched_attr *attr)
 {
 	return __sched_setscheduler(p, attr, false, true);
 }
+EXPORT_SYMBOL_GPL(sched_setattr_nocheck);
 
 /**
  * sched_setscheduler_nocheck - change the scheduling policy and/or RT priority of a thread from kernelspace.
@@ -5749,6 +5797,7 @@ int sched_setscheduler_nocheck(struct task_struct *p, int policy,
 {
 	return _sched_setscheduler(p, policy, param, false);
 }
+EXPORT_SYMBOL_GPL(sched_setscheduler_nocheck);
 
 /*
  * SCHED_FIFO is a broken scheduler model; that is, it is fundamentally
@@ -7056,6 +7105,9 @@ void migrate_tasks(struct rq *dead_rq, struct rq_flags *rf,
 	 */
 	update_rq_clock(rq);
 
+#ifdef CONFIG_SCHED_DEBUG
+	orf.clock_update_flags |= RQCF_UPDATED;
+#endif
 	for (;;) {
 		/*
 		 * There's this thread running, bail when that's the only
@@ -9191,6 +9243,10 @@ void sched_exit(struct task_struct *p)
 	sched_set_group_id(p, 0);
 #endif
 
+#ifdef CONFIG_SCHED_RTG_QOS
+	sched_exit_qos_list(p);
+#endif
+
 	rq = task_rq_lock(p, &rf);
 
 	/* rq->curr == p */
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 0a94f01a1..932f44711 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -86,6 +86,7 @@ enum sched_tunable_scaling sysctl_sched_tunable_scaling = SCHED_TUNABLESCALING_L
  * (default: 0.75 msec * (1 + ilog(ncpus)), units: nanoseconds)
  */
 unsigned int sysctl_sched_min_granularity			= 750000ULL;
+EXPORT_SYMBOL_GPL(sysctl_sched_min_granularity);
 static unsigned int normalized_sysctl_sched_min_granularity	= 750000ULL;
 
 /*
@@ -3406,6 +3407,7 @@ void set_task_rq_fair(struct sched_entity *se,
 	se->avg.last_update_time = n_last_update_time;
 }
 
+
 /*
  * When on migration a sched_entity joins/leaves the PELT hierarchy, we need to
  * propagate its contribution. The key to this propagation is the invariant
@@ -3473,6 +3475,7 @@ void set_task_rq_fair(struct sched_entity *se,
  * XXX: only do this for the part of runnable > running ?
  *
  */
+
 static inline void
 update_tg_cfs_util(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cfs_rq *gcfs_rq)
 {
@@ -3701,19 +3704,7 @@ update_cfs_rq_load_avg(u64 now, struct cfs_rq *cfs_rq)
 
 		r = removed_util;
 		sub_positive(&sa->util_avg, r);
-		sub_positive(&sa->util_sum, r * divider);
-		/*
-		 * Because of rounding, se->util_sum might ends up being +1 more than
-		 * cfs->util_sum. Although this is not a problem by itself, detaching
-		 * a lot of tasks with the rounding problem between 2 updates of
-		 * util_avg (~1ms) can make cfs->util_sum becoming null whereas
-		 * cfs_util_avg is not.
-		 * Check that util_sum is still above its lower bound for the new
-		 * util_avg. Given that period_contrib might have moved since the last
-		 * sync, we are only sure that util_sum must be above or equal to
-		 *    util_avg * minimum possible divider
-		 */
-		sa->util_sum = max_t(u32, sa->util_sum, sa->util_avg * PELT_MIN_DIVIDER);
+		sa->util_sum = sa->util_avg * divider;
 
 		r = removed_runnable;
 		sub_positive(&sa->runnable_avg, r);
@@ -5361,7 +5352,7 @@ static void do_sched_cfs_slack_timer(struct cfs_bandwidth *cfs_b)
 /*
  * When a group wakes up we want to make sure that its quota is not already
  * expired/exceeded, otherwise it may be allowed to steal additional ticks of
- * runtime as update_curr() throttling can not trigger until it's on-rq.
+ * runtime as update_curr() throttling can not not trigger until it's on-rq.
  */
 static void check_enqueue_throttle(struct cfs_rq *cfs_rq)
 {
@@ -10821,8 +10812,20 @@ void nohz_balance_enter_idle(int cpu)
 	SCHED_WARN_ON(cpu != smp_processor_id());
 
 	/* If this CPU is going down, then nothing needs to be done: */
-	if (!cpu_active(cpu))
+	if (!cpu_active(cpu)) {
+		/*
+		 * A CPU can be paused while it is idle with it's tick
+		 * stopped. nohz_balance_exit_idle() should be called
+		 * from the local CPU, so it can't be called during
+		 * pause. This results in paused CPU participating in
+		 * the nohz idle balance, which should be avoided.
+		 *
+		 * When the paused CPU exits idle and enters again,
+		 * exempt the paused CPU from nohz_balance_exit_idle.
+		 */
+		nohz_balance_exit_idle(rq);
 		return;
+	}
 
 	/* Spare idle load balancing on CPUs that don't want to be disturbed: */
 	if (!housekeeping_cpu(cpu, HK_FLAG_SCHED))
diff --git a/kernel/sched/loadavg.c b/kernel/sched/loadavg.c
index d2a655643..b5837e277 100644
--- a/kernel/sched/loadavg.c
+++ b/kernel/sched/loadavg.c
@@ -75,6 +75,7 @@ void get_avenrun(unsigned long *loads, unsigned long offset, int shift)
 	loads[1] = (avenrun[1] + offset) << shift;
 	loads[2] = (avenrun[2] + offset) << shift;
 }
+EXPORT_SYMBOL_GPL(get_avenrun);
 
 long calc_load_fold_active(struct rq *this_rq, long adjust)
 {
diff --git a/kernel/sched/pelt.c b/kernel/sched/pelt.c
index 2c613e1cf..134aa5ee2 100644
--- a/kernel/sched/pelt.c
+++ b/kernel/sched/pelt.c
@@ -28,6 +28,57 @@
 #include "sched.h"
 #include "pelt.h"
 
+int pelt_load_avg_period = PELT32_LOAD_AVG_PERIOD;
+int pelt_load_avg_max = PELT32_LOAD_AVG_MAX;
+const u32 *pelt_runnable_avg_yN_inv = pelt32_runnable_avg_yN_inv;
+int get_pelt_halflife(void)
+{
+	return pelt_load_avg_period;
+}
+EXPORT_SYMBOL_GPL(get_pelt_halflife);
+static int __set_pelt_halflife(void *data)
+{
+	int rc = 0;
+	int num = *(int *)data;
+	switch (num) {
+	case PELT8_LOAD_AVG_PERIOD:
+		pelt_load_avg_period = PELT8_LOAD_AVG_PERIOD;
+		pelt_load_avg_max = PELT8_LOAD_AVG_MAX;
+		pelt_runnable_avg_yN_inv = pelt8_runnable_avg_yN_inv;
+		pr_info("PELT half life is set to %dms\n", num);
+		break;
+	case PELT32_LOAD_AVG_PERIOD:
+		pelt_load_avg_period = PELT32_LOAD_AVG_PERIOD;
+		pelt_load_avg_max = PELT32_LOAD_AVG_MAX;
+		pelt_runnable_avg_yN_inv = pelt32_runnable_avg_yN_inv;
+		pr_info("PELT half life is set to %dms\n", num);
+		break;
+	default:
+		rc = -EINVAL;
+		pr_err("Failed to set PELT half life to %dms, the current value is %dms\n",
+			num, pelt_load_avg_period);
+	}
+	return rc;
+}
+int set_pelt_halflife(int num)
+{
+	return stop_machine(__set_pelt_halflife, &num, NULL);
+}
+EXPORT_SYMBOL_GPL(set_pelt_halflife);
+static int __init set_pelt(char *str)
+{
+	int rc, num;
+	rc = kstrtoint(str, 0, &num);
+	if (rc) {
+		pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc);
+		return 0;
+	}
+	__set_pelt_halflife(&num);
+	return rc;
+}
+
+early_param("pelt", set_pelt);
+
 /*
  * Approximate:
  *   val * y^n,    where y^32 ~= 0.5 (~1 scheduling period)
@@ -54,7 +105,7 @@ static u64 decay_load(u64 val, u64 n)
 		local_n %= LOAD_AVG_PERIOD;
 	}
 
-	val = mul_u64_u32_shr(val, runnable_avg_yN_inv[local_n], 32);
+	val = mul_u64_u32_shr(val, pelt_runnable_avg_yN_inv[local_n], 32);
 	return val;
 }
 
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index 886ec0712..98c9fe373 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -1393,6 +1393,27 @@ static void dequeue_rt_entity(struct sched_rt_entity *rt_se, unsigned int flags)
 	enqueue_top_rt_rq(&rq->rt);
 }
 
+#ifdef CONFIG_SMP
+static inline bool should_honor_rt_sync(struct rq *rq, struct task_struct *p,
+					bool sync)
+{
+	/*
+	 * If the waker is CFS, then an RT sync wakeup would preempt the waker
+	 * and force it to run for a likely small time after the RT wakee is
+	 * done. So, only honor RT sync wakeups from RT wakers.
+	 */
+	return sync && task_has_rt_policy(rq->curr) &&
+		p->prio <= rq->rt.highest_prio.next &&
+		rq->rt.rt_nr_running <= 2;
+}
+#else
+static inline bool should_honor_rt_sync(struct rq *rq, struct task_struct *p,
+					bool sync)
+{
+	return 0;
+}
+#endif
+
 /*
  * Adding/removing a task to/from a priority array:
  */
@@ -1400,6 +1421,7 @@ static void
 enqueue_task_rt(struct rq *rq, struct task_struct *p, int flags)
 {
 	struct sched_rt_entity *rt_se = &p->rt;
+	bool sync = !!(flags & ENQUEUE_WAKEUP_SYNC);
 
 	if (flags & ENQUEUE_WAKEUP)
 		rt_se->timeout = 0;
@@ -1407,7 +1429,8 @@ enqueue_task_rt(struct rq *rq, struct task_struct *p, int flags)
 	enqueue_rt_entity(rt_se, flags);
 	walt_inc_cumulative_runnable_avg(rq, p);
 
-	if (!task_current(rq, p) && p->nr_cpus_allowed > 1)
+	if (!task_current(rq, p) && p->nr_cpus_allowed > 1 &&
+	    !should_honor_rt_sync(rq, p, sync))
 		enqueue_pushable_task(rq, p);
 }
 
@@ -1464,7 +1487,12 @@ select_task_rq_rt(struct task_struct *p, int cpu, int sd_flag, int flags)
 {
 	struct task_struct *curr;
 	struct rq *rq;
+	struct rq *this_cpu_rq;
 	bool test;
+	int target_cpu = -1;
+	bool may_not_preempt;
+	bool sync = !!(flags & WF_SYNC);
+	int this_cpu;
 
 	/* For anything but wake ups, just return the task_cpu */
 	if (sd_flag != SD_BALANCE_WAKE && sd_flag != SD_BALANCE_FORK)
@@ -1474,6 +1502,8 @@ select_task_rq_rt(struct task_struct *p, int cpu, int sd_flag, int flags)
 
 	rcu_read_lock();
 	curr = READ_ONCE(rq->curr); /* unlocked access */
+	this_cpu = smp_processor_id();
+	this_cpu_rq = cpu_rq(this_cpu);
 
 	/*
 	 * If the current task on @p's runqueue is an RT task, then
@@ -1508,6 +1538,14 @@ select_task_rq_rt(struct task_struct *p, int cpu, int sd_flag, int flags)
 	test |= sysctl_sched_enable_rt_cas;
 #endif
 
+	/*
+	 * Respect the sync flag as long as the task can run on this CPU.
+	 */
+	if (should_honor_rt_sync(this_cpu_rq, p, sync) &&
+	    cpumask_test_cpu(this_cpu, p->cpus_ptr)) {
+		cpu = this_cpu;
+		goto out_unlock;
+	}
 	if (test || !rt_task_fits_capacity(p, cpu)) {
 		int target = find_lowest_rq(p);
 
diff --git a/kernel/sched/sched-pelt.h b/kernel/sched/sched-pelt.h
index c529706be..92a6875bc 100644
--- a/kernel/sched/sched-pelt.h
+++ b/kernel/sched/sched-pelt.h
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 /* Generated by Documentation/scheduler/sched-pelt; do not modify. */
 
-static const u32 runnable_avg_yN_inv[] __maybe_unused = {
+static const u32 pelt32_runnable_avg_yN_inv[] __maybe_unused = {
 	0xffffffff, 0xfa83b2da, 0xf5257d14, 0xefe4b99a, 0xeac0c6e6, 0xe5b906e6,
 	0xe0ccdeeb, 0xdbfbb796, 0xd744fcc9, 0xd2a81d91, 0xce248c14, 0xc9b9bd85,
 	0xc5672a10, 0xc12c4cc9, 0xbd08a39e, 0xb8fbaf46, 0xb504f333, 0xb123f581,
@@ -10,5 +10,20 @@ static const u32 runnable_avg_yN_inv[] __maybe_unused = {
 	0x85aac367, 0x82cd8698,
 };
 
-#define LOAD_AVG_PERIOD 32
-#define LOAD_AVG_MAX 47742
+#define PELT32_LOAD_AVG_PERIOD 32
+#define PELT32_LOAD_AVG_MAX 47742
+
+static const u32 pelt8_runnable_avg_yN_inv[] __maybe_unused = {
+	0xffffffff, 0xeac0c6e6, 0xd744fcc9, 0xc5672a10,
+	0xb504f333, 0xa5fed6a9, 0x9837f050, 0x8b95c1e3,
+};
+
+#define PELT8_LOAD_AVG_PERIOD 8
+#define PELT8_LOAD_AVG_MAX 12336
+
+extern const u32 *pelt_runnable_avg_yN_inv;
+extern int pelt_load_avg_period;
+extern int pelt_load_avg_max;
+
+#define LOAD_AVG_PERIOD pelt_load_avg_period
+#define LOAD_AVG_MAX pelt_load_avg_max
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 3457a8ac7..5d526e366 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -1911,6 +1911,7 @@ extern const int		sched_latency_to_weight[40];
 #else
 #define ENQUEUE_MIGRATED	0x00
 #endif
+#define ENQUEUE_WAKEUP_SYNC	0x80
 
 #define RETRY_TASK		((void *)-1UL)
 
diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c
index 9191e5daa..58d840c62 100644
--- a/kernel/sched/topology.c
+++ b/kernel/sched/topology.c
@@ -5,6 +5,9 @@
 #include "sched.h"
 
 DEFINE_MUTEX(sched_domains_mutex);
+#ifdef CONFIG_LOCKDEP
+EXPORT_SYMBOL_GPL(sched_domains_mutex);
+#endif
 
 /* Protected by sched_domains_mutex: */
 static cpumask_var_t sched_domains_tmpmask;
diff --git a/kernel/sched/wait.c b/kernel/sched/wait.c
index a55642aa3..6911bbca0 100644
--- a/kernel/sched/wait.c
+++ b/kernel/sched/wait.c
@@ -396,7 +396,8 @@ void finish_wait(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_en
 }
 EXPORT_SYMBOL(finish_wait);
 
-int autoremove_wake_function(struct wait_queue_entry *wq_entry, unsigned mode, int sync, void *key)
+__sched int autoremove_wake_function(struct wait_queue_entry *wq_entry, unsigned int mode,
+				     int sync, void *key)
 {
 	int ret = default_wake_function(wq_entry, mode, sync, key);
 
@@ -432,7 +433,7 @@ static inline bool is_kthread_should_stop(void)
  * }						smp_mb(); // C
  * remove_wait_queue(&wq_head, &wait);		wq_entry->flags |= WQ_FLAG_WOKEN;
  */
-long wait_woken(struct wait_queue_entry *wq_entry, unsigned mode, long timeout)
+__sched long wait_woken(struct wait_queue_entry *wq_entry, unsigned int mode, long timeout)
 {
 	/*
 	 * The below executes an smp_mb(), which matches with the full barrier
@@ -457,7 +458,8 @@ long wait_woken(struct wait_queue_entry *wq_entry, unsigned mode, long timeout)
 }
 EXPORT_SYMBOL(wait_woken);
 
-int woken_wake_function(struct wait_queue_entry *wq_entry, unsigned mode, int sync, void *key)
+__sched int woken_wake_function(struct wait_queue_entry *wq_entry, unsigned int mode,
+				int sync, void *key)
 {
 	/* Pairs with the smp_store_mb() in wait_woken(). */
 	smp_mb(); /* C */
diff --git a/mm/memblock.c b/mm/memblock.c
index f72d53957..16331c252 100644
--- a/mm/memblock.c
+++ b/mm/memblock.c
@@ -97,6 +97,26 @@ struct pglist_data __refdata contig_page_data;
 EXPORT_SYMBOL(contig_page_data);
 #endif
 
+#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT
+static unsigned long defer_start __initdata;
+static unsigned long defer_end __initdata;
+
+#define DEFAULT_DEFER_FREE_BLOCK_SIZE SZ_256M
+static unsigned long defer_free_block_size __initdata =
+	DEFAULT_DEFER_FREE_BLOCK_SIZE;
+
+static int __init early_defer_free_block_size(char *p)
+{
+	defer_free_block_size = memparse(p, &p);
+
+	pr_debug("defer_free_block_size = 0x%lx\n", defer_free_block_size);
+
+	return 0;
+}
+
+early_param("defer_free_block_size", early_defer_free_block_size);
+#endif
+
 unsigned long max_low_pfn;
 unsigned long min_low_pfn;
 unsigned long max_pfn;
@@ -814,6 +834,9 @@ int __init_memblock memblock_free(phys_addr_t base, phys_addr_t size)
 	kmemleak_free_part_phys(base, size);
 	return memblock_remove_range(&memblock.reserved, base, size);
 }
+#ifdef CONFIG_ARCH_KEEP_MEMBLOCK
+EXPORT_SYMBOL_GPL(memblock_free);
+#endif
 
 int __init_memblock memblock_reserve(phys_addr_t base, phys_addr_t size)
 {
@@ -1391,6 +1414,9 @@ phys_addr_t __init memblock_phys_alloc_range(phys_addr_t size,
 					     phys_addr_t start,
 					     phys_addr_t end)
 {
+	memblock_dbg("%s: %llu bytes align=0x%llx from=%pa max_addr=%pa %pS\n",
+		     __func__, (u64)size, (u64)align, &start, &end,
+		     (void *)_RET_IP_);
 	return memblock_alloc_range_nid(size, align, start, end, NUMA_NO_NODE,
 					false);
 }
@@ -1628,6 +1654,7 @@ phys_addr_t __init_memblock memblock_end_of_DRAM(void)
 
 	return (memblock.memory.regions[idx].base + memblock.memory.regions[idx].size);
 }
+EXPORT_SYMBOL_GPL(memblock_end_of_DRAM);
 
 static phys_addr_t __init_memblock __find_max_addr(phys_addr_t limit)
 {
@@ -1913,6 +1940,28 @@ static void __init __free_pages_memory(unsigned long start, unsigned long end)
 	}
 }
 
+#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT
+int __init defer_free_memblock(void *unused)
+{
+	if (defer_start == 0)
+		return 0;
+
+	pr_debug("start = %ld, end = %ld\n", defer_start, defer_end);
+
+	__free_pages_memory(defer_start, defer_end);
+
+	totalram_pages_add(defer_end - defer_start);
+
+	pr_info("%s: size %luM free %luM [%luM - %luM] total %luM\n", __func__,
+		defer_free_block_size >> 20,
+		(defer_end - defer_start) >> (20 - PAGE_SHIFT),
+		defer_end >> (20 - PAGE_SHIFT),
+		defer_start >> (20 - PAGE_SHIFT),
+		totalram_pages() >> (20 - PAGE_SHIFT));
+	return 0;
+}
+#endif
+
 static unsigned long __init __free_memory_core(phys_addr_t start,
 				 phys_addr_t end)
 {
@@ -1923,6 +1972,15 @@ static unsigned long __init __free_memory_core(phys_addr_t start,
 	if (start_pfn >= end_pfn)
 		return 0;
 
+#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT
+	if ((end - start) > defer_free_block_size) {
+		defer_start = start_pfn;
+		defer_end = end_pfn;
+
+		return 0;
+	}
+#endif
+
 	__free_pages_memory(start_pfn, end_pfn);
 
 	return end_pfn - start_pfn;
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index d2c678520..c3653b067 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -161,11 +161,7 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
 		break;
 	}
 
-	/* Use L2CAP_MODE_LE_FLOWCTL (CoC) in case of LE address and
-	 * L2CAP_MODE_EXT_FLOWCTL (ECRED) has not been set.
-	 */
-	if (chan->psm && bdaddr_type_is_le(chan->src_type) &&
-	    chan->mode != L2CAP_MODE_EXT_FLOWCTL)
+	if (chan->psm && bdaddr_type_is_le(chan->src_type))
 		chan->mode = L2CAP_MODE_LE_FLOWCTL;
 
 	chan->state = BT_BOUND;
@@ -259,11 +255,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
 			return -EINVAL;
 	}
 
-	/* Use L2CAP_MODE_LE_FLOWCTL (CoC) in case of LE address and
-	 * L2CAP_MODE_EXT_FLOWCTL (ECRED) has not been set.
-	 */
-	if (chan->psm && bdaddr_type_is_le(chan->src_type) &&
-	    chan->mode != L2CAP_MODE_EXT_FLOWCTL)
+	if (chan->psm && bdaddr_type_is_le(chan->src_type) && !chan->mode)
 		chan->mode = L2CAP_MODE_LE_FLOWCTL;
 
 	l2cap_sock_init_pid(sk);
@@ -903,8 +895,6 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
 	struct l2cap_conn *conn;
 	int len, err = 0;
 	u32 opt;
-	u16 mtu;
-	u8 mode;
 
 	BT_DBG("sk %p", sk);
 
@@ -1087,16 +1077,16 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
 			break;
 		}
 
-		if (copy_from_sockptr(&mtu, optval, sizeof(u16))) {
+		if (copy_from_sockptr(&opt, optval, sizeof(u16))) {
 			err = -EFAULT;
 			break;
 		}
 
 		if (chan->mode == L2CAP_MODE_EXT_FLOWCTL &&
 		    sk->sk_state == BT_CONNECTED)
-			err = l2cap_chan_reconfigure(chan, mtu);
+			err = l2cap_chan_reconfigure(chan, opt);
 		else
-			chan->imtu = mtu;
+			chan->imtu = opt;
 
 		break;
 
@@ -1118,14 +1108,14 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
 			break;
 		}
 
-		if (copy_from_sockptr(&mode, optval, sizeof(u8))) {
+		if (copy_from_sockptr(&opt, optval, sizeof(u8))) {
 			err = -EFAULT;
 			break;
 		}
 
-		BT_DBG("mode %u", mode);
+		BT_DBG("opt %u", opt);
 
-		err = l2cap_set_mode(chan, mode);
+		err = l2cap_set_mode(chan, opt);
 		if (err)
 			break;
 
@@ -1537,9 +1527,6 @@ static void l2cap_sock_close_cb(struct l2cap_chan *chan)
 {
 	struct sock *sk = chan->data;
 
-	if (!sk)
-		return;
-
 	l2cap_sock_kill(sk);
 }
 
@@ -1548,9 +1535,6 @@ static void l2cap_sock_teardown_cb(struct l2cap_chan *chan, int err)
 	struct sock *sk = chan->data;
 	struct sock *parent;
 
-	if (!sk)
-		return;
-
 	BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
 
 	/* This callback can be called both for server (BT_LISTEN)
@@ -1742,10 +1726,8 @@ static void l2cap_sock_destruct(struct sock *sk)
 {
 	BT_DBG("sk %p", sk);
 
-	if (l2cap_pi(sk)->chan) {
-		l2cap_pi(sk)->chan->data = NULL;
+	if (l2cap_pi(sk)->chan)
 		l2cap_chan_put(l2cap_pi(sk)->chan);
-	}
 
 	if (l2cap_pi(sk)->rx_busy_skb) {
 		kfree_skb(l2cap_pi(sk)->rx_busy_skb);
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 081d1ee3d..6a8796933 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -93,10 +93,10 @@ static void sco_sock_timeout(struct work_struct *work)
 
 	BT_DBG("sock %p state %d", sk, sk->sk_state);
 
-	lock_sock(sk);
+	bh_lock_sock(sk);
 	sk->sk_err = ETIMEDOUT;
 	sk->sk_state_change(sk);
-	release_sock(sk);
+	bh_unlock_sock(sk);
 
 	sock_put(sk);
 }
@@ -193,10 +193,10 @@ static void sco_conn_del(struct hci_conn *hcon, int err)
 
 	if (sk) {
 		sock_hold(sk);
-		lock_sock(sk);
+		bh_lock_sock(sk);
 		sco_sock_clear_timer(sk);
 		sco_chan_del(sk, err);
-		release_sock(sk);
+		bh_unlock_sock(sk);
 		sock_put(sk);
 	}
 
@@ -280,10 +280,11 @@ static int sco_connect(struct hci_dev *hdev, struct sock *sk)
 	return err;
 }
 
-static int sco_send_frame(struct sock *sk, struct sk_buff *skb)
+static int sco_send_frame(struct sock *sk, struct msghdr *msg, int len)
 {
 	struct sco_conn *conn = sco_pi(sk)->conn;
-	int len = skb->len;
+	struct sk_buff *skb;
+	int err;
 
 	/* Check outgoing MTU */
 	if (len > conn->mtu)
@@ -291,6 +292,14 @@ static int sco_send_frame(struct sock *sk, struct sk_buff *skb)
 
 	BT_DBG("sk %p len %d", sk, len);
 
+	skb = bt_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err);
+	if (!skb)
+		return err;
+
+	if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
+		kfree_skb(skb);
+		return -EFAULT;
+	}
 	hci_send_sco(conn->hcon, skb);
 
 	return len;
@@ -568,24 +577,19 @@ static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen
 	    addr->sa_family != AF_BLUETOOTH)
 		return -EINVAL;
 
-	lock_sock(sk);
-	if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) {
-		err = -EBADFD;
-		goto done;
-	}
+	if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND)
+		return -EBADFD;
 
-	if (sk->sk_type != SOCK_SEQPACKET) {
-		err = -EINVAL;
-		goto done;
-	}
+	if (sk->sk_type != SOCK_SEQPACKET)
+		return -EINVAL;
 
 	hdev = hci_get_route(&sa->sco_bdaddr, &sco_pi(sk)->src, BDADDR_BREDR);
-	if (!hdev) {
-		err = -EHOSTUNREACH;
-		goto done;
-	}
+	if (!hdev)
+		return -EHOSTUNREACH;
 	hci_dev_lock(hdev);
 
+	lock_sock(sk);
+
 	/* Set destination address and psm */
 	bacpy(&sco_pi(sk)->dst, &sa->sco_bdaddr);
 
@@ -720,7 +724,6 @@ static int sco_sock_sendmsg(struct socket *sock, struct msghdr *msg,
 			    size_t len)
 {
 	struct sock *sk = sock->sk;
-	struct sk_buff *skb;
 	int err;
 
 	BT_DBG("sock %p, sk %p", sock, sk);
@@ -732,21 +735,16 @@ static int sco_sock_sendmsg(struct socket *sock, struct msghdr *msg,
 	if (msg->msg_flags & MSG_OOB)
 		return -EOPNOTSUPP;
 
-	skb = bt_skb_sendmsg(sk, msg, len, len, 0, 0);
-	if (IS_ERR(skb))
-		return PTR_ERR(skb);
+
 
 	lock_sock(sk);
 
 	if (sk->sk_state == BT_CONNECTED)
-		err = sco_send_frame(sk, skb);
+		err = sco_send_frame(sk, msg, len);
 	else
 		err = -ENOTCONN;
 
 	release_sock(sk);
-
-	if (err < 0)
-		kfree_skb(skb);
 	return err;
 }
 
@@ -1103,10 +1101,10 @@ static void sco_conn_ready(struct sco_conn *conn)
 
 	if (sk) {
 		sco_sock_clear_timer(sk);
-		lock_sock(sk);
+		bh_lock_sock(sk);
 		sk->sk_state = BT_CONNECTED;
 		sk->sk_state_change(sk);
-		release_sock(sk);
+		bh_unlock_sock(sk);
 	} else {
 		sco_conn_lock(conn);
 
@@ -1121,12 +1119,12 @@ static void sco_conn_ready(struct sco_conn *conn)
 			return;
 		}
 
-		lock_sock(parent);
+		bh_lock_sock(parent);
 
 		sk = sco_sock_alloc(sock_net(parent), NULL,
 				    BTPROTO_SCO, GFP_ATOMIC, 0);
 		if (!sk) {
-			release_sock(parent);
+			bh_unlock_sock(parent);
 			sco_conn_unlock(conn);
 			return;
 		}
@@ -1147,7 +1145,7 @@ static void sco_conn_ready(struct sco_conn *conn)
 		/* Wake up parent */
 		parent->sk_data_ready(parent);
 
-		release_sock(parent);
+		bh_unlock_sock(parent);
 
 		sco_conn_unlock(conn);
 	}
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 53aa3e18c..13ad74747 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -378,7 +378,11 @@ quiet_cmd_lzo = LZO     $@
       cmd_lzo = { cat $(real-prereqs) | $(KLZOP) -9; $(size_append); } > $@
 
 quiet_cmd_lz4 = LZ4     $@
-      cmd_lz4 = { cat $(real-prereqs) | $(LZ4) -l -c1 stdin stdout; \
+      cmd_lz4 = { cat $(real-prereqs) | lz4 -l --favor-decSpeed stdin stdout; \
+                  $(size_append); } > $@
+
+quiet_cmd_lz4c = LZ4C     $@
+      cmd_lz4c = { cat $(real-prereqs) | lz4 -12 --favor-decSpeed stdin stdout; \
                   $(size_append); } > $@
 
 # U-Boot mkimage
diff --git a/scripts/kconfig/parser.y b/scripts/kconfig/parser.y
index 190f1117f..421bb2fa6 100644
--- a/scripts/kconfig/parser.y
+++ b/scripts/kconfig/parser.y
@@ -10,6 +10,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stdbool.h>
+#include <unistd.h>
 
 #include "lkc.h"
 
@@ -20,11 +21,18 @@
 
 int cdebug = PRINTD;
 
+static const char *kconfig_white_list[] = {
+	"vendor/Kconfig",
+	"net/newip/Kconfig",
+	"net/newip/hooks/Kconfig",
+};
+
 static void yyerror(const char *err);
 static void zconfprint(const char *err, ...);
 static void zconf_error(const char *err, ...);
 static bool zconf_endtoken(const char *tokenname,
 			   const char *expected_tokenname);
+static bool zconf_in_whitelist(const char *path);
 
 struct symbol *symbol_hash[SYMBOL_HASHSIZE];
 
@@ -367,7 +375,9 @@ menu_option_list:
 source_stmt: T_SOURCE T_WORD_QUOTE T_EOL
 {
 	printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), $2);
-	zconf_nextfile($2);
+	if (access(($2), F_OK) == 0 || zconf_in_whitelist($2) == false) {
+		zconf_nextfile($2);
+	}
 	free($2);
 };
 
@@ -484,6 +494,16 @@ assign_val:
 
 %%
 
+static bool zconf_in_whitelist(const char *path)
+{
+	int i;
+	for (i = 0; i < sizeof(kconfig_white_list) / sizeof(kconfig_white_list[0]); i++) {
+		if(strcmp(kconfig_white_list[i], path) == 0)
+			return true;
+	}
+	return false;
+}
+
 void conf_parse(const char *name)
 {
 	struct symbol *sym;
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 662f7b4a9..a4e66d3b1 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -101,7 +101,7 @@
 #include "ibpkey.h"
 #include "xfrm.h"
 #include "netlabel.h"
-#include "audit.h"
+#include "include/audit.h"
 #include "avc_ss.h"
 
 struct selinux_state selinux_state;
diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h
index f2eeb3626..96f121d33 100644
--- a/security/selinux/ss/avtab.h
+++ b/security/selinux/ss/avtab.h
@@ -21,7 +21,7 @@
 #ifndef _SS_AVTAB_H_
 #define _SS_AVTAB_H_
 
-#include "security.h"
+#include "../include/security.h"
 
 struct avtab_key {
 	u16 source_type;	/* source type */
diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h
index 62990aa1e..9ec2bb0a4 100644
--- a/security/selinux/ss/context.h
+++ b/security/selinux/ss/context.h
@@ -18,7 +18,7 @@
 
 #include "ebitmap.h"
 #include "mls_types.h"
-#include "security.h"
+#include "../include/security.h"
 
 /*
  * A security context consists of an authenticated user
diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig
index d610b553e..2925971bc 100644
--- a/sound/soc/rockchip/Kconfig
+++ b/sound/soc/rockchip/Kconfig
@@ -16,6 +16,15 @@ config SND_SOC_ROCKCHIP_I2S
 	  Rockchip I2S device. The device supports upto maximum of
 	  8 channels each for play and record.
 
+config SND_SOC_ROCKCHIP_I2S_TDM
+	tristate "Rockchip I2S/TDM Device Driver"
+	depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP
+	select SND_SOC_GENERIC_DMAENGINE_PCM
+	help
+	  Say Y or M if you want to add support for I2S/TDM driver for
+	  Rockchip I2S/TDM device. The device supports up to maximum of
+	  8 channels each for play and record.
+
 config SND_SOC_ROCKCHIP_PDM
 	tristate "Rockchip PDM Controller Driver"
 	depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP
@@ -34,6 +43,21 @@ config SND_SOC_ROCKCHIP_SPDIF
 	  Say Y or M if you want to add support for SPDIF driver for
 	  Rockchip SPDIF transceiver device.
 
+config SND_SOC_ROCKCHIP_SPDIFRX
+	tristate "Rockchip SPDIFRX Device Driver"
+	depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP
+	select SND_SOC_GENERIC_DMAENGINE_PCM
+	help
+	  Say Y or M if you want to add support for SPDIFRX driver for
+	  Rockchip SPDIF receiver device.
+
+config SND_SOC_ROCKCHIP_VAD
+	tristate "Rockchip Voice Activity Detection Driver"
+	depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP
+	help
+	  Say Y or M if you want to add support for VAD driver for
+	  Rockchip VAD device.
+
 config SND_SOC_ROCKCHIP_MAX98090
 	tristate "ASoC support for Rockchip boards using a MAX98090 codec"
 	depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP
@@ -45,6 +69,13 @@ config SND_SOC_ROCKCHIP_MAX98090
 	  Say Y or M here if you want to add support for SoC audio on Rockchip
 	  boards using the MAX98090 codec and HDMI codec, such as Veyron.
 
+config SND_SOC_ROCKCHIP_MULTICODECS
+	tristate "ASoC support for Rockchip multicodecs"
+	depends on SND_SOC_ROCKCHIP && CLKDEV_LOOKUP
+	help
+	  Say Y or M here if you want to add support for SoC audio on Rockchip
+	  boards using multicodecs, such as RK3308 boards.
+
 config SND_SOC_ROCKCHIP_RT5645
 	tristate "ASoC support for Rockchip boards using a RT5645/RT5650 codec"
 	depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP
@@ -54,6 +85,14 @@ config SND_SOC_ROCKCHIP_RT5645
 	  Say Y or M here if you want to add support for SoC audio on Rockchip
 	  boards using the RT5645/RT5650 codec, such as Veyron.
 
+config SND_SOC_ROCKCHIP_HDMI
+	tristate "ASoC support for Rockchip HDMI audio"
+	depends on SND_SOC_ROCKCHIP && CLKDEV_LOOKUP
+	select SND_SOC_HDMI_CODEC
+	help
+	  Say Y or M here if you want to add support for SoC audio on Rockchip
+	  boards using built-in HDMI or external HDMI.
+
 config SND_SOC_RK3288_HDMI_ANALOG
 	tristate "ASoC support multiple codecs for Rockchip RK3288 boards"
 	depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP
diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile
index 65e814d46..ec17d97d0 100644
--- a/sound/soc/rockchip/Makefile
+++ b/sound/soc/rockchip/Makefile
@@ -1,20 +1,35 @@
 # SPDX-License-Identifier: GPL-2.0
 # ROCKCHIP Platform Support
 snd-soc-rockchip-i2s-objs := rockchip_i2s.o
-snd-soc-rockchip-pcm-objs := rockchip_pcm.o
+snd-soc-rockchip-i2s-tdm-objs := rockchip_i2s_tdm.o
 snd-soc-rockchip-pdm-objs := rockchip_pdm.o
 snd-soc-rockchip-spdif-objs := rockchip_spdif.o
+snd-soc-rockchip-spdifrx-objs := rockchip_spdifrx.o
+snd-soc-rockchip-vad-objs := rockchip_vad.o
+ifdef CONFIG_THUMB2_KERNEL
+snd-soc-rockchip-vad-$(CONFIG_THUMB2_KERNEL) += vad_preprocess_thumb.o
+else
+snd-soc-rockchip-vad-$(CONFIG_ARM64) += vad_preprocess_arm64.o
+snd-soc-rockchip-vad-$(CONFIG_ARM) += vad_preprocess_arm.o
+endif
 
-obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o snd-soc-rockchip-pcm.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S_TDM) += snd-soc-rockchip-i2s-tdm.o
 obj-$(CONFIG_SND_SOC_ROCKCHIP_PDM) += snd-soc-rockchip-pdm.o
 obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIFRX) += snd-soc-rockchip-spdifrx.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_VAD) += snd-soc-rockchip-vad.o
 
+snd-soc-rockchip-hdmi-objs := rockchip_hdmi.o
 snd-soc-rockchip-max98090-objs := rockchip_max98090.o
+snd-soc-rockchip-multicodecs-objs := rockchip_multicodecs.o
 snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o
 snd-soc-rk3288-hdmi-analog-objs := rk3288_hdmi_analog.o
 snd-soc-rk3399-gru-sound-objs := rk3399_gru_sound.o
 
+obj-$(CONFIG_SND_SOC_ROCKCHIP_HDMI) += snd-soc-rockchip-hdmi.o
 obj-$(CONFIG_SND_SOC_ROCKCHIP_MAX98090) += snd-soc-rockchip-max98090.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_MULTICODECS) += snd-soc-rockchip-multicodecs.o
 obj-$(CONFIG_SND_SOC_ROCKCHIP_RT5645) += snd-soc-rockchip-rt5645.o
 obj-$(CONFIG_SND_SOC_RK3288_HDMI_ANALOG) += snd-soc-rk3288-hdmi-analog.o
 obj-$(CONFIG_SND_SOC_RK3399_GRU_SOUND) += snd-soc-rk3399-gru-sound.o
diff --git a/sound/soc/rockchip/rockchip_i2s.h b/sound/soc/rockchip/rockchip_i2s.h
index fcaae24e4..251851bf4 100644
--- a/sound/soc/rockchip/rockchip_i2s.h
+++ b/sound/soc/rockchip/rockchip_i2s.h
@@ -88,15 +88,17 @@
 #define I2S_CKR_MSS_SLAVE	(1 << I2S_CKR_MSS_SHIFT)
 #define I2S_CKR_MSS_MASK	(1 << I2S_CKR_MSS_SHIFT)
 #define I2S_CKR_CKP_SHIFT	26
-#define I2S_CKR_CKP_NEG		(0 << I2S_CKR_CKP_SHIFT)
-#define I2S_CKR_CKP_POS		(1 << I2S_CKR_CKP_SHIFT)
+#define I2S_CKR_CKP_NORMAL	(0 << I2S_CKR_CKP_SHIFT)
+#define I2S_CKR_CKP_INVERTED	(1 << I2S_CKR_CKP_SHIFT)
 #define I2S_CKR_CKP_MASK	(1 << I2S_CKR_CKP_SHIFT)
 #define I2S_CKR_RLP_SHIFT	25
 #define I2S_CKR_RLP_NORMAL	(0 << I2S_CKR_RLP_SHIFT)
-#define I2S_CKR_RLP_OPPSITE	(1 << I2S_CKR_RLP_SHIFT)
+#define I2S_CKR_RLP_INVERTED	(1 << I2S_CKR_RLP_SHIFT)
+#define I2S_CKR_RLP_MASK	(1 << I2S_CKR_RLP_SHIFT)
 #define I2S_CKR_TLP_SHIFT	24
 #define I2S_CKR_TLP_NORMAL	(0 << I2S_CKR_TLP_SHIFT)
-#define I2S_CKR_TLP_OPPSITE	(1 << I2S_CKR_TLP_SHIFT)
+#define I2S_CKR_TLP_INVERTED	(1 << I2S_CKR_TLP_SHIFT)
+#define I2S_CKR_TLP_MASK	(1 << I2S_CKR_TLP_SHIFT)
 #define I2S_CKR_MDIV_SHIFT	16
 #define I2S_CKR_MDIV(x)		((x - 1) << I2S_CKR_MDIV_SHIFT)
 #define I2S_CKR_MDIV_MASK	(0xff << I2S_CKR_MDIV_SHIFT)
diff --git a/sound/soc/rockchip/rockchip_pdm.h b/sound/soc/rockchip/rockchip_pdm.h
index 8e5bbafef..cab977272 100644
--- a/sound/soc/rockchip/rockchip_pdm.h
+++ b/sound/soc/rockchip/rockchip_pdm.h
@@ -41,6 +41,8 @@
 #define PDM_PATH1_EN		BIT(28)
 #define PDM_PATH0_EN		BIT(27)
 #define PDM_HWT_EN		BIT(26)
+#define PDM_SAMPLERATE_MSK	GENMASK(7, 5)
+#define PDM_SAMPLERATE(x)	((x) << 5)
 #define PDM_VDW_MSK		(0x1f << 0)
 #define PDM_VDW(X)		((X - 1) << 0)
 
@@ -51,6 +53,9 @@
 #define PDM_FD_DENOMINATOR_MSK	GENMASK(15, 0)
 
 /* PDM CLK CTRL */
+#define PDM_PATH_SHIFT(x)	(8 + (x) * 2)
+#define PDM_PATH_MASK(x)	(0x3 << PDM_PATH_SHIFT(x))
+#define PDM_PATH(x, v)		((v) << PDM_PATH_SHIFT(x))
 #define PDM_CLK_FD_RATIO_MSK	BIT(6)
 #define PDM_CLK_FD_RATIO_40	(0X0 << 6)
 #define PDM_CLK_FD_RATIO_35	BIT(6)
@@ -66,6 +71,7 @@
 #define PDM_CLK_1280FS		(0x2 << 0)
 #define PDM_CLK_2560FS		(0x3 << 0)
 #define PDM_CLK_5120FS		(0x4 << 0)
+#define PDM_CIC_RATIO_MSK	(0x3 << 0)
 
 /* PDM HPF CTRL */
 #define PDM_HPF_LE		BIT(3)
